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

时间:2016-10-26 20:23:25

标签: c

我正在尝试理解关于字符串'的基本内容。在c中定义。

char s[2];
scanf("%s", s);
printf("%s", s);
printf("sizeof s %d", sizeof(s));

我不是c程序员。我知道使用scanf来获取用户输入存在问题,而且我甚至没有检查其返回值等。这只是为了解一些关于声明字符串的基本知识。

鉴于以上代码,如果我输入“helloworld'印刷' helloworld'。好。但是我想通过说char [2]我说的是类似于长度为2的数组,其中每个元素都是char'。

因此我期待看到他'打印。不是' helloworld'。因为我的阵列只有2个字符的空间。

sizeof仍然返回2.但看起来我的数组已经增长到用户输入的大小。

发生了什么事?

3 个答案:

答案 0 :(得分:4)

这显示了为什么scanf如此危险的完美原因。您已经覆盖了其他不适用于此的内存。读取常量长度字符串的更安全的方法是执行类似这样的操作

char a[2];
fgets(a, sizeof(a), stdin);
printf("%s\n", a);

如果你这样做并且输入了helloworld,那么你只能得到h到stdout(因为字符串由于空终止符而有sizeof(a) - 1个字符。这意味着数组{{1}实际上是a)。 {'h', '\0'}对于阅读常量大小的字符串比fgets更安全。

答案 1 :(得分:3)

两个问题:

  • C不对数组访问进行任何边界检查;
  • 在大多数情况下,数组类型的表达式将转换为指针类型的表达式,表达式的值将是数组的第一个元素的地址。这包括作为参数传递给函数的数组表达式。

%s转换规范告诉printf打印从指定地址开始的字符的序列,直到它在字符串末尾看到0终结符。类似地,它告诉scanf存储从指定地址开始的字符序列,直到它看到空白字符或EOF。

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

所有scanf接收都是指针值 - 它不知道从该地址开始的数组有多大。所以它不知道s只有大到足以包含两个字符。相反,它愉快地将那些额外的字符写在数组的末尾。同样地,printf不知道数组只有2个字符宽 - 它只是保持打印直到它看到0终止符。

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

scanf( "%1s", s ); 

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

答案 2 :(得分:0)

我认为还没有人真正回答过这个问题。这里有两个问题......

首先,阵列" s"只有2个字节长,因此超过1个字符的任何字符串的 scanf 将产生不希望的结果。

fgets(s, sizeof(s), stdin); // was scanf("%s", s);

另外,如前所述, scanf 是一种输入字符串的可怕方式。请改用 fgets

printf("strlen s %d", strlen(s)); // was printf("sizeof s %d", sizeof(s));

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

protocol MyProtocol {}

struct MyAssociatedValue: MyProtocol {}

enum MyEnum {
    case myCase(MyAssociatedValue)
}

func myEnumClosureMapping() -> (MyAssociatedValue) -> MyEnum {
    return MyEnum.myCase
}