当你在 C 语言的结构体中有一个数组时,最好的做法是什么?

What's the best practice when you have an array in a struct in C?

提问人:Thibault de Villèle 提问时间:12/6/2018 最后编辑:Thibault de Villèle 更新时间:12/7/2018 访问量:169

问:

我有一个自定义结构,我将使用它通过 TCP 连接发送数据。在这个结构中声明数组的最佳方法是什么?会不会是:

typedef struct programData {
    int* dataArray;
    size_t numberofelements;
} pd;
// ...
pd data = {0};
data.dataArray = malloc(5*sizeof(int));
// put content in array ...
data.numerofelements = 5;

或者会是这样:

typedef struct programData {
    int dataArray[5];
} pd;
// ...
pd data = {0};
data.dataArray[0] = ...;
// ...
data.dataArray[4] = ...;

出于在 C 语言中使用的习惯,我做了第一种方法,但不要认为数组的内容实际上会传递给连接另一端的客户端,因为实际上是指向服务器内存内内存地址的指针。或者实际上会用它发送数组的内容?malloc()dataArraysend(2)

编辑:由于从我的代码中复制粘贴而导致的一些不连贯性

c 结构 体网络编程 发送

评论

5赞 Lundin 12/6/2018
最好的方法是根本不使用结构体作为通信协议。您必须同时考虑对齐和网络一致性。您必须处理填充。正常的方法是编写某种形式的序列化/反序列化例程。
0赞 ryyker 12/6/2018
您是发送整个 via ,还是仅发送数组成员?如果是整个结构,那么 Lunden 的评论是很好的建议,否则最佳实践的答案仍然会因其他约束而异。structTCP
1赞 alk 12/6/2018
我怀疑这是一个错别字,你的意思是.你想要一个数组,对吧?int* dataArray[5];int dataArray[5];int
0赞 alk 12/6/2018
这也没有意义,因为指针也不是。data->data
1赞 alk 12/6/2018
请阅读文档并了解它需要指针和大小。然后,它尝试发送从地址开始的字符到指针指向的字符。就是这样,非常简单明了。从要发送的数据块的任何成员引用的不同区域收集内存没有魔法。 不知道它在发送什么send()sizesend()

答:

1赞 ryyker 12/6/2018 #1

最佳做法是让项目的要求决定使用哪种方法。两者都具有明显的优势,具体取决于需要什么。

举两个例子:

1)

typedef struct programData {
int dataArray[5];//assuming '*' was a typo
} pd;

2)

typedef struct programData {
    int* dataArray;
    size_t numberofelements;
} pd; 

如果您在运行时之前就知道大小要求,则始终首选选项 1)(更简单的方法)。如果没有,则需要选项 2),但有其成本。内存的动态分配增加了代码在错误处理和内存管理方面的复杂性,并确保使用 calloc 和 family 的所有内容在使用完后都会被释放。

建议使用序列化和反序列化来传输任一形式。(并且对于选项 2 是必需的,因为使用了指针。实施的额外严格性在提高所发送内容的可预测性方面带来了好处。

评论

0赞 Thibault de Villèle 12/6/2018
谢谢!但是,由于我将通过 TCP 连接发送结构,因此会将数组的内容复制到套接字缓冲区中,还是仅复制服务器内存中数组的内存位置?send(2)
0赞 alk 12/6/2018
@ThibaultdeVillèle:请看我的评论。
0赞 ryyker 12/6/2018
首先,我会密切关注 Lundin 在您帖子下的评论中的言论。如果必须发送整个结构,那么请熟悉编译指示包技术,以及通过 传递结构体时的字节序问题。但是,仍然不推荐使用这种方法。发送结构数据的最佳方式是序列化成员数据,然后发送。TCP
1赞 Thibault de Villèle 12/6/2018
@ryyker谢谢,在进一步完成任务之前,我们将更多地研究序列化
0赞 Thibault de Villèle 12/6/2018
@alk感谢您的解释。我假设它是这样工作的,但我不确定,手册页对我来说似乎有点模糊send()
3赞 Eric Postpischil 12/6/2018 #2

send不是用于传输复合数据结构的服务,包括解释指针和连接数据的含义。它是一种用于发送原始字节的服务。使用 时,必须将数据转换为可发送的原始字节。接收方必须从这些字节构建自己的数据结构。这意味着您必须创建一个使用字节表示数据的方案。send

当一个结构的原始字节被发送到另一个系统,并且接收系统使用这些相同的原始字节来表示一个结构时,数据的最终含义可能会有所不同,原因包括:

  • 系统以不同顺序表示具有字节的对象(例如整数)。
  • 系统在结构中插入不同数量的填充字节,以保持硬件要求或首选的对齐方式。
  • 系统对字符或浮点数据使用不同的编码。
  • 系统上的类型是不同的,因为一个可能使用两个字节,而另一个使用四个字节。int
  • 一个系统上的指针在另一个系统上毫无意义,因为它们指向从未传输到另一个系统的数据,并且包含与另一个系统上的地址布局无关的地址。

使用简单的数据结构,可以定义传输原始字节的协议,以发送表示数据结构的实际字节。如果发送和接收系统使用相同的硬件和软件,则尤其如此。但是,即使在这种情况下,也应明确指定协议:每个元素有多大,使用什么数据编码,每个元素中的字节顺序如何,等等。

假设你有简单的数据结构,并使用一个简单的协议来发送表示数据的实际字节,那么当然,在结构中声明一个数组是最简单的。如果阵列很小或通常接近满,因此通过存储和传输未使用的数据只会产生少量的浪费,那么在结构内部声明阵列可能是一个很好的解决方案。

如果阵列中所需的数据量变化不大,则通常首选动态分配阵列,以考虑资源效率。如问题所示,该结构可能包含一个指针,该指针填充了数组数据的地址。

当结构包含此类指针时,不能发送指针(如果不做出额外的努力来提供其解释)。相反,您需要使用一个或多个调用来发送结构中的其他数据,然后需要另一个调用来发送数组中的数据。当然,用于传输数据的协议必须包括一种通信所发送的数组元素数量的方法。sendsendsend

还有一种选择既混合了数组的动态空间分配,又将数组包含在结构中:结构的最后一个元素可以是灵活的数组成员。这是一个在结构中声明为的数组。它必须是结构的最后一个元素。它没有固有大小,但是,在为结构分配空间时,您将为数组添加额外的空间。在这种情况下,数组不是具有指向数组的指针的结构,而是遵循内存中结构的基本部分。这种结构及其数组可以在单个调用中发送,前提是提供了上述注意事项:接收系统必须能够正确解释字节,并且必须传达数组的大小。Type dataArray[];send