在c中声明变长字符串-显然是动态的?

在c中声明变长字符串-显然是动态的?,c,C,我试图了解一些关于“字符串”如何在c中定义的基本知识 char s[2]; scanf("%s", s); printf("%s", s); printf("sizeof s %d", sizeof(s)); 我不是c程序员。我知道使用scanf获取用户输入有问题,我甚至没有检查它的返回值等。这只是为了了解一些关于声明字符串的基本知识 给定上面的代码,如果我输入say'helloworld'是print'helloworld',它会做什么。好的但是我认为通过说char s[2],我是说类似于“

我试图了解一些关于“字符串”如何在c中定义的基本知识

char s[2];
scanf("%s", s);
printf("%s", s);
printf("sizeof s %d", sizeof(s));
我不是c程序员。我知道使用scanf获取用户输入有问题,我甚至没有检查它的返回值等。这只是为了了解一些关于声明字符串的基本知识

给定上面的代码,如果我输入say'helloworld'是print'helloworld',它会做什么。好的但是我认为通过说char s[2],我是说类似于“s是一个长度为2的数组,其中每个元素的类型都是char”

因此,我希望看到“他”被印刷出来。不是“helloworld”。因为我的s数组只能容纳2个字符

sizeof仍然返回2。但是看起来我的数组已经扩展到用户输入的大小了


发生了什么事?

这说明了
scanf
如此危险的原因。您已经覆盖了其他不适用于此的内存。读取等长字符串的一种更安全的方法是执行以下操作

char a[2];
fgets(a, sizeof(a), stdin);
printf("%s\n", a);
如果执行此操作并键入
helloworld
,则只能将
h
发送到stdout(因为字符串由于空终止符而具有
sizeof(a)-1
字符。这意味着数组
a
实际上是
{h','\0'}
fgets
读取恒定大小的字符串要比scanf安全得多两个问题:

  • C对数组访问不做任何边界检查
  • 在大多数情况下,数组类型的表达式将转换为指针类型的表达式,表达式的值将是数组第一个元素的地址。这包括作为参数传递给函数的数组表达式
%s
转换规范告诉
printf
打印从指定地址开始的字符序列,直到在字符串末尾看到0终止符为止。类似地,它告诉
scanf
存储从指定地址开始的字符序列,直到看到空白字符或EOF为止

当您将表达式
s
作为参数传递给
scanf
printf
时,表达式将从类型“char的2元素数组”转换为“指向
char
的指针”,表达式的值是数组第一个元素的地址(这相当于传递表达式
&s[0]

所有
scanf
接收的都是一个指针值-它不知道从该地址开始的数组有多大。因此它不知道
s
的大小只足以包含两个字符。相反,它会愉快地将这些额外的字符写入数组的末尾。类似地,
printf
也不知道数组已打开只有2个字符宽-它会一直打印,直到看到0终止符

可以指定字段宽度作为转换的一部分:

scanf( "%1s", s ); 

这将从标准输入读取最多1个字符,并将其存储到
s
。请记住,字符串是一个字符序列,后跟一个0终止符,因此要存储一个N字符字符串,您需要留出一个N+1元素数组来存储它。

我认为还没有人真正回答过这个问题莱姆在这里

首先,数组“s”只有2字节长,因此任何超过1个字符的字符串的scanf都将生成不希望的结果

char s[255]; // was char s[2];
此外,如前所述,scanf是一种可怕的字符串输入方式。请改用fgets

其次,您不能使用sizeof获取字符串的长度。它将返回包含字符串的数组的长度(相信我,这不是您想要的)。相反,请使用strlen


它没有增长。您为
s
分配了2个字符,这就是堆栈上为它预留的内存量。但是,
scanf()
只接收一个指向内存的指针。它无法知道分配了多少内存,所以它只能读取和存储、写入可能为其他内容设置的内存,直到完成。内存管理部分由您来完成。这些类型的错误是许多“什么…”的根源调试时刻,因为它通常看起来像其他东西被破坏了。为什么他在扫描中没有&s?您正在覆盖数组的边界,从而导致未定义的行为。您的数组只有2个
char
s是正确的。您只是幸运地发现,在2个
char
s之外写入不会破坏某些内容e、 在另一台计算机上或使用不同的编译器选项时,您可能不会这么幸运。此外,
c
字符串以NULL(
\0
)结尾,因此您需要在数组中添加一个额外的字符。“helloworld”是10
char
s长,这意味着您需要一个
char
array 11
char
s长才能正确存储它。不,数组没有增长。通过输入长文本,您已经覆盖了堆栈的一部分(或数组分配的任何内存)在阵列外部。当堆栈内存发生这种情况时,通常会导致崩溃或执行不需要的代码,特别是当代码是远程访问服务的一部分时。您已经暴露了
scanf
中的缺陷,即它不是内存安全的。如果您在此处使用
fgets
,您将得到
h
(不要忘记空终止符)。详细信息:代码可以使用sizeof获取字符串的大小。strlen(p)使用指针p,指针指向字符串并返回字符串的长度。在C中,字符串是一个数组。回答很好。详细信息:“C不会对数组访问执行任何边界检查-->它只是UB,编译器可以进行边界检查。边界检查肯定不是C行为指定的。
fgets(s, sizeof(s), stdin); // was scanf("%s", s);
printf("strlen s %d", strlen(s)); // was printf("sizeof s %d", sizeof(s));