Mrmj
Mrmj

Reputation: 125

How to read 4 bytes of data from a given char pointer in C


Scenario is that, i wanna read 4 bytes of data from a given pointer which is of type char.
Eg: Consider the following -

int a=0;
char* c; // This will have some address 

What i wanna do is read 4 bytes starting from c (i.e. the address) and assign them in variable a which is an integer.

My Solution:

a = *(int*)c;  // Assembly is LDR    r1, [r6,#0x00]

My Problem:
Above solution works well on some architectures but fails on some. To be specific, in my case, it fails on Arm CortexM0.

If any one has any portable, highly efficient(with minimum assembly) replacement of my solution please share, it would be a great help to me and I thank you for that in advance ;)

Please ask if more info needed.

Upvotes: 4

Views: 12648

Answers (4)

Lundin
Lundin

Reputation: 213852

There are at many different problems here.

  • Alignment. The char pointer must point at an aligned address if you wish to read an integer at that address.
  • Signedness of char. It is implementation-defined whether char is treated as signed or unsigned. It is therefore a bad type to use for any form of bit/byte manipulation. Instead, use uint8_t.
  • Pointer aliasing. Casting a raw address pointed at by a char* to an int* is undefined behavior as it violates the so-called strict aliasing rule. This could cause your code to get incorrectly optimized by the compiler (particularly gcc). The other way around, from int* to char* would have been fine though.

Endianess is not an issue if the stored integer is already in the same endianess format as that of the current system. If not, you'd have to convert it, but that's quite unrelated to the question here...

Example of a portable, safe solution:

#include <stdint.h>
#include <assert.h>
#include <string.h>

#include <stdio.h>
#include <inttypes.h>


int main (void) {

  int x = 123;
  uint8_t* c = (uint8_t*)&x; // point to something that is an int
  assert((uintptr_t)c % _Alignof(uint32_t) == 0); // ensure no misalignment

  uint32_t i;
  memcpy(&i, c, sizeof(i)); // safely copy data without violating strict aliasing

  printf("%"PRIu32, i); // print 123

  return 0;
}

Upvotes: 4

Some programmer dude
Some programmer dude

Reputation: 409176

The problem could be because of alignment. Some CPU architectures can't read or write non-byte values on unaligned addresses.

The solution is to make unaligned byte-access instead, which can easily be done with memcpy:

memcpy(&a, c, sizeof a);

Upvotes: 7

Jabberwocky
Jabberwocky

Reputation: 50775

If endianness is an issue for you:

Instead of:

a = *(int*)c;  // Assembly is LDR    r1, [r6,#0x00]

you need this:

On big endian systems:

a = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];

On little endian systems:

a = c[3] << 24 | c[2] << 16 | c[1] << 8 | c[0];

// probably faster (only on little endian systems) :
memcpy(&a, c, sizeof a);

Upvotes: 1

LPs
LPs

Reputation: 16223

Depending on the endianness

#include <stdio.h>

int main(void)
{
    unsigned char bytes[] = { 0xAA, 0x55, 0xAA, 0x55 };
    unsigned int a=0;
    unsigned char* c = bytes;

    a += (*c++ & 0xFFFFFFFFu) << 0;
    a += (*c++ & 0xFFFFFFFFu) << 8;
    a += (*c++ & 0xFFFFFFFFu) << 16;
    a += (*c & 0xFFFFFFFFu) << 24;

    printf("HEX: %X\n", a);

    a = 0;
    c = bytes;

    a |= (*c++ & 0xFFFFFFFFu) << 24;
    a |= (*c++ & 0xFFFFFFFFu) << 16;
    a |= (*c++ & 0xFFFFFFFFu) << 8;
    a |= (*c & 0xFFFFFFFFu) << 0;

    printf("HEX: %X\n", a);
}

Upvotes: 0

Related Questions