0

How safe is reading a void * pointing to a char as an int?

Example: To test the first bit of a char in a system where 8 bit chars are much slower to access than 32 bit ints.

char c = 'B'; // a char here to illustrate the potentially dangerous case, but the
              // point is this could be a char, could be an int... I am just
              // interested in the first bit
void *v = &c;
int i = *(int *)v;
if (i & 0x01)
{
    printf("yep");
}

Seems to work, but if my char (c) was right at the edge of the valid memory allocated to this process, would it be reading into invalid memory? Or is the system clever enough to stop copying after the first 8 bits?

thanks

jayjay
  • 1,017
  • 1
  • 11
  • 23
  • Reading an 8-bit char will be at least as fast as reading a 32-bit int. *Writing* might not be, but that's a different problem. – Roddy Jan 18 '14 at 22:20
  • 1. Choose between C++ or C 2. Guess it is C as `printf` is invoked. 3. Avoid void pointers. You are embarking on the void/black hole – Ed Heal Jan 18 '14 at 22:20
  • 4. Don't use C-style cast ( if this is C++ ) – olevegard Jan 18 '14 at 22:21
  • This is unlikely to be a good approach for all the other reasons mentioned in comments and answers. However, if you want to do this with an implementation which might have reasonably well defined semantics, use a `union`, not casts. – Ned Jan 18 '14 at 22:30

5 Answers5

3

It's undefined behavior in all cases.

In most practical systems, it will read normally from both c and adjacent memory, failing (with access violation aka segmentation fault) if c is close to the end of a memory page and the adjacent page is marked unreadable.

In a smaller but still non-trivial number of systems, you're likely to get an alignment fault and/or read a 32-bit value overlapping with c but with c at some location other than the start.

But if the compiler catches you doing this, it has permission from the Standard to "optimize" all your code in very broken ways.


If you're afraid of systems where 8-bit accesses are slow, then use int_fast8_t instead of char. That lets your build environment pick a better variable size.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • How is it undefined? `char* -> void* -> int* -> int` is undefined? – Brandon Jan 18 '14 at 23:03
  • @CantChooseUsernames: You're reading a value of type `int&` which references memory where no `int` exists. (An `int` exists where there is sufficient storage, and `c` is not sufficient storage for an object of type `int`. See Standard 3.8p1) You also haven't derived the pointer safely, nor have you guaranteed correct alignment. – Ben Voigt Jan 18 '14 at 23:04
  • From 5.2.9.13: A prvalue of type "pointer to cv1 `void`" can be converted to a prvalue of type "pointer to cv2 `T`," where `T` is an object type... If the original pointer value represents the address `A` of a byte in memory and `A` satisfies the alignment requirement of `T`, then the resulting pointer value represents the same address as the original pointer value, that is, `A`. **The result of any other such pointer conversion is unspecified.** – Ben Voigt Jan 18 '14 at 23:10
2

No, it's not safe.

And your use case is totally artificial as I have NEVER heard of a system where reading a 32-bit value is faster than reading an 8-bit one. If anything, it's sometimes the other way around.

(And even if you found such a system, leave "optimizations" like this to the compiler. it will do a much better job.)

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • 1
    Some systems cannot natively read 8-bit values at all. Access to `int8_t` variable is done using 32-bit read and bit masking and shifting. (But on such systems the mask and shift is almost always made very fast, even free) – Ben Voigt Jan 18 '14 at 22:24
  • @BenVoigt You mean that several instructions are required? Rather than the usual 32/64 -bit memory access and the processor automatically masking the required byte? – Roddy Jan 18 '14 at 22:27
  • 1
    Sometimes the memory bus does the masking, sometimes the memory read is full-width and the processor does, and I'm sure there are rare cases where additional instructions are needed. – Ben Voigt Jan 18 '14 at 22:28
  • @BenVoigt Anyway, a system that can't read bytes is unlikely to be happy reading a 32-bit value from an address that isn't 32-bit aligned? – Roddy Jan 18 '14 at 22:28
  • 1
    Absolutely true. And I also agree that the compiler should be trusted to make `char` access into something sensible. The only reasonable source-level optimization would be to use `int_fast8_t` instead of `char`. – Ben Voigt Jan 18 '14 at 22:29
1

The system is not clever enough, and your program is prone to memory access violations

barak manos
  • 29,648
  • 10
  • 62
  • 114
1

It's simply undefined behavior.

On the other hand I can imagine a situation where this will work. Notice that after declaring c, you declare v. Since c is on stack, by casting it's address to void* and then dereferencing it you might get int which will be made of 1 byte from c and 3 bytes from v. But this is all just one of many possible implementations.

tumdum
  • 1,981
  • 14
  • 19
1

The int variable is going to be loaded with 3 bytes plus the one you want to load. Also your cast to int may cause misalignment. And, this cast is not portable between platforms that differs in endianess according to the way you mask inside the if. This is a bad approach, rearchitect your system to avoid the current situation.

Felipe Lavratti
  • 2,887
  • 16
  • 34