在C语言中,使用strlen和malloc有什么区别?

3

What's the difference between (1)

char s[5] = {'a', 'b', 'c', 'd', '\0'};
char s2[strlen(s) + 1]; 
strcpy(s2, s);

并且(2)

char s[5] = {'a', 'b', 'c', 'd', '\0'};
char *s2 = malloc(strlen(s) + 1);
strcpy(s2, s);

在什么情况下应该使用它们中的每一个?当你确定数组s是静态大小时,你只能使用第一个吗?
2个回答

7
在情况1下,
char s2[strlen(s) + 1]; 

s2 是一个数组。在第二种情况下,

char *s2 = malloc(strlen(s) + 1);

s2是一个指针。

顺便提一下,如果编译器不支持VLA,第一种方法将无法工作。

根据最新标准C11,VLA是一个可选的特性,编译器不必支持。第二种方法是C标准库的一个组成部分,无需任何额外的编译器支持即可使用。


谢谢,我会继续使用malloc方法。 - user3247608
3
好的,按照您的要求进行翻译:请记得在使用后free()它。祝愉快! :) - Sourav Ghosh
既然你坚持要“可选”部分,那么你是否知道有哪些符合C11标准的编译器不支持VLA? - Jens Gustedt
@JensGustedt 不好意思,我个人并不了解这样的编译器。然而,由于这是一个可选功能,可能存在某些编译器支持。此外,在 C99 之前不太可能支持 VLA(我没有考虑 GCC 扩展)。如果我有任何错误,请您指正。 - Sourav Ghosh
1
当然,“可能存在”。我只是认为这种可能性非常小。所有遵循C99标准的编译器都有它,它们不会自愿去除该功能并扰乱其现有客户。我所知道的唯一一个未采用VLA的主流编译器来自微软公司。而且他们远未使用C11。他们甚至没有符合要求的预处理器。 - Jens Gustedt

1
您需要考虑三种情况:简单静态分配数组、可变长度数组(VLA,第一个示例)和动态分配数组(第二个示例)。
需考虑的方面很多,没有黑白之分。
实际应用需求:如果项目数量在编译时已知,则通常无需使用动态内存分配(除了在本答案后面提到的大量数据)。请注意,如果程序设计合理,则总是存在最坏情况的上限分配项目数,因为没有程序能处理无限量的数据。根据最坏情况所需的项目数量,你将不得不决定是否使用动态分配。作为经验法则,当项目数量庞大时,请使用动态分配。
在单进程系统(如裸机嵌入式系统)中使用动态内存分配也没有任何意义,因为没有其他共享RAM。这样的系统可以将整个RAM静态分配,因为它可以自由访问100%的系统资源。
性能:静态分配数组是最快的。可变长度数组(VLA)可能不会慢太多,但可能会引入一些额外的开销代码。堆上的动态分配是最慢的,因为它涉及查找、多个函数调用、碎片处理等。
进程内存保留:在所有主流操作系统上,进程具有有限的堆栈和.data/.bss内存。然而,堆远不如此受限,可能仅受限于运行程序的计算机中存在的RAM数量。因此,在分配大量内存时,应始终使用动态内存分配。
安全性:动态内存分配因导致内存泄漏和堆碎片而臭名昭著。在嵌入式系统编程中,由于这些原因,通常完全禁止使用动态内存分配,并且在每个与安全相关的编程标准中也禁止使用。
兼容性/可移植性:可变长度数组(VLA)在C99标准中引入,不会在古老的编译器上编译。在C11中,VLAs不再是必须实现的,尽管实际上我非常怀疑任何C11编译器都会拒绝实现它们。C++不支持VLAs。
我认为,VLA的主要好处不在于分配数组,而是让指针类型更加安全。例如,VLA使得这样的函数成为可能:void func (int x, int y, int array[x][y]) {},其中array实际上是一个指针,而不是数组。
一边注释:你提供的两个示例在性能方面都写得很差。它们都应该使用sizeof(s)而不是strlen。

你最后一段似乎在推荐微观优化。任何现代编译器都会用常量替换 strlen 调用。 - M.M
1
@MattMcNabb 将事物从运行时转移到编译时不是微观优化,而是良好的编程实践。因为编译时项目会被编译器检查。例如考虑这个错误:static char s[5]; char s2[strlen(s)];。我们成功创建了一个零长度数组。在gcc中编译得很好。 - Lundin
ISO C 中没有“编译时”和“运行时”的区别。我们正在讨论的代码是 strlen(s) + 1,它不可能计算为零。 - M.M
1
@MattMcNabb 当然有,这些术语只是被称为一些花哨的东西:常量表达式与非常量表达式。正如C标准6.6所说:“常量表达式可以在编译期间而不是运行时进行评估,并且因此可以在任何可以使用常量的地方使用。”至于我的例子,它是一个通用的例子,旨在说明为什么编译时评估更可取。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接