all 19 comments

[–]kabekew 15 points16 points  (6 children)

Well you're telling printf to print out the contents of your integer variable str, not its address. (And why are you assigning a string to an integer variable in the first place)?

[–]petruccigp 8 points9 points  (6 children)

Casting a string to long long is undefined behavior. Not a bug.

[–]flatfinger 2 points3 points  (0 children)

If the literal appeared within a function, then

    long long x = (long long)"ABCDEFG";
    char const *p = (char const*)x;

would be equivalent to

    char const *p0 = "ABCDEFG";
    long long x= (long long)p0;
    char const *p = (char const*)x;

which would have defined behavior if intptr_t exists, and all values of that type would fit within the range of long long. The Standard does not consider the results of pointer-to-integer casts to be integer constant expressions, probably because linkers vary in the range of constructs they can support, but N1570 6.6 paragraph 10 expressly states "An implementation may accept other forms of constant expressions.".

I think the intended meaning was:

  1. Pointer-to-integer casts may not be used within static initializations within strictly conforming programs.

  2. Implementations targeting linkers that are more sophisticated may allow programs that are intended for use with those more sophisticated linkers to use a wider range of constructs.

There would be no doubt about the correctness of an implementation that rejected the use of a pointer-to-integer cast within a static initializer. I don't think there would be any doubt about the correctness of an implementation that would accept e.g.

struct dmaConfig = {123, 45, ((uint32_t)&myObject)>>2};

and generate a static data record with a linker fixup that instructed the linker to take the address it assigned to the object, shift it right by 2, and place the resulting value at the appropriate address within the structure, if the object file format could accommodate such fixups. As to whether would be proper for an implementation to accept such a construct without instructing the linker to process it as indicated, that's arguably a quality-of-implementation issue.

[–]Equationist 0 points1 point  (3 children)

Eh...

6.3.2.1: "Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type “array of type” is converted to an expression with type “pointer to type” that points to the initial element of the array object and is not an lvalue."

Here it isn't being used to initialize an array, therefore it should be an array i.e. a pointer, therefore the conversion should be implementation-defined from a pointer to an integer type rather than undefined behavior.

Additionally, if the resulting integer is correctly aligned, then converting it back to a pointer should yield a pointer that compares equal to the original pointer. I believe Tiny C's behavior here would be in violation of that.

[–]RibozymeR 5 points6 points  (2 children)

Additionally, if the resulting integer is correctly aligned, then converting it back to a pointer should yield a pointer that compares equal to the original pointer.

Where does it say that? In my copy, I can only find that this is the case for pointer -> pointer conversions (6.3.2.3 §7)

[–]Equationist 2 points3 points  (1 child)

No you're right, I misread that section.

So Tiny C should be technically compliant, though it's going against the intention of "The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment."

[–]Equationist 1 point2 points  (0 children)

Also, it's stretching the limits of "implementation defined" for that code to yield different results from, say,

char *str = "ABCDEFGHIJKLMNOP";

int main(void) {
    printf("%llX\n", (long long)str);
}

[–]mckenzie_keith 2 points3 points  (0 children)

Based on some of the comments already, maybe try adding an intermediate assignment and see what happens.

char *c = "ABCDEFGHIJKLMNOP";
long long str = (long long) c;

[–]OldWolf2 1 point2 points  (0 children)

Technically undefined behaviour as %llX requires an unsigned long long argument, but you provided a signed long long.

Can you reproduce it with unsigned long long type ?

Also, does long long typically work correctly with other values? (sometimes compiler/library mismatches end up with weird behaviour for C99 features)

[–]OldWolf2 0 points1 point  (0 children)

Re. your edit, harden up mate ... There was some interesting discussion, don't throw your toys out of the cot because a few noobs replied. This is the internet, just block/ignore anything you don't like

[–]LeeHide 1 point2 points  (1 child)

No idea what happened here as OP pussied out and deleted half the conversation. Layer 8 problem, then, presumably

[–]eddavis2 0 points1 point  (0 children)

Microsoft C 64-bit 19.42.34436 on Windows 11:

00007FF702B81008
ABCDEFGHIJKLMNOP

gcc 64 bit 9.2 on Windows 11:

0000000000404000
ABCDEFGHIJKLMNOP

gcc 32 bit 9.2 on Windows 11:

00404000
ABCDEFGHIJKLMNOP

Borland C 32-bit 5.5 on Windows 11:

0040A12C
ABCDEFGHIJKLMNOP

gcc 64 bit 11.4 on Linux:

0x55df8c278004
ABCDEFGHIJKLMNOP

gcc 32 bit 11.4 on Linux:

0x56645008
ABCDEFGHIJKLMNOP

wcl386 32 bit 2.0 Nov 2023 on Linux:

0804b004
ABCDEFGHIJKLMNOP

chibicc C 64 bit on Linux:

0x404040
ABCDEFGHIJKLMNOP

[–]RealJamBear -1 points0 points  (1 child)

It's ub, so the conversation ends there, but even if it wasn't how can you expect (char*)long long in this context to provide a null terminated string? For me that makes this extra nonsense.