Thread: Weird endianness related bug

  1. #1
    Registered User
    Join Date
    Jan 2020
    Posts
    7

    Question Weird endianness related bug

    So, apparently when casting an array of bytes, C automatically reverses the endianness:

    Code:
    #include <stdio.h>
    #include <stdint.h>
    
    int main(){
        uint8_t a[4] = {0x52,0x4a,0x75,0xd8};
        uint32_t x = *(uint32_t*)a;
        uint32_t y = 0x524a75d8;
    
        printf("x=%x, y=%x, a=%02x%02x%02x%02x\n",x,y,a[0],a[1],a[2],a[3]);
    }
    It outputs x=d8754a52, y=524a75d8, a=524a75d8, which means the bytes (but not the bits!) of x are reversed with respect to y.

    1. Why does this happen automatically?
    2. How to make it not happen?
    3. Why is endiannes split into bits and bytes, and the order is big bit, small byte?

    For those wondering, I am using GCC 11.1.0 without any extra options.
    Last edited by midyro edellve; 06-23-2021 at 11:11 AM.

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    Nothing is happening.
    Your machine (our common Intel/AMD machines) are little endian.
    So the value 0xd8754a52 is stored in memory with the "little end" (least significant byte) "first" (lowest) in memory, i.e., 0x52, 0x4a, 0x75, 0xd8.

    Look at y's bytes:
    Code:
    #include <stdio.h>
    #include <stdint.h>
     
    int main(){
        uint32_t y = 0x524a75d8;
        uint8_t *p = (uint8_t*)&y;
        printf("%02x %02x %02x %02x\n", p[0], p[1], p[2], p[3]);  // prints d8754a52
        return 0;
    }
    I don't understand what you mean about "endianness split into bits and bytes...".
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    Incidentally, you can detect endianness very easily at runtime.

    Code:
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    bool little_endian(void) {
      static bool state = false;
      static bool known = false;
      if (!known) {
        unsigned long sample = 0xff;
        uint8_t* bytes = (uint8_t*)&sample;
        if (bytes[0] == 0xff)
          state = true;
        known = true;
      }
      return state;
    }
    
    #define big_endian() !little_endian()
    
    void reverse_bytes(void* data, size_t size) {
      uint8_t* left = (uint8_t*)data;
      uint8_t* right = left + size;
      while (left < --right) {
        uint8_t copy = *left;
        *left++ = *right;
        *right = copy;
      }
    }
    
    void to_little_endian(void* data, size_t size) {
      if (!little_endian())
        reverse_bytes(data, size);
    }
    
    void to_big_endian(void* data, size_t size) {
      if (!big_endian())
        reverse_bytes(data, size);
    }
    
    #include <stdio.h>
    
    int main(void) {
      uint8_t a1[4] = {0x52, 0x4a, 0x75, 0xd8};
      uint32_t x1 = *(uint32_t*)a1;
      uint32_t y1 = 0x524a75d8;
    
      printf("CPU is %s-endian\n", big_endian() ? "big" : "little");
      printf("x=%x, y=%x, a=%02x%02x%02x%02x\n", x1, y1, a1[0], a1[1], a1[2],
             a1[3]);
    
      puts("Switching to big-endian format");
      to_big_endian(a1, 4);
      printf("x=%x, y=%x, a=%02x%02x%02x%02x\n", x1, y1, a1[0], a1[1], a1[2],
             a1[3]);
    
      uint8_t a2[4] = {0x52, 0x4a, 0x75, 0xd8};
      uint32_t x2 = *(uint32_t*)a2;
      uint32_t y2 = 0x524a75d8;
    
      puts("Switching to little-endian format");
      to_little_endian(a2, 4);
      printf("x=%x, y=%x, a=%02x%02x%02x%02x\n", x2, y2, a2[0], a2[1], a2[2],
             a2[3]);
    }
    Output on my machine:

    CPU is little-endian
    x=d8754a52, y=524a75d8, a=524a75d8
    Switching to big-endian format
    x=d8754a52, y=524a75d8, a=d8754a52
    Switching to little-endian format
    x=d8754a52, y=524a75d8, a=524a75d8
    Last edited by Sir Galahad; 06-23-2021 at 04:31 PM. Reason: fix logic error

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Big and little are not your only choices - Question 20.9
    Nor is it necessarily something cast in stone - Endianness - Wikipedia

    The only guaranteed way to do this, without assuming anything about the current machine, is
    Code:
    int main(){
        uint8_t a[4] = {0x52,0x4a,0x75,0xd8};
        uint32_t x = (uint32_t)a[0] << 24 |
                     (uint32_t)a[1] << 16 |
                     (uint32_t)a[2] <<  8 |
                     (uint32_t)a[3];
        uint32_t y = 0x524a75d8;
     
        printf("x=%x, y=%x, a=%02x%02x%02x%02x\n",x,y,a[0],a[1],a[2],a[3]);
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Endianness and bit shifting
    By synthetix in forum C Programming
    Replies: 27
    Last Post: 06-07-2009, 01:06 AM
  2. Replies: 5
    Last Post: 04-15-2008, 10:24 AM
  3. Endianness - Is this correct?
    By Tommo in forum Tech Board
    Replies: 2
    Last Post: 01-06-2008, 12:56 PM
  4. endianness
    By DavidP in forum Tech Board
    Replies: 4
    Last Post: 06-13-2004, 04:44 PM
  5. Weird fatal error, header file related
    By alkis_y3k in forum C++ Programming
    Replies: 2
    Last Post: 12-26-2002, 09:54 AM

Tags for this Thread