-->

Byte precision pointer arithmetic in C when sizeof

2019-01-20 05:31发布

问题:

How can one portably perform pointer arithmetic with single byte precision?

Keep in mind that:

  • char is not 1 byte on all platforms
  • sizeof(void) == 1 is only available as an extension in GCC
  • While some platforms may have pointer deref pointer alignment restrictions, arithmetic may still require a finer granularity than the size of the smallest fundamental POD type

回答1:

Your assumption is flawed - sizeof(char) is defined to be 1 everywhere.

From the C99 standard (TC3), in section 6.5.3.4 ("The sizeof operator"):

(paragraph 2)

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type.

(paragraph 3)

When applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1.

When these are taken together, it becomes clear that in C, whatever size a char is, that size is a "byte" (even if that's more than 8 bits, on some given platform).

A char is therefore the smallest addressable type. If you need to address in units smaller than a char, your only choice is to read a char at a time and use bitwise operators to mask out the parts of the char that you want.



回答2:

sizeof(char) always returns 1, in both C and C++. A char is always one byte long.



回答3:

According to the standard char is the smallest addressable chunk of data. You just can't address with greater precision - you would need to do packing/unpacking manually.



回答4:

sizeof(char) is guaranteed to be 1 by the C standard. Even if char uses 9 bits or more.

So you can do:

type *pt;
unsigned char *pc = (unsigned char *)pt;

And use pc for arithmetic. Assigning pc to pt by using the cast above is undefined behavior by the C standard though.

If char is more than 8-bits wide, you can't do byte-precision pointer arithmetic in portable (ANSI/ISO) C. Here, by byte, I mean 8 bits. This is because the fundamental type itself is bigger than 8 bits.



回答5:

Cast the pointer to a uintptr_t. This will be an unsigned integer that is the size of a pointer. Now do your arithmetic on it, then cast the result back to a pointer of the type you want to dereference.

(Note that intptr_t is signed, which is usually NOT what you want! It's safer to stick to uintptr_t unless you have a good reason not to!)



回答6:

The C99 standard defines the uint8_t that is one byte long. If the compiler doesn't support this type, you could define it using a typedef. Of course you would need a different definition, depending on the the platform and/or compiler. Bundle everything in a header file and use it everywhere.



回答7:

I don't understand what you are trying to say with sizeof(void) being 1 in GCC. While type char might theoretically consist of more than 1 underlying machine byte, in C language sizeof(char) is 1 and always exactly 1. In other words, from the point of view of C language, char is always 1 "byte" (C-byte, not machine byte). Once you understand that, you'd also understand that sizeof(void) being 1 in GCC does not help you in any way. In GCC the pointer arithmetic on void * pointers works in exactly the same way as pointer arithmetic on char * pointers, which means that if on some platform char * doesn't work for you, then void * won't work for you either.

If on some platform char objects consist of multiple machine bytes, the only way to access smaller units of memory than a full char object would be to use bitwise operations to "extract" and "modify" the required portions of a complete char object. C language offers no way to directly address anything smaller than char. Once again char is always a C-byte.