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

时间:2017-06-01 12:54:14

标签: c pointers pointer-arithmetic strict-aliasing

我应该创建一个变量

long long hex = 0x1a1b2a2b3a3b4a4bULL;

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

我的方法是创建一个指针

long long *ptr1 = &hex;

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

short *ptr1 = &hex;

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

5 个答案:

答案 0 :(得分:8)

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

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

请不要将 投射到short* 请参阅下面的注释,它们不是兼容类型。您只能使用char*来验证代码。

引用C11,章节§6.3.2.3

  

[...]当指向对象的指针转换为指向字符类型的指针时,   结果指向对象的最低寻址字节。连续增量   结果,直到对象的大小,产生指向对象剩余字节的指针。

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

注意:由于所有其他答案都提示了一个明显错误的方法(将指针强制转换为显式违反严格别名的short *),让我对我的答案和支持引号进行扩展。

引用C11,章节§6.5/ P7

  

对象的存储值只能由具有其中一个的左值表达式访问   以下类型: 88)

     

- 与对象的有效类型兼容的类型,

     

- 与对象的有效类型兼容的类型的限定版本

     

- 对应于有效类型的有符号或无符号类型   对象,

     

- 对应于合格版本的有符号或无符号类型的类型   有效的对象类型,

     

- 包含其中一种上述类型的聚合或联合类型   成员(包括,递归地,子集合或包含的联合的成员),或

     

- 字符类型。

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

来自问题正文的剪切 - 粘贴

这是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;
}

答案 1 :(得分:2)

正如所料,已经指出这是未定义的行为。这可能是这些愚蠢的“C课程”作业之一,其中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:

  

如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的相应部分将被重新解释为对象表示形式。 6.2.6中描述的新类型(有时称为''punning''的过程)。这可能是陷阱表示。

因此,在某些情况下,使用陷阱表示仍可能遇到问题,但至少它没有未定义。结果是实现定义,例如因为主机的字节顺序。

答案 2 :(得分:1)

添加演员:

short *ptr1 = (short*)&hex;

但是,请务必注意平台的This is the entire slide。 例如,在x86上,数据首先存储在最后,所以

ptr1[0]应指向0x4a4b

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

此外,请注意对齐。您可以将uint64_t用作uint16_t[4],但不能相反,因为uint16_t的地址通常可以分为两个,uint64_t的地址可以分为8 {{1}}

endianness

答案 3 :(得分:0)

int main() {
    long long hex = 0x1a1b2a2b3a3b4a4bULL;
    short *ptr1 = (short*)&hex;
    return 0;
}

这通常是一个警告,你可能有&#34; Werror&#34;编译器标志。

答案 4 :(得分:0)

您需要转换指针以将其指定为其他类型:

I wondered if it was possible to force Scala to use ForkJoinPool with a preset number of threads decided by us GLOBALLY

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