0

Look at the following C code:

#include <stdio.h>

int main(int argc, char* argv[])
{
    char a = 0, b = 0;
    int* p = (int*)&b;
    *p = 258;
    //printf("%d %d\n%p %p\n", a, b, &a, &b);
    printf("%p %p\n%d %d\n", &a, &b, a, b);
    //printf("%p %p\n", &a, &b);
    //printf("hd\n");
    //printf("%p %p\n", &a, &b);
}

The above output:

1 2

0061FF1B 0061FF1A

#include <stdio.h>

int main(int argc, char* argv[])
{
    char a = 0, b = 0;
    int* p = (int*)&b;
    *p = 258;
    printf("%d %d\n", a, b);
    //printf("%p %p\n", &a, &b);
}

The above output:

0 2

Two pieces of code that comment one line of output will result in a different output on the first line. Why?

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • 6
    Your programs have undefined behavior so they could do anything – Ted Lyngmo Mar 30 '22 at 11:49
  • C and C++ are two *very* different languages. Please don't add irrelevant (or sometimes even completely wrong) tags to your questions. – Some programmer dude Mar 30 '22 at 11:51
  • 2
    The claimed output from the first program doesn't even match the code. Please copy-paste (as text) the *actual* output. And please take some time to refresh [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Mar 30 '22 at 11:53
  • UB͏͏͏͏͏͏͏͏͏͏͏͏͏ – Bathsheba Mar 30 '22 at 11:53
  • In more detail (although my previous comment is really all you need to know), if you want to analyse undefined behaviour then you need to look at the generated assembly for both cases. It will be clear then. – Bathsheba Mar 30 '22 at 11:54
  • 1
    @Someprogrammerdude: Please take note that you wrote “The claimed output from the first program doesn't even match the code” in spite of the fact that the behavior is not defined by the C standard, so any output matches the code in the sense of the C standard. This contrasts with what you wrote in, for example, [2018](https://stackoverflow.com/questions/53340290/why-am-i-printing-the-path/53340434#53340434). It appears you do, in fact, have expectations of how code with “undefined behavior” behaves. – Eric Postpischil Mar 30 '22 at 12:00
  • @Someprogrammerdude Sorry, I'll be more careful next time – Wang Apples Mar 30 '22 at 12:23
  • Thank you very much for the discussion, and I think I know why – Wang Apples Mar 30 '22 at 12:25
  • Undefined behavior is always fun. On my machine, the program crashes. – Eljay Mar 30 '22 at 14:32

2 Answers2

2

EDIT: Your code is an example of an Undefined Behaviour, but I will try to explain in detail why my computer outputs the same as your second output.

The following code in my computer gives your second output, which is what makes the most sense.

#include <stdio.h>

int main(int argc, char* argv[])
{
    char a = 0, b = 0;
    int* p = (int*)&b;
    *p = 258;
    //printf("%d %d\n%p %p\n", a, b, &a, &b);
    printf("%p %p\n%d %d\n", &a, &b, a, b);
    //printf("%p %p\n", &a, &b);
    //printf("hd\n");
    //printf("%p %p\n", &a, &b);
}

Outputs:

&a=0x7ffe2c4f7bde &b=0x7ffe2c4f7bdf                                                                                                                                                                                                                                                    
a=0 b=2

Assuming big endianness, bytes are read from left to right in ascending value order. If you declared first a and then b, then the memory of a is located at the right of b (depending on the compiler it might be reversed, but I will try to explain my computers behaviour), diagramatically:

 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       b       |        a      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

if you cast the pointer to b as an int, then the compiler will think you have 4 bytes instead of one at the left of a, and will write to it. 258 in binary is 0b100000010, so only the last 8 bits of this fall into the char range that b reads, which would be 0000 0010, which is 2. This is why a stays zero. A cool way to test this is declaring another char c after b, and checking how after executing the same program, c is 1 because it is located at the inmediate left of b and *p = 258 would have written the first bit of c to 1. Here's what I mean:

#include <stdio.h>

int main(int argc, char* argv[])
{
    char a = 0, b = 0, c = 0;
    int* p = (int*)&b;
    *p = 258;
    printf("&a=%p &b=%p\n &c=%p a=%d b=%d c=%d\n", &a, &b, &c, a, b, c);
}

Outputs:

&a=0x7ffc6ce4943d &b=0x7ffc6ce4943e
&c=0x7ffc6ce4943f a=0 b=2 c=1
carce-bo
  • 480
  • 2
  • 10
  • 1
    "If you declared first a and then b, then the memory of a is located at the right of b" -> Maybe. C does not specify that. – chux - Reinstate Monica Mar 30 '22 at 12:25
  • @chux-ReinstateMonica You are right. It depends on the compiler. But afaik stack memory allocation works either from left to right or right to left. I edited the answer just in case. – carce-bo Mar 30 '22 at 12:36
  • @carce-bo There are more than just the "left to right or right to left" to consider: padding, layout per type, optimizations, and the a _stack_ itself is not required. Best to not assume an implementation's layout of the objects. – chux - Reinstate Monica Mar 30 '22 at 12:40
  • This is a good attempt, but answering questions about undefined behaviour is far from trivial and perhaps are even to be avoided. Among the things you're missing, you're forgetting about the possibility of 1's complement `char`, as well as the possibility that `sizeof(char) == sizeof(int)`. – Bathsheba Mar 30 '22 at 12:44
  • @chux-ReinstateMonica Oh, ok. Thank you I will do some research on those topics see what I can learn. Should I delete the answer as it could be misleading ? – carce-bo Mar 30 '22 at 12:48
  • 1
    @carce-bo I'd leave it up as, setting aside the UB pedantry, it is an extremely (and probably correct) well-written exposition on what is happening. It might be an idea to put some sort of disclaimer up saying a more formal version of "yes I know it's UB and yes the compiler might eat my cat, my hard disk might spontaneously combust, it might output the software specification of Facebook, yadadadada, but on balance this is probably the explanation on a typical desktop platform at the time of writing". I also upvoted it despite my first comment. – Bathsheba Mar 30 '22 at 12:51
  • 1
    @Bathsheba "my [hard disk](https://en.wikipedia.org/wiki/Hard_disk_drive)" --> sounds quaint these days. ;-) – chux - Reinstate Monica Mar 30 '22 at 12:55
  • Disclaimer added. Thanks for your comments ! – carce-bo Mar 30 '22 at 12:56
1

The problem is that you're typecasting a char* to an int* and then dereferencing p which leads to undefined behavior.

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior. The program may just crash.

So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.

So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.


1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

Jason
  • 36,170
  • 5
  • 26
  • 60