Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make pointer point to two or more consecutive cells

Tags:

c

pointers

Suppose I have a uint8_t pointer point to the third element in an array, {1, 2, 4, 5} so *p is 4. How can I get it to point to 4 and 5 so that *p is 45. Is it even possible? I'm currently typecasting. Here's what I do e.g. if data is my array {1, 2, 4, 5} and I want to get 4 and 5.

uint8_t dummyAddress = data[2]; 
dummyAddress = *((uint16_t*) dummyAddress);
*p = *dummyAddress; 

My understanding is that p should now point to 4 and 5 but I'm not. Instead I get a seg fault when I execute the second line. p is a uint64_t * const. It works to get one byte, but when applying same logic to two bytes I'm not getting it right.

like image 851
ponderingdev Avatar asked Nov 03 '15 16:11

ponderingdev


2 Answers

You can do this:

uint8_t data[] = { 1, 2, 4, 5 };
uint8_t *p8 = &data[2];
uint16_t u16 = *(uint16_t*)p8;

Then (uint16_t*)p8 will point to data[2] and data[3]. But you cannot easily access *(uint16_t*)p8. First this is because p8 might not be aligned on uint16_t boundary. Some processor cause an exception in this case. Second, even if you can access *(uint16_t*)p8 you might get different results depending on the processor's byte order (big endian/little endian). You might get 0x0405 or you might get 0x0504.

Please also note that in your line

dummyAddress = *((uint16_t*) dummyAddress);

you are first reinterpreting dummyAddress as a pointer to uint16_t and then dereferencing it, so returning a uint16_t and storing this in dummyAddress. This clearly is not what you wanted to do. If this really compiles, please consider compiling with a higher warning level and taking a look at every compiler warning (and only ignoring it if you understand it and are sure it is not a programming error).

BTW, you will not be able to get a pointer p where *p==45 as you request in your question.

Also, as mch said: What is the strict aliasing rule?

like image 115
Werner Henze Avatar answered Oct 13 '22 01:10

Werner Henze


If I understand what you are doing, then yes, you can access both bytes by casting to uint16_t, but you will run into problems (or endianess issues) with how the order the bytes are stored in memory as well as violate strict-aliasing. This will probably return an answer you were not suspecting. For example:

#include <stdio.h>
#include <stdint.h>

int main (void) {

    uint8_t array[] = {1,2,3,4};
    uint8_t *p = array + 2;

    printf ("\n the uint8_t answer  : %hhd\n", *p);
    printf (" the uint16_t answer : %hd\n\n", *(uint16_t *)p);

    printf ("**note: the will cast result in bytes 4, 3"
            " (or (4 << 8 | 3) = 1027)\n\n");

    return 0;
}

Output

$ ./bin/cast_array

 the uint8_t answer  : 3
 the uint16_t answer : 1027

**note: the cast will result in bytes 4, 3 (or (4 << 8 | 3) = 1027)

The bytes are stored in memory on my system in little endian order. So when you cast to uint16_t it expects to find least significant, most significant bytes. Which in the example above with 34 in memory, the cast will interpret the actual order as 43 resulting in a value of 1027 instead of (3 << 8) | 4 = 772.

As noted, you will be violating rules related to strict aliasing where type pruning by casting to anything other than char results in undefined behavior. The results of all of these actions are hardware and compiler dependent and should not be used for anything other than a learning exercise.

like image 22
David C. Rankin Avatar answered Oct 13 '22 01:10

David C. Rankin