【问题标题】:Casting structures to buffers in embedded code将结构转换为嵌入式代码中的缓冲区
【发布时间】:2018-02-01 21:39:48
【问题描述】:

有时需要将数据结构转换为指针,以便数据可以发送,例如,通过接口发送,或写出到其他流。在这些情况下,我通常会这样做:

typedef struct {
  int    field1;
  char   field2;
} testStruct;

int main()
{
  char *buf;
  testStruct test;

  buf = (char *)&test;

  // write(buf, sizeof(test)) or whatever you need to do

  return 0;
}

然而,最近在一些微处理器代码中,我看到了类似的东西:

typedef struct {
  int    field1;
  char   field2;
} testStruct;

int main()
{
  char buf[5];
  testStruct test;

  *(testStruct *)buf = test;

  // write(buf, sizeof(test)) or whatever you need to do

  return 0;
}

对我来说,前者感觉更安全一些。您只有一个指针,然后将结构的地址分配给指针。

在后一种情况下,如果您不小心为数组buf 分配了错误的大小,您最终会出现未定义的行为或段错误。

启用优化后,我收到来自 gcc 的 -Wstrict-aliasing 警告。但是,同样,这段代码在微处理器上运行,所以我可能会在那里遗漏一些东西吗?

结构中没有指针或任何东西,它非常简单。

【问题讨论】:

  • 你之前的两个问题是这个:stackoverflow.com/questions/48571295/…
  • 这个问题与memcpy 或一般的深拷贝无关。更多的是关于将数据结构转换为缓冲区的正确方法。
  • (testStruct *)buf 可能会为 testStruct 生成未对齐的地址,从而导致总线故障。不使用。 union 更好。
  • write 和朋友使用 void * 作为缓冲区参数,因此不需要强制转换。 write(fd, &test, sizeof(test)) 完全可以,只要您考虑到平台差异。 *(testStruct *)buf = test; 不会通过代码审查。
  • 除了运行未对齐的数据访问的相当严重的风险之外,*(testStruct *)buf = test 是一个坏主意(我会说实际上是一个非常愚蠢的主意),因为它不必要地复制数据。如果您有test,但您只是想暂时将其视为一个 blob o' 字节,那么相当于 buf = (unsigned char *)&test 就是您想要的。 (或者,如果您真的想复制数据,请致电 memcpy。但强制转换和假装结构分配只是一个坏主意,它是 C 早期粗糙、无赖的遗留物。)

标签: c gcc


【解决方案1】:

(testStruct *)buf 可能会为 testStruct 生成未对齐的地址,从而导致总线故障。不使用。

工会更好。它有助于应对抗锯齿问题以及对齐问题。

另见@Steve Summit的好评。

考虑像testStruct_all这样的主类型。

typedef struct {  // OP's structure
  int    field1;
  char   field2;
} testStruct1;

typedef struct {  // Perhaps another structure to send
  double field1;
  char   field2;
} testStruct2;

// A union of all possible structures used in this app
typedef union {
  testStruct1 tS1;
  testStruct2 tS2;
  char buf[1]; 
} testStruct_all;

int main(void) {
  testStruct_all ux; 
  foo(&ux.tS1);  // populate ux.tSn of choice.

  write(ux.buf, sizeof ux.tS1);

  read(ux.buf, sizeof ux.tS1);
  // the union insures alignment and avoids AA issues
  bar(&ux.tS1);
  return 0;
}

write() 通常接受void * @user58697,因此代码可以删除buf 成员并使用:

  write(&ux, sizeof ux.tS1);  //  or whatever you need to do

【讨论】:

  • sizeof(double) != sizeof(int) 在大多数系统上。你的意思是int64_tdouble 之类的吗?或者您是否有意指出这种方法如何解决对不同大小的 structs 的引用?
  • 忽略 write() 调用。它是一个微处理器,如果我将指针分配给一个 SPI tx 寄存器并用中断或其他东西将其锁存出来怎么办?它更多的是关于反序列化/强制转换而不是它的调用。
  • @PatrickRoberts 是的,这个想法是为了展示structs、char[] 在大小和对齐需求上的差异,但对于联合,所有内容都是对齐的,并且作为联合绕过 AA问题。
  • @justynnuff (testStruct *)buf 可以很容易地使需要int 在偶数边界上的uP 失败,但允许char[] 存在于偶数/奇数边界上。
  • 你的问题是正确的答案。但我一直在想,写这段代码的人并没有那么愚蠢。通过在已分配内存的 buf 中执行*(testStruct *)buf = test;,我想知道这是否是他们在当时可能没有 memcpy 的 uP/编译器上执行 memcpy 的一种方式?
猜你喜欢
  • 2021-03-13
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-14
  • 1970-01-01
  • 2012-12-25
相关资源
最近更新 更多