【问题标题】:Accessing struct elements using single pointer?使用单指针访问结构元素?
【发布时间】:2020-11-05 23:39:21
【问题描述】:

我在一个结构中定义了 5 个浮点数:struct as {float a1, a2, a3, a4, a5;}; 我想使用单个指针 float* p_a = &a1; 访问这 5 个浮点数,然后只使用 p_a++ 等。它会工作吗?转换为数组 float a[5] 需要我想避免的重大更改。

【问题讨论】:

  • 如何将结构更改为具有数组的 union 和 5 个成员?应该由标准明确定义的 IIRC(尽管我不确定 struct padding 是否会与此混淆)
  • 它在技术上可以工作依赖于实现,因为它将成员存储在结构中。我不知道这是否违反了任何标准条款。
  • This 可能有用。
  • @pmg p_a + 4 导致未定义的行为(指针运算只能发生在数组的边界内,为此目的,标量计为大小为 1 的数组)
  • 就在我认为再也没有棘手的(愚蠢的?)C 指针问题的时候......

标签: c pointers memory struct


【解决方案1】:

我宁愿

typedef struct
{
    union
    {
        float fa[5];
        struct { float f1,f2,f3,f4,f5; };
    };
    /* .... */
}MY_T;


void foo()
{
    MY_T s;

    float *p = s.fa;

    p++
    *p = something;
}

【讨论】:

  • 只要是 C99 或更新版本,请注意。
  • 不会f1..f5 共享相同的内存位置,因为它们不在嵌套结构中?
  • 因此,有可能可以使用所有三种表示法,通过指针访问*p = something;,通过数组元素访问s.fa[0] = something; 或通过对象访问s.f1 = something;,并且所有三种方法都访问相同的内存,如果p == &(s.f1),对吧?
  • @RobertSsupportsMonicaCellio 也是指针算法 *p = s.fa; *(p+3) = 某物;
  • @Gerhardh 确实如此。谢谢。
【解决方案2】:

您可以对数组元素指针执行++,只要您在数组中或在它后面的一个元素内。

标准的某些部分 (http://port70.net/~nsz/c/c11/n1570.html#K.3.9.1.2p4) 认为标量等同于一个元素数组,但我不会依赖它。

一个安全的策略应该是使用字符指针进行指针运算,然后在使用前转换为float*。 (详见https://stackoverflow.com/a/62695674/1084774)。

从技术上讲,您不能保证 struct as 的连续性,但实际上它会是连续的,您可以通过 _Static_assert 保证它是连续的。

例子:

struct as {float a1, a2, a3, a4, a5;};

_Static_assert(sizeof(struct as)==sizeof(float[5]),"");

float *subscript_struct_as(struct as *X, unsigned Ix)
{
    return (float*)((char*)X + sizeof(float)*Ix);
}

【讨论】:

    【解决方案3】:

    我想使用单指针 float* p_a = &a1; 访问这 5 个,然后只使用 p_a++ 等。

    p_a 指向a1,它不是数组类型。指针算术只在数组的范围内定义,使指针递增然后取消引用它是未定义的行为。

    会有用吗?

    也许,由于编译器实现指针算法和内存处理的方式。但是语言标准并不能保证,所以你应该避免这种结构。

    (例如,由于构造没有定义,编译器不需要“意识到”您通过递增指针的访问将会改变,例如a2,以及随后读取@987654324 @ 可能会给出旧值,因为编译器优化了内存访问——没有什么可以改变a2,现在可以吗?)

    【讨论】:

      【解决方案4】:

      正如其他人所说,理想的解决方案是使用数组:

      struct as {
          float a[5];
      }
      

      这将允许指针算术任何问题。

      鉴于您(不能或)不想修改结构,您想要做的应该实际上适用于任何实现(但在标准中未定义),因为通常在相同的成员之间不存在填充类型(但在标准中不保证)。

      也可以考虑使用offsetof来检查是否有连续(无填充)。由于offsetof 在编译时工作,您可以在构建时在所有目标架构上验证这一点。

      struct as {
          float f1, f2, f3, f4, f5;
      };
      
      _Static_assert((offsetof(struct as, f1) - offsetof(struct as, f5)) != 4 * sizeof(float));
      

      除此之外,对于“未定义的行为”,您无能为力。

      【讨论】:

        猜你喜欢
        • 2011-06-04
        • 1970-01-01
        • 2023-02-16
        • 2013-06-11
        • 1970-01-01
        • 2017-01-04
        • 2013-05-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多