现在,我正在研究Yale course。
其中一个片段是:
char *
strdup(const char *s)
{
char *s2;
s2 = malloc(strlen(s)+1);
if(s2 != 0) {
strcpy(s2, s);
}
return s2;
}
如果我理解正确,我们需要malloc()
,因为我们将字符串s
复制到另一个字符串s2
中,因此我们需要为s2
分配足够的空间。但是,当我尝试以下代码时:
#include <stdio.h>
int main(void) {
// your code goes here
char *str = "Test";
printf("%s", str);
return 0;
}
here,它为我提供了正确和预期的输出。
所以,我有两个问题:
第一个代码段中是否需要malloc()
,因为我们需要s2
作为数组?
我是否在第二个片段中调用UB,(因为我没有声明*str
指向数组的地方)?我们需要一个数组,因为在C中,字符串存储为字符数组。
答案 0 :(得分:2)
是的,需要复制字符串。 (你无关紧要的原因)你正在分配内存来保存重复的字符串。在第二种情况下,您只是通过将指针传递给printf
来打印文字,您不会复制任何内容。
这里没有UB - 这是合法代码。 char* str
正在将char*
初始化为指向文字数组的衰减指针。衰减的指针指向数组的第一个元素。
确实,字符串在C中存储为char数组 - 但它如何影响我们在第二个片段中看到的行为?我们正在传递一个char*
,它指向空终止的char数组,正好是%s
的{{1}}说明符所期望的。
文字也有静态存储时间。您不需要创建用于存储它们的内存。当您编写printf
时,这是隐式完成的。
答案 1 :(得分:1)
此代码:
char *str = "Test";
表示:
创建一个包含T,e,s,t和空字符的五个字符的数组。该阵列的存储是“静态的”;它是在程序的整个生命周期内分配的。
创建指向名为char
的{{1}}的指针,并将其初始值设置为上面创建的数组中第一个字符的地址。
在这种情况下,str
的存储空间会自动分配给您,作为编译,加载和执行程序的一部分。
相反,在"Test"
代码中,函数传递指向字符串的指针。 (字符串是一个字符数组,其字符串用于标记字符串的结尾。)在这种情况下,函数事先不知道字符串的长度。因此,在调用函数之前,它不知道需要多少内存,它使用strdup
来测量字符串的长度。
如果您事先不知道需要多少内存,通常需要使用strlen
或其中一个相关的内存分配例程来分配存储空间。 (另一种方法是使用可变长度数组。并非所有C实现都支持可变长度数组,并且它们只应用于仅在一个函数中需要的小型或中等大小的数组。)
答案 2 :(得分:0)
我没有声明* str指向数组
你错了。字符串文字存储为具有静态存储持续时间的字符数组。
所以在这个宣言中
char *str = "Test";
发生了两件事。
第一个是编译器创建一个类型为char[5]
的字符数组,其中存储了字符串文字。然后,数组的第一个字符的地址用于初始化具有自动存储持续时间的指针str
。
您甚至可以通过以下方式重写声明
char *str = &"Test"[0];
第一个片段中是否需要malloc()因为我们需要s2才能成为 阵列
函数strdup
创建原始字符串的副本。所以你必须存储创建副本的某个地方。要做到这一点,你必须分配足够的内存来存储副本,并从函数返回一个指针,该指针将指向带有字符串副本的已分配内存。