Click here to Skip to main content
16,020,305 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
Hi,
I've been experimenting with pointers in C, and I've come across a situation where in the memory allocated is seen in bytes, while I need to extract a 32-bit Data type from that buffer. (No this isn't homework!). In assembler it was as simple as a "mov dword eax, [memory_buf+offset]" :)
I try the following program:
C
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main(int argc, const char* argv[])
{
	uint8_t* cast1;
	cast1 = (uint8_t*)malloc(50);
	cast1[5] = 1;
	cast1[6] = 2;
	cast1[7] = 3;
	cast1[8] = 4;
	uint32_t x = (uint32_t)cast1[5];
	printf("%d\n", x);
        return 0;

}

The output of this program is:
1

Since sizeof(uint32_t) = 4, shouldn't the integer take the entire '1234' as one? Why so?
Posted
Comments
[no name] 6-Jun-14 1:23am    
You're getting the correct result. What are you expecting?
sid2x 6-Jun-14 2:10am    
I am expecting a 1234 instead of a 1 in the integer.
Sergey Alexandrovich Kryukov 6-Jun-14 2:21am    
Oh, I think I understand where the OP's confusion is. Just a little confusion, maybe because of the experience of the one user to lower-level, CPU and Assembly. Please see Solution 2 where I try to explain it.
—SA

No. Everything is correct. Here is the thing: you are not type-casting a pointer; you are type-casting the byte. Here is what happens, if we break it into smaller steps:
C++
// type of cast1 is uint8_t*
// therefore, type of cast1[5] is uint8_t, so:
uint8_t someByte = cast1[5];
uint32_t x = (uint32_t)someByte;


The cast shown above is not reinterpret cast (in C++ terms). This is actually the conversion from smaller integer type to a bigger type, which is done the way the "semantic" (mathematical) values in both types would be the same (which is not always possible by converting bigger type to smaller, but always possible if you do the opposite, as in this case). In unsigned types (and even in more complex case of two's complement numbers) it takes zero value and fills in the least significant byte(s) from the source object (someByte).

—SA
 
Share this answer
 
Comments
sid2x 6-Jun-14 2:32am    
Excellent explanation! I hope you don't mind if I ask another question,
How am I supposed to grab a 32-bit value from a block of memory which is an array of
8-bit values?
Stefan_Lang 6-Jun-14 2:50am    
Typically not by casting. Just interpret the byte sequence as series of digits of a number represented to the base of 256.
Legor 6-Jun-14 3:13am    
Please accept the answer if it helped you.
Look carefully at this line
uint32_t x = (uint32_t)cast1[5];


It literally says get the value at cast1[5] and upcast it to 32 bits ... clearly that is not what you wanted as that returns 1 lifted back as a 32 bit result ... which is exactly what you got.

What you wanted was the 32 bit value that starts a cast1[5] so you need a 32 pointer to that statement looks like this
(uint32_t)&cast1[5]

Okay now you don't want the pointer your want the value so you have to dereference it back
*((uint32_t)&cast1[5])

so the final result looks like
uint32_t x = *((uint32_t)&cast1[5]);

That is all very ugly but it did what you wanted and showed you the literal

The normal easier way and more readable way is to use a 32 bit temp pointer
uint32_t* dummyptr = (uint32_t*)&cast1[5]; // Create 32 bit dummy pointer
uint32_t x = *dummyptr;                    // Get value at pointer

Any optimizing compiler will immediately recognize it can remove the dummy pointer so it cost you nothing it just makes the code more human readable.
 
Share this answer
 
Comments
Stefan_Lang 6-Jun-14 2:47am    
I'm too lazy to test or look up what the standard says - but casting an odd memory address to a pointer to a 32bit type may cause problems- at least when you have 32 bit variables allocated on the stack or heap they will always be aligned to 32 bit!
[no name] 6-Jun-14 5:27am    
This is wrong. This line won't even compile:
uint32_t x = *((uint32_t)&cast1[5]);
Here you're trying to dereference a uint32_t.
Stefan_Lang 6-Jun-14 5:59am    
Not sure what you're trying to say. the code you present is wrong, but is not the same as posted in this solution.

The solution may be wrong on this aspect, but not for the reason you state - see my own comment above.
[no name] 6-Jun-14 6:02am    
See under the line "so the final result looks like".
leon de boer 6-Jun-14 7:45am    
He is correct I left the pointer "*" out in the final line it should be

*((uint32_t*)&cast1[5]) .... Simple typo error because it's sort of a naff way to write it.

The reasoning is simple

&cast1[5] is a pointer to a uint8_t

(uint32_t*) is required to stop the compiler warning because the above pointer is to a unit8_t and it is going to a uint32_t it will work but with a compiler warning.

*(...) is the dereference to the bit in the brackets

So the complete thing and final line should have been:

uint32_t x = *((uint32_t*)&cast1[5]);

The logic above is exactly what the compiler does but as I said it's hard to human read and easy to make errors like I did. That is why the dummy pointer is the much better and more standard way to do it.

Whichever way you go typecasting is dangerous and you need to use only when absolutely needed.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900