3

I'm supposed to create a variable

long long hex = 0x1a1b2a2b3a3b4a4bULL;

and then define 4 pointers that point to 1a1b, 2a2b, 3a3b and 4a4b. I'm then printing the addresses and values of those double bytes.

My approach was to create a pointer

long long *ptr1 = &hex;

and then use pointer arithmetic to get to the next value. What I realized was that incrementing this pointer would increment it by long long bytes and not by 2 bytes like I need it to. Creating a short pointer

short *ptr1 = &hex;

Is what I would need but my compiler won't let me since the data types are incompatible. How do I get around that? Is there a way to create a pointer that increments by 2 bytes and assign that to a variable of a larger data type?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Dennis
  • 57
  • 1
  • 2
  • 10
  • Please show a [MCVE]. Or in other words: show your code instead of describing it. – Jabberwocky Jun 01 '17 at 12:55
  • 3
    The 'working' code will violate strict aliasing rules and hence technically be UB. Most compilers will do what you expect, but the Standard doesn't guarantee that anything (good) will happen. – underscore_d Jun 01 '17 at 12:59
  • @MichaelWalz I'll keep that in mind for next time, I also edited my post! – Dennis Jun 01 '17 at 14:20
  • _Hey Dennis, I removed the solution from the question and had put that in my answer (nothing personal, since you accepted my answer, I thought it's best to put it there). A question needs to be a question, it's no place for an answer. Please let me know if you think otherwise. Thank you._ – Sourav Ghosh Jun 02 '17 at 06:06

4 Answers4

8

You can access any variable only through compatible types.

However, a char pointer can be used to access any type of variable.

Please do not cast it to a short* Please see NOTE below , they are not compatible types. You can only use a char* for conforming code.

Quoting C11, chapter §6.3.2.3

[...] When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

So, the way out is, use a char * and use pointer arithmetic to get to the required address.


NOTE: Since all other answers suggest a blatantly wrong method (casting the pointer to short *, which explicitly violates strict aliasing), let me expand a bit on my answer and supporting quotes.

Quoting C11, chapter §6.5/P7

An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 88)

— a type compatible with the effective type of the object,

— a qualified version of a type compatible with the effective type of the object,

— a type that is the signed or unsigned type corresponding to the effective type of the object,

— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or

— a character type.

In this case, a short and a long long are not compatiable types. so the only way out is to use pointer tochar` type.


Cut-'n-Paste from Question body

This was added as update by OP

Edit: Here's the correct solution that doesn't cause undefined behavior. Edit 2: Added the memory address.

#include <stdio.h>
int main() {
    long long hex = 0x1a1b2a2b3a3b4a4bULL;
    char *ptr = (char*)&hex;
    int i; int j;
    for (i = 1, j = 0; i < 8, j < 7; i += 2, j += 2) {
        printf("0x%hx%hx at address %p \n", ptr[i], ptr[j], (void *) ptr+i);
    }
    return 0;
}
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Isn't this what the OP wants though? whether or not someone considers it bad/good practice? – kabanus Jun 01 '17 at 12:59
  • 1
    @kabanus It's not about practice, it's wrong to use different incompatible types. – Sourav Ghosh Jun 01 '17 at 13:01
  • So I would need to create 8 pointers? My assignment only wants 4 so I'm suspecting they want it done with short pointers.. your way, it would look like this for the first 2 bytes: `char *ptr1 = (char*)&hex+1;` `char *ptr2 = (char*)&hex;` `printf("%hx%hx", *ptr1, *ptr2);` Is there a way to format that a bit more efficiently? – Dennis Jun 01 '17 at 13:17
  • @Dennis Although this is the correct answer, your assignment probably expects code as in e.g. Lanting's answer. Just keep in mind it's undefined behavior and never use something like this in a real-world software. For a way avoiding the undefined behavior (but not the implementation-defined result), see my answer. –  Jun 01 '17 at 13:22
  • @Dennis No, you don;t need 8 pointers. Notice the part.. _"...Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object."_ So, you can have only one pointer and using pointer arithmetic, you can _get_ the required values. – Sourav Ghosh Jun 01 '17 at 13:37
  • @SouravGhosh I added my solution using char pointers to my original post. – Dennis Jun 01 '17 at 13:59
  • @FelixPalmen I looked at your answer but we haven't covered unions yet.. so I'll have to read up on those before I can make sense of that. Thanks, though! – Dennis Jun 01 '17 at 13:59
2

As expected, it has been pointed out that this is undefined behavior. It's probably one of these stupid "C course" assignments where C isn't completely understood.

Just in case you want to avoid the UB, you could solve it using a union:

#include <stdio.h>

union longparts
{
    unsigned long long whole;
    unsigned short parts[4];
};

int main(void)
{
    union longparts test;
    test.whole = 0x1a1b2a2b3a3b4a4bULL;

    for (int i = 0; i < 4; ++i)
    {
        unsigned short *part = &test.parts[i];
        printf("short at addr %p: 0x%hx\n", (void *)part, *part);
    }
    return 0;
}

from C11 §6.5.2.3, footnote 95:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.

So, you could still run into problems in some cases with trap representations, but at least it's not undefined. The result is implementation defined, e.g. because of endianness of the host machine.

1

add a cast:

short *ptr1 = (short*)&hex;

However, make sure you pay attention to the endianness of your platform. On x86, for instance, data is stored little end first, so

ptr1[0] should point to 0x4a4b

Also pay attention to your platforms actual sizes: long long is at least 64bit, and short is at least 16 bit. If you want to make sure the types are really those sizes, use uint64_t and uint16_t. You'll get a compiler error if there aren't any types matching those exact sizes available on your system.

Furthermore, take note of alignment. You can use uint64_t as uint16_t[4], however not the other way around, as the address of a uint16_t is usually dividable by two, and the address of uint64_t dividable by 8.

Should I worry about the alignment during pointer casting?

Lanting
  • 3,060
  • 12
  • 28
0

You need to cast the pointer to assign it to a different type:

short *ptr1 = (short*)&hex;

However, doing this results in implementation-defined behavior, since you're depending on the endianness of the system.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 3
    The results are not implementation defined, the behaviour is undefined, because it breaks the strict aliasing rule. – mch Jun 01 '17 at 13:03