【问题标题】:What are for square brackets in an addressed-of pointer?寻址指针中的方括号是什么?
【发布时间】:2020-12-06 11:33:04
【问题描述】:

我一直在寻找地址指针中的方括号是什么,但我继续没有理解它。以下是函数的几行代码,其中“id”变量是一个 uint32_t 指针,之前已作为参数传递。

#define LIST_STARTED (0x0001) /*defined out of the function*/
#define LIST_FIRST   (0x0002) /*defined out of the function*/

uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];
bool started = *flags & LIST_STARTED;
bool first   = *flags & LIST_FIRST;

if (!started){
    /* does something */
    *flags = MSEC_PRM_MGMT_LIST_STARTED;
    return true;
}
if (!first){
    /* does something */
    *flags |= MSEC_PRM_MGMT_LIST_FIRST;
    *index = 1;
    return true;
}
if (*index == final_index)
    /* does something */
    return false;

*index += 1;

我了解函数的逻辑是什么,但我不明白以下几行是做什么的。我把“所有”代码放在上面以防万一。

uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];

如果有人可以帮助我,我将不胜感激! 谢谢!

我编辑说这段 C 代码在嵌入式系统中运行良好,我没有修改它,我只是在观察它的行为。

【问题讨论】:

  • 不应该 uint32_t *pointer = id;uint32_t *pointer = &id; 吗?请张贴id的声明,以便问题得到解答。
  • @Lundin 来自文本Here are the lines of the function, where "id" variable is an uint32_t pointer that has been previously passed as an argument. 我会说id 的类型为uint32_t *void *
  • @mch 在这种情况下,请发布函数的调用方式。一个minimal reproducible example。整个代码可能是也可能不是严格的别名违规。
  • ID 为 uint32_t *id;
  • @Nkolot 并指向哪里?

标签: c pointers embedded memory-address flags


【解决方案1】:

以下代码尝试将uint32_t 对象读取为两个uint16_t 对象的数组:

uint32_t *id = ...;
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];
uint16_t *index = &((uint16_t *)pointer)[1];

通过flagsindex 指针将uint32_t 对象读取为2 个uint16_t 对象是未定义的行为,因为这违反了严格的别名规则。

正确的做法是:

uint16_t flags = *id; // The lowest order bits of id.
uint16_t index = *id >> 16; // The highest order bits of id.

uint32_tuint16_t 的上述分配中,它会截断id 的最高位。

【讨论】:

  • C 代码没有中断,它已经在嵌入式系统中运行。我只是看到代码,而不是修改它。无论如何,虽然你还没有解决我的疑惑,但还是谢谢你的回答。
  • 如果不知道id 的类型,就不能说这是一个严格的别名违规。如果有的话,uint32_t *pointer = id; 看起来像是一个不应编译的约束违规错误。
  • @Nkolot 它已经在嵌入式系统中运行 并不意味着当您更改编译器、优化选项或重构代码时它不会中断。正确的版本性能不逊色。
  • 有很多草率的编译器会在编译时发出警告,包括 gcc。
  • 而不是 id 是 uint32_t 我希望你考虑成为 uint16_t。在那种情况下,什么是 uint16_t *flags = &(pointer)[0];或 uint16_t *index = &(pointer)[1];为了?对不起我的英语。
【解决方案2】:
uint32_t *pointer = id;
uint16_t *flags = &((uint16_t *)pointer)[0];

相当于。

uint32_t *pointer = id;
uint16_t *flags = (uint16_t *)pointer;

定义:

uint16_t *index = &((uint16_t *)pointer)[1];

相当于:

uint16_t *temp = (uint16_t *)pointer;
uint16_t *index = temp + 1;
//or
uint16_t *index = &temp[1];

这被称为:指针双关语,它被认为是危险的,不可移植。

您可以使用联合来进行安全的双关语(至少在使用 gcc 或其衍生物时)

typedef union
{
    uint64_t u64;
    uint32_t u32[2];
    uint16_t u16[4];
    uint8_t u8[8];
    struct
    {
        uint8_t n1: 4;
        uint8_t n2: 4;
    }u4[8];
}union_pune_t;


uint16_t foo16(uint32_t *p32)
{
    union_pune_t *d64 = (void *)p32;

    return d64 -> u16[1];
}

uint8_t foo8(uint32_t *p32)
{
    union_pune_t *d64 = (void *)p32;

    return d64 -> u8[5];
}

uint8_t foon4(uint32_t *p32)
{
    union_pune_t *d64 = (void *)p32;

    return d64 -> u4[9].n2;
}

【讨论】:

  • u4[16] 最多生成 16 个字节的数据,所以可以跳过那个。
  • 同样关于可移植性,联合意味着依赖字节序的代码,所以这也不是真正的可移植性。而且我不确定这段代码是否真的解决了严格的别名问题,因为你不能在uint32_t* 和联合类型之间切换,除非实际数据的有效类型与联合成员类型之一兼容。如果是其他东西,比如自定义结构,那么这段代码也不安全。
  • @Lundin 他们不会在 gcc、ARM 编译器、IAR 编译器、GHC 上生成 16 字节的数据
  • 是的,他们确实...(4+4 位 = 1 字节,1 x 16 = 16)godbolt.org/z/9dvhsa
猜你喜欢
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多