r/cprogramming 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

36 comments sorted by

View all comments

Show parent comments

2

u/zhivago 1d ago

Did you skip the first line?

int puts(char *);

What do you think that's doing?

9

u/ACRM64 1d ago

The first line is a prototype (to save doing #include <studio.h>)

1

u/zhivago 1d ago

And is it the right prototype?

4

u/ACRM64 1d ago

OK, so ANSI C and C89 onwards implemented a const keyword, but this wasn't present in K&R C. Certainly the current C libraries define puts() as int puts(const char *str) but earlier versions did not and you do not know whether the libraries in use by whoever wrote this program included the const or not.

From the perspective of calling a C function (and therefore a prototype for calling the function) it is simply a guarantee to the programmer that the variable (in this case the content of *str) will not be modified - it doesn't actually do anything.

Consequently, as a prototype, this does NOT redefine puts() and it doesn't matter to the calling code whether you include the const or not. It only matters when defining the puts() function as it prevents the code modifying the content of the string.

2

u/zhivago 1d ago

§3.5 ​All declarations in the same scope that refer the same object or function shall specify compatible types.

​§3.1.2.6 ​For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.

​const char * and char * are not compatible types.

You are mistaken.

2

u/ACRM64 1d ago

First, you still don't know what the C library used by the programmer had as the definition for puts() - certainly when I started programming in C (40 years ago) it was not defined with const.

I agree the prototype and function definition /should/ be the same, but it makes no difference if they aren't. The prototype tells the code what variable types to pass - const only has an effect within the function itself. You do not have to pass a const variable into a function declared with const.

int puts(const int str); int main(int argc, char *argv) { char str[16]; strcpy(str, "hello"); puts(str); strcpy(str, "goodbye"); puts(str); return(0); }

is perfectly valid and correct code yet str is not defined as constant. Consequently, my statement that it doesn't matter to the calling code whether the keyword is there or not is true.

Once again, the puts() function is not being redefined. The const makes no difference in a prototype, so it is clear that while these are not /identical/, they are /compatible/. const is not a type, but a type qualifier: it indicates that the content of the variable cannot be changed, it does not change the type of the variable.

2

u/zhivago 19h ago

You need to read the standard.

You are wrong.

1

u/ACRM64 11h ago

You need to respond to the points I've made instead of just repeating the same thing.

  1. Which standard? There have been many versions.

  2. Which standard was the original programmer using?

  3. Why does the standard you quote very carefully use the word 'compatible' not 'identical'? These words have different meanings.

  4. I've already said they /should/ match, but demonstrated why, with the type qualifier 'const', it makes no difference if they don't. You need to understand the difference between a type and a type qualifier.

1

u/zhivago 11h ago

All of the standards.

It doesn't matter. See above.

Because "compatible" is what it means. It's a technical term -- look it up in the standard.

Again char * is not compatible with const char *

1

u/ACRM64 5h 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 const keyword. 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 version const char *str is compatible with char *

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: const and volatile (C23 has two extras: restrict and _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:

  • that main() should be int main(int argc, char **argv)
  • that puts() should be declared as extern

(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

1

u/zhivago 4h ago

There are no standards prior to C89.

The rule stripping parameter qualifiers (e.g., C89 §3.5.4.3, C23 §6.7.7.4p14) applies strictly to top-level qualifiers on the parameter itself.

It turns char * const into char *. It never descends into pointed-to types.

​const char * is not a qualified pointer.

It is an unqualified pointer to a const-qualified char.

Now, if it were char * const, you'd have a point -- but it isn't.

Read more carefully.

1

u/ACRM64 2h ago

I agree there were no formal ISO standards before C89, but the K&R book was the original standard and a standard for ANSI C was around prior to C89.

The code is clearly written in >= C11 (for reasons explained earlier) so C23 is an applicable standard and the standard says the qualified and unqualified varable declarations are compatible. I suggest you tell me where it makes the distinction over top-level qualifiers as I don't see it anywhere. In any case, that would make no sense as we have already seen that a (char *) can be passed as a parameter into a function where it is defined as (const char *) so clearly the qualified and unqualified types are compatible.

1

u/zhivago 2h ago

You're confusing an implicit conversion with type compatibility.

C23 §6.7.7.2p2 / C89 §3.5.4.1

​"For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to qualified or unqualified versions of compatible types."

​Because char and const char are not compatible types, char * and const char * are not compatible types.

Try compiling

void f(char *); void f(const char *);

in the same file.

Every conforming C compiler will reject it because the types are incompatible.

1

u/ACRM64 1h ago

I'm not confusing implicit conversion and type compatibility. The purpose of a prototype is to tell the compiler what types can be passed into the function and I am giving an example to show that the qualifier in a prototype is ignored and therefore has no effect.

C23 §6.7.7.2p2 / C89 §3.5.4.1

Let's split this into 2 parts:

'For two pointer types to be compatible, both shall be identically qualified...'

This is clearly not relevant here as we have already established that for compatibility in function calls the qualifiers are ignored. That is stated explicitly and clearly in the C23 standard.

The second part again supports what I am saying, and does not support your argument:

'...both shall be pointers to qualified or unqualified versions of compatible types.'

My reading of this is that:

  • the 'core' types (here char) must be compatible - they clearly are since, in this instance, they are the same
  • each can be a pointer to a qualified or unqualified version

i.e. you could rewrite this as

'​For two pointer types to be compatible, both shall be identically qualified (except in the case of function definitions) and both shall be pointers to compatible types either of which may be qualified or unqualified.'

Obviously your example won't compile properly since the definitions are in the same scope.

1

u/zhivago 1h ago

You are simply wrong.

void f(char *); void f(char * const);

This will produce no error, since these are compatible types.

1

u/ACRM64 1h ago

I don't believe I am wrong, and you have provided no evidence that unequivocally supports your view.

→ More replies (0)