【问题标题】:Pack trimmed C structs打包修剪过的 C 结构体
【发布时间】:2018-10-28 10:50:08
【问题描述】:

我有一个 C 库(我在 C++ 中使用它),它定义了一个结构和一个对其进行操作的函数。

struct s {
  type1 x1;
  type2 x2;
  type3 x3;
  type4 x4;
  type5 x5;
};

void f(s* x);

我知道f 不会做任何涉及s::x4s::x5 的事情。由于它们对我的目的都没有用,而且我有很多s 的实例,我想将它们分配到一个数组中,以便将它们打包以便s[n+1]s[n].x3 之后立即开始。 执行以下类似操作会导致未定义的行为吗?假设从未使用过 x4x5

struct s_trimmed {
  type1 x1;
  type2 x2;
  type3 x3;
};

size_t num_s = 1000;
char *mem = new char[stuff_before + num_s*sizeof(s_trimmed) + stuff_after];

for (size_t i=0; i<nums; ++i)
  f((s*)(mem + stuff_before + i*sizeof(s_trimmed)));

mem 是一个char 数组,因为它只是一块内存,除了s 的实例之外,我还希望其中包含其他内容。

【问题讨论】:

  • 显示的代码看起来并没有那么糟糕。尽管与stuff_before 相关的所有内容都有些可疑。但是您可能会将它与库实现的函数并行使用。请相应地扩展您的示例。
  • 你没有创建任何s,使用的数组就是UB。它会起作用吗?可能,如果类型都是微不足道的
  • @Yunnosch 是的,我会将它与库实现的函数一起使用。 f 应该代表他们。我只展示了一个简洁的。我明确展示了s 的多个成员,以防有人建议对齐可能是一个问题。 x4x5 仅用于特殊的角落案例。大多数 C 库都不关心它们。
  • @PasserBy:(不幸的是)这是一个见仁见智的问题。如果 C 函数实际上是 init_1_3(s* x) 并写入 x-&gt;x1x-&gt;x3,则它不是 C 端的 UB,并且为了互操作性,它也必须由 C++ 定义。现在确实有一个普遍的说法,即您不能通过写入未初始化的内存来创建 C++ 对象,但也有人声称 C 和 C++ 仍然是可互操作的。两者不可能同时为真。而实际的编译器选择第二种解释。
  • @MSalters 有关于here 的完整讨论。无论如何,由于没有足够的存储空间来存储所有s,因此它们不存在是毫无疑问的。如果它们确实存在,您认为(*s)(mem + stuff_before)[0].x4 应该是什么意思?

标签: c++ c memory-management undefined-behavior


【解决方案1】:

如果您避免任何与对齐相关的问题,您的代码将属于标准作者可能期望质量实现能够有效处理的程序类别,但该标准的 N1570 6.5p7 节允许实现处理以任意方式。请注意 C 标准的编写方式,甚至类似于:

struct foo {int x;} s = {0};
s.x = 1;

将属于同一类别,因为该标准没有描述任何 int 类型的左值可用于影响对象的情况 类型为struct foo,它的左值定义也不适应左值可能具有int 类型但与某些其他类型具有6.5p7 关联的想法。

没有理由让任何质量编译器难以识别将T1* 转换为T2* 并将其传递给作用于T2* 的函数的代码可能访问T1* 类型的对象。在没有-fno-strict-aliasing 标志的情况下,gcc 和 clang 会故意对这种可能性视而不见,但是使用该标志将使它们的行为类似于高质量的编译器。

【讨论】:

    【解决方案2】:

    只要您的函数 f 不处理 x4 和 x5,那么您就可以安全地将结构复制到具有修剪长度的无符号字符数组中并将其传递给函数。示例程序可以是这样的:

    #include <iostream>
    #include <memory.h>
    struct s {
      double x1;
      float x2;
      long long x3;
      int x4;
      int x5;
    };
    struct s_trimmed {
      double x1;
      float x2;
      long long x3;
    };
    void f(s* x)
    {
      std::cout<<x->x1<<std::endl;
      std::cout<<x->x2<<std::endl;
      std::cout<<x->x3<<std::endl;
    }
    int main() {
      size_t nums = 2;
      unsigned char *mem = new unsigned char[nums*sizeof(s_trimmed)];
      unsigned int start = 0;
      for(int i=0;i<nums;i++)
      {
        s* s_obj = new s;
        s_obj->x1 = 10.5;
        s_obj->x2 = 89.98;
        s_obj->x3=28765;
        s_obj->x4=1;
        s_obj->x5=5;
        memcpy(mem+start,s_obj,sizeof(s_trimmed));
        start = start + sizeof(s_trimmed);
        delete s_obj;
        s_obj = nullptr;
      }
      start = 0;
      for(int i=0;i<nums;i++)
      {
        unsigned char *memf = new unsigned char[sizeof(s_trimmed)];
        memcpy(memf,mem+start,sizeof(s_trimmed));    
        f((s*)memf);    
    
      }
      delete[] mem;
      mem=nullptr;
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-29
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2020-12-15
      • 2017-10-16
      相关资源
      最近更新 更多