使用指针访问 long long 变量中的字节

Accessing bytes in a long long variable with pointers

我应该创建一个变量

long long hex = 0x1a1b2a2b3a3b4a4bULL;

然后定义4个指针,分别指向1a1b、2a2b、3a3b和4a4b。然后我打印那些双字节的地址和值。

我的方法是创建一个指针

long long *ptr1 = &hex;

然后使用指针运算得到下一个值。我意识到增加这个指针会增加 long long 字节,而不是像我需要的那样增加 2 个字节。创建短指针

short *ptr1 = &hex;

这是我需要的,但我的编译器不允许,因为数据类型不兼容。我该如何解决这个问题?有没有办法创建一个递增 2 个字节的指针并将其分配给更大数据类型的变量?

您只能通过兼容类型访问任何变量。

但是,char 指针可用于访问任何类型的变量。

请不要将其转换为short* 请参阅下面的注释,它们不是兼容的类型。您只能将 char* 用于符合规范的代码。

引用 C11,章节 §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.

所以,出路是,使用char *并使用指针算法来获得所需的地址。


注意:由于所有其他答案都提出了一个明显错误的方法(将指针投射到 short *,这明显违反了严格的别名),让我扩展一下我的答案和支持引号.

引用 C11,章节 §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.

在这种情况下,shortlong long 不是 兼容的 类型。所以唯一的出路是使用 pointer tochar` 类型。


从问题正文中剪切粘贴

这是由 OP

添加的更新

编辑: 这是不会导致未定义行为的正确解决方案。 编辑 2: 添加了内存地址。

#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;
}

您需要转换指针以将其分配给不同的类型:

short *ptr1 = (short*)&hex;

但是,这样做会导致实现定义的行为,因为您依赖于系统的字节顺序。

添加演员:

short *ptr1 = (short*)&hex;

但是,请务必注意您平台的 endianness。 例如,在 x86 上,数据先存储小端,因此

ptr1[0] 应该指向 0x4a4b

还要注意您平台的实际大小:long long 至少为 64 位,short 至少为 16 位。如果您想确保类型确实是那些大小,请使用 uint64_tuint16_t。如果没有任何类型与您的系统上可用的这些确切大小相匹配,您将收到编译器错误。

此外,注意对齐。您可以将 uint64_t 用作 uint16_t[4],但反之则不行,因为 uint16_t 的地址通常可以被 2 整除,而 uint64_t 的地址通常可以被 8 整除.

Should I worry about the alignment during pointer casting?

不出所料,有人指出这是未定义的行为。这可能是这些愚蠢的 "C course" 作业之一,其中 C 还没有被完全理解。

以防万一你想避免 UB,你可以使用 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;
}

来自 C11 §6.5.2.3,脚注 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.

因此,在某些情况下,您仍然可以 运行 遇到 陷阱表示 的问题,但至少它不是未定义的。结果是实现定义,例如因为主机的字节序。