Pregunta

I need to find the most standards-compliant way to obtain the address of a pointer and store its bytes separately (for instance, to transmit them serially).

I have two versions below, the first one which contains, I believe, undefined behavior, and the second one, which should contain only defined behavior according to C99. But my tool tells me I have undefined behavior on the second one as well. Could someone please confirm it, and indicate a solution with neither undefined behavior, nor implementation-defined behavior, if possible?

Edit: I changed the type from int to unsigned long to aid in finding a non-implementation-dependent solution. I also removed the "16-bit wide pointer".

unsigned long a[2];
unsigned char b0, b1, b2, b3;

int main1() {
  unsigned long l = (unsigned long) &(a[0]);
  b0 = (l >> 24) & 0xFF;
  b1 = (l >> 16) & 0xFF;
  b2 = (l >> 8) & 0xFF;
  b3 = l & 0xFF;
  return 0;
}


typedef union { unsigned long* p; char c[sizeof(unsigned long *)]; } u;

int main2() {
  u x;
  x.p = a;
  b0 = x.c[3];
  b1 = x.c[2];
  b2 = x.c[1];
  b3 = x.c[0];
  return 0;
}

Edit 2: added reference to a part of the C99 standard concerning these programs:

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

Does it mean it is not possible to read the address of array a without relying on some implementation-defined behavior? Or is there a way to circumvent it?

¿Fue útil?

Solución

For pointers, it is better to use type unsigned long (or unsigned long long). Unless there is uintptr_t data type. Why unsigned? Because shift operations are common only for unsigned integers. For signed ones, it is platform-dependent.

So it you want to transfer the address (for whatever reason, as address is usually process-local), you can do like the following:

/**
 * @param ptr Pointer to serialize
 * @param buf Destination buffer
 * @param be  If 0 - little endian, 1 - big endian encoding
 */
void ptr2buf(const void *ptr, void *buf, int be)
{
    uintptr_t u = (uintptr_t)ptr;
    unsigned char *d = buf;

    if (be)
    {
        /* big endian */
        d += sizeof(t) - 1;

        for (i = 0; i < sizeof(t); ++i)
        {
            *d-- = u & 0xFF;
            u >>= 8;
        }
    }
    else
    {
        /* little endian */

        for (i = 0; i < sizeof(t); ++i)
        {
            *d++ = u & 0xFF;
            u >>= 8;
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top