-1
uint8_t bytes[32];
uint32_t *i = (uint32_t *)&bytes[12];
*i = 0x01020304;
printf("%d\n", bytes[12]);
printf("%d\n", bytes[16]);
printf("%d\n", bytes[20]);
printf("%d\n", bytes[24]);

return 0;

I have this code that prints:

4
208
23
80

Can someone explain to me why it prints these values? I can understand why 4 but not the others.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Jess
  • 11

1 Answers1

2

Can someone explain to me why it prints these values? I can understand why 4 but not the others.

The array uint8_t bytes[32]; is uninitialized. The write *i = 0x01020304; only updates items 12, 13, 14, 15. Assuming that this is a local scope array, then the rest of the data contains indeterminate values ("garbage"). Reading a variable with indeterminate value gives unspecified results on most systems - you can get any value and the program need not be consistent about it. The most common is that you get some random crap value that happened to sit in that memory cell since earlier execution.

(On some exotic systems, reading an indeterminate value could result in something called trap representation, meaning possible program crash & undefined behavior.)

The 4 you see comes from your system using a little endian CPU, where the number 0x01020304 is stored like [12]=04, [13]=03 and so on, least significant byte on the lowest address. Other CPUs with big endian store numbers in the opposite order and then you'd get index [12]=01 instead.

However, the conversion from uint8_t to uint32_t is fishy in itself:

(uint32_t *)&bytes[12];

There are two big undefined behavior problems with this line:

  • The data could be misaligned (it wasn't in this specific case) or otherwise not presentatble as the target type (unlikely in case of signed integers, very likely in pretty much every other case).

  • This is also a violation of the C type system ("strict aliasing violation", an advanced topic What is the strict aliasing rule?) which means that the compiler might end up drawing wrong conclusions about what's stored at a certain memory location upon generating the machine code.

Generally speaking, we cannot meaninfully reason about the results of a program containing undefined behavior, nor about one containing reliance on unspecified behavior. See What is undefined behavior and how does it work?.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    `uint8_t` can't have trap representations. “Not presentable as the target type” is unlikely in the case of unsigned integers as well. – Gilles 'SO- stop being evil' Oct 27 '20 at 11:36
  • The statement “we cannot meaninfully reason about the results of a program containing undefined behavior” is false. People reason about the results all the time: When programs misbehave, people use the observed behavior as clues to the causes. This often involves behavior that is not defined by the C standard, yet it is generally a useful, and therefore meaningful, way to proceed. Saying we cannot reason about so-called “undefined behavior” is like saying doctors cannot diagnose disease because there is no authoritative specification of biology. Further, behavior not defined by the C standard… – Eric Postpischil Oct 27 '20 at 11:58
  • … is in many ways governed by other things, including logical necessities, common practices, hardware behaviors, designs being guided by desire for efficiency or other features, and more. Furthermore, some behavior not defined by the C standard is defined by other things, such as compiler documentation. So the statement ““we cannot meaninfully reason about the results of a program containing undefined behavior” is false in fact, for both theoretical and practical purposes. – Eric Postpischil Oct 27 '20 at 11:59
  • @EricPostpischil The doctor/vet will however need to know the species of the creature in order to diagnose it. If the case is "my generic mammal exhibits a strange behavior", then you cannot reason about it. Is it a human? A chinchilla? A whale? We'd need to know the exact system, compiler, optimizer settings and so on. And even then it is highly uninteresting to reason about why the OP's unspecified behavior gave the value 208 and not 42. – Lundin Oct 27 '20 at 12:10
  • One can in fact reason about mammals generally. But reasoning about individual programs or particular compiler implementations or programs running on various operating systems is still reasoning. If you wanted to make an argument that the amount of reasoning one could do about all C programs in all possible circumstances in the presence of undefined behavior is limited, you could form a statement that were true, but of limited use. But the statement you made, that we **cannot** meaningfully reason about the results of a program containing undefined behavior is false. – Eric Postpischil Oct 27 '20 at 14:35
  • As a mammal example, showing how we can reason about all C programs in any implementation, the C standard does not define the performance of `qsort`. But we can reason that by logical necessity its worst case complexity is at least O(n log n), with any compiler using any library on any operating system. – Eric Postpischil Oct 27 '20 at 14:38