当前位置: 首页 > 知识库问答 >
问题:

在C中,将结构的标量成员视为包含数组是否有效?

邢弘业
2023-03-14

在查看Dear Imgui的代码时,我发现以下代码(为关联性而编辑):

struct ImVec2
{
    float x, y;
    float& operator[] (size_t idx) { return (&x)[idx]; }
};

很明显,这在实践中是有效的,但从C标准的角度来看,这段代码合法吗?如果没有,是否有任何主要编译器(G,MSVC,Clang)提供任何显式或隐式保证,即此代码将按预期工作?

共有2个答案

柴耀
2023-03-14

现实情况是,双关语类型解决方案已经在C中成功使用了很长时间。问题是它是脆弱的,而C不是C - 出现了你可能无法解释的其他问题。

对于您可能会喜欢的解决方案,我建议使用引用访问器:

#include <iostream>
#include <stdexcept>

struct point
{
  double xy[2];

  double & x() { return xy[0]; }
  double & y() { return xy[1]; }

  double const & x() const { return xy[0]; }
  double const & y() const { return xy[1]; }

  double       & operator [] ( std::size_t n )       { return xy[n]; }
  double const & operator [] ( std::size_t n ) const { return xy[n]; }
};

int main()
{
  point p{ 2, 3 };
  
  std::cout << p[0] << ", " << p.x() << "\n";
  std::cout << p[1] << ", " << p.y() << "\n";
  
  p[0]  = 5;
  p.y() = 7;
  std::cout << p[0] << ", " << p.x() << "\n";
  std::cout << p[1] << ", " << p.y() << "\n";
  
  auto f = []( const point & p )
  {
#if 0
    p[0]  = 11;  // won't compile
    p.y() = 13;  // won't compile
#endif
    std::cout << p[0] << ", " << p.x() << "\n";
    std::cout << p[1] << ", " << p.y() << "\n";
  };
  f( p );
}

编译得非常干净。

您可能很想直接使用引用:

struct point
{
  double xy[2];
  double & x;  // DON’T DO THIS
  double & y;  // DON’T DO THIS

  point() : x{xy[0]}, y{xy[0]} { }
  point( double x, double y ) : x{xy[0]=x}, y{xy[1]=y} { }
};

最后一种方法的问题是它破坏了const保证。也就是说,即使您在某个地方有一个const点,您仍然可以通过引用对其进行修改。

void f( const point & p )
{
  p[0] = 97;          // compiler complains properly
  p.y  = 3.14159265;  // compiler blithely accepts this
}

因此,使用引用访问器方法。

汤兴生
2023-03-14

这个代码合法吗?

不,它有未定义的行为。表达式<代码>

是否有任何主要编译器(G、MSVC、Clang)明确或隐含地保证此代码能够按预期工作?

未定义的行为意味着任何1 都可能发生,包括但不限于提供预期输出的程序。但永远不要依赖(或基于)具有未定义行为的程序的输出。该程序可能会崩溃。

所以你看到的(可能看到的)输出是未定义行为的结果。正如我所说的,不要依赖有UB的程序的输出。程序可能会崩溃。

因此,使程序正确的第一步是删除UB。然后,只有这样,您才能开始推理程序的输出。

1有关未定义行为的更精确的技术定义,请参阅以下内容,其中提到:对程序的行为没有任何限制

 类似资料:
  • 问题内容: 我正在尝试使用。我对处理包含数组的C结构感兴趣。考虑以下 并假设我已经将其编译到共享库中了从Python我可以做这样的事情 到目前为止一切顺利:我已经创建了正确的类型,并且一切似乎都正常。但是之后: 我显然缺少了一些东西。该类型是公认的(否则呼叫将抛出一个错误),但不正确初始化。 问题答案: 代码有2个问题(如我在评论中所述): print_array_struct.argtype-

  • 我不确定安德烈·卡隆在这里是什么意思: C中的虚拟函数 ...其中一些代码依赖于(正式的)非标准行为,这些行为“恰好”在大多数编译器上工作。主要问题是代码假设 < code>m的类型为< code>struct meh *。类型为< code>struct foo *的对象f通过向< code>struct meh *的强制转换被赋给m。< code>struct meh具有< code>stru

  • 问题内容: 以下是gcc 4.4.4下的简单代码段错误 将最后一行更改为 工作良好。使用编译时,这两个版本均可使用。我是在简单地调用未定义的行为,还是在标准中进行了某些更改,从而使代码可以在C99下工作?为什么在C89下崩溃? 问题答案: 我相信C89 / C90和C99中的行为均未定义。 是数组类型的表达式,特别是。 C99 6.3.2.1p3说: 除非它是 sizeof 运算符或一元 & 运算

  • 做这样的事情可以吗? 我正在通过从文件写入整个结构来更改常量结构成员。应该如何处理这个问题?如果一个或多个结构成员是常量,则写入非常量结构是未定义的行为吗?如果是这样,处理常量结构成员的公认做法是什么?

  • 如果我有以下结构 我初始化为 这应该值初始化对象并根据标准执行以下操作: 如果T是一个(可能是cv限定的)类类型,没有用户提供或删除的默认构造函数,则该对象为零初始化,并检查默认初始化的语义约束,如果T有一个非平凡的默认构造函数,则该对象为默认初始化的; 有一个非常重要的构造函数,可以通过以下方法进行检查: 该标准是否意味着我的对象在值初始化的情况下应该始终为零初始化,然后随后默认初始化? 如果我

  • 我对C++还很陌生,在研究构造函数和析构函数行为时,我发现了这个问题: 当我运行上面的代码时,输出是: 但是,当我将更改为并将更改为时,则输出为: 有人能解释一下为什么这个改变会使析构函数少运行一次吗?