为什么使用scanf读取字符串缓冲区时,有些情况下需要加上&符号,有些情况下又不需要呢?

169

我对某件事有些困惑。我原本以为使用scanf()读取C字符串的正确方法是:

(不用担心可能的缓冲区溢出,这只是一个简单的例子)

char string[256];
scanf( "%s" , string );

然而,以下方法似乎也可以起作用:

scanf( "%s" , &string );

这是仅限于我的编译器(gcc),纯属运气,还是其他原因?


在第二种情况下,实际上根本不存在可能的缓冲区溢出,因为您根本没有使用该缓冲区。或者,您可以说任何大于3个字符的字符串都会溢出您的“缓冲区”。 - T.E.D.
我是在提到第一个例子。而且,其他人已经指出了这里发生的事情。 - abeln
是的。我试了一下,Gareth是对的。很奇怪。 - T.E.D.
由于这个问题是通过搜索“如何使用scanf读取字符串”返回的,因此指出这个问题是关于如何将指向缓冲区的指针传递给scanf的方法,并且问题和接受的答案都专注于此,并省略了在实际代码中应该使用的最大输入长度的_至关重要_限制(但对于这个问题来说不是重点)。 - Arkku
2个回答

164
一个数组"衰变"为一个指向其第一个元素的指针,因此scanf("%s", string)等同于scanf("%s", &string[0])。另一方面,scanf("%s", &string)传递了一个char [256]的指针,但它指向的是同一个位置。

然后scanf在处理其参数列表尾部时,将尝试提取char *。当你传递string&string[0]时,这是正确的,但当你传递&string时,你依赖于语言标准没有保证的东西,即指向不同类型和大小的对象,但从相同的位置开始的指针&string&string[0]被表示为相同的方式。
我不认为我曾经遇到过这种情况,实际上你可能是安全的。尽管如此,这是错误的,在某些平台上可能会失败。(假设的例子:包括每个指针的类型信息的“调试”实现。我认为Symbolics“Lisp机器”的C实现就做了类似的事情。)

-12

我认为以下内容是准确的,它可能会有所帮助。如果您发现任何错误,请随意更正。我对C语言还很陌生。

char str[]  

1. 具有自己在内存中地址的 char 类型值数组 2. 具有自己在内存中地址,并且具有与数组元素数量相同的连续地址的 char 类型值数组 3. 包括终止空字符 '\0' 的 &str、&str[0] 和 str,三者表示内存中相同的位置,即数组 str 的第一个元素的地址 char *strPtr = &str[0]; // 声明和初始化
char *strPtr; strPtr = &str[0];
  1. strPtr是指向char的指针
  2. strPtr指向数组str
  3. strPtr是一个具有自己内存地址的变量
  4. strPtr是一个存储&str[0]地址值的变量
  5. strPtr自身在内存中的地址与它所存储的内存地址(即数组在内存中的地址,也就是&str[0])不同
  6. &strPtr表示strPtr本身在内存中的地址

我认为您可以声明一个指向指针的指针:

char **vPtr = &strPtr;  

声明并初始化为strPtr指针的地址

或者你可以分成两个部分:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr 指向 strPtr 指针
  2. *vPtr 是一个具有自己内存地址的变量
  3. *vPtr 是一个存储地址和 &strPtr 值的变量
  4. 最后的注释:你不能执行 str++,因为 str 的地址是一个常量,但是你可以执行 strPtr++

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