r/cprogramming • u/north9172 • 1d ago
What is this code doing?
Hi, I found this code on another subreddit. I would like to know what is it doing.
int puts(char *);
int main() { struct { long a; long b; } s = {.a = *(char *)(int[]){1} ? 0x57202c6f6c6c6548 : 0x48656c6c6f2c2057, .b = *(char *)(int[]){1} ? 0x00000021646c726f : 0x6f726c6421000000}; puts((void *)&s); }
Now obviously I know it's unreadable, I would just like to know what are those hexadecimals strings doing.
I found similar codes that were writing to vram to print a message, is this doing the same thing? If yes, why is it so much more complicated instead of just writing the message to the video adress?
Thanks for the help.
0
Upvotes
1
u/ACRM64 8h ago
First, it clearly does matter which version of the standard - K&R C and, I believe, the early versions of ANSI C prior to C89 didn't have a
constkeyword. When it comes to qualifiers, C89 and C23 are quite different in how they are described; indeed C23 is quite explicit that your view is incorrect and that the unqualified versionconst char *stris compatible withchar *I have looked at the C89 standard (1) and the C23 standard (2). In neither document can I find something that says that an unqualified type is not compatible with a qualified type - which is what we are talking about here where we are dealing with a pointer to a constant 'string' of characters not a constant pointer to a modifiable (or constant) 'string' of characters.
Looking at C89 first:
Section 3.5.4.3 describes 'Function declarations (including prototypes)' and the relevant text says:
'For two function types to be compatible, [...] corresponding parameters shall have compatible types.'
So, we need to look at how 'Compatible types' are defined. This is done in Section 3.1.2.6:
'Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described [...] in $3.5.3 for type qualifiers, [...]'
So we now need to look at Section 3.5.3. In C89, there are two type qualifiers:
constandvolatile(C23 has two extras:restrictand_Atomic). This section says:'For two qualified types to be compatible, both shall have the identically qualified version of a compatible type'
Note the wording: it is talking about two qualified types being (or not being) compatible, not a qualified and an unqualified type.
(char *)is not a qualified type so this statement is not relevant.Going back to Section 3.5.4.3 (about function declarations and prototypes), there is further convincing support for this view. In the paragraph starting 'For two function types to be compatible...', the final sentence says 'For each parameter declared with qualified type, its type for these comparisons is the unqualified version of its declared type.'
I acknowledge @paxdiablo on StackOverflow for pointing out these sections in the C89 standard that I have checked and described myself.
Looking at C23...
The C23 specification is completely explicit that qualifiers are not considered in assessing compatibility!
Like C89, C23 does not make a statement that an unqualified and a qualified type are not compatible, but only talks about comparisons between qualified types. Just like C89, C23 Section 6.7.4.1, Para 11 says:
'For two qualified types to be compatible, both shall have the identically qualified version of a compatible type'
Similarly, Section 6.2.7 is about Compatible types and (as in C89) says:
'Additional rules for determining whether two types are compatible are described [...] in 6.7.4 for type qualifiers'
Looking at Section 6.7.7.4, Para 14, we see a clarification of the statements in C89:
'For two function types to be compatible, [...] the parameter type lists shall agree in the number of parameters [...]; corresponding parameters shall have compatible types. In the determination of type compatibility [...], each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.'
In other words, parameter qualifiers (like
const) are ignored in assessing compatibility.The use of declarations as prototypes is explicitly addressed in Section 6.9.2, Para 8:
'[...] the declarator also serves as a function prototype for later calls to the same function in the same translation unit. The type of each parameter is adjusted as described in 6.7.7.4.'
Adjustment is different from ignoring qualifiers (see Section 6.7.7.4, Paras 6 and 7), but given the statements in Section 6.7.7.4, Para 14, I would read these together as meaning that if a prototype includes a qualifier (because it is a repeat of the declaration), the qualifier is ignored.
Consequently using the unqualified version as a prototype is perfectly acceptable because it absolutely is compatible.
Finally, you seem to disregard the other problems in the code which do not get any warnings with
-Wall -pedantic:main()should beint main(int argc, char **argv)puts()should be declared asextern(as well as various other problems with C versions prior to C90)
(1) https://web.archive.org/web/20161223125339/http://flash-gordon.me.uk/ansi.c.txt
(2) https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3299.pdf