我的编译器(clang)显示以下消息:
11:17:warning: format specifies type 'char *' but the argument has type 'char (*)[0]' [-Wformat] scanf("%s", &name); ~~ ^~~~~ 1 warning generated.
来自以下代码(问候程序):
/*
* Program: gretting2.c
* Utility: Display a greeting with your name.
* Author: Adrián Garro.
*/
#include <stdio.h>
int main () {
char name[0];
printf("-------------------\n");
printf("Write your name: \n");
printf("-------------------\n");
scanf("%s", &name);
printf("------------------------------------\n");
printf("Hello %s, nice to meet you\n",name);
printf("------------------------------------\n");
}
实际发生了什么,我该如何解决?
答案 0 :(得分:4)
为了理解这一点,您必须了解scanf
正在做什么。在这种情况下,scanf
是从stdin读取一个字符串,并将其放入您提供的缓冲区中。 它不会为您分配该空间,也不会检测溢出。您需要为字符串分配足够的空间。就像现在一样,你正在为你的字符串分配零空间,所以一切都是溢出的。这是一个主要的错误。
代替char[0]
,你做了char[40]
,正如另一位用户建议的那样。如果你的程序用户写了超过40个字符怎么办?这导致未定义的行为。从本质上讲,它会写入你不希望它写入的内存。它可能会导致段错误,可能导致重要内存被覆盖,或者可能会发生。这是scanf.
的弱点。查看fgets.
您告诉它缓冲区的大小,输入将被截断以适应。
当然,这与你的警告无关。您收到警告是因为引用数组的名称与引用指向其第一个元素的指针(即name <==> &(name[0])
)相同。把指针指向这就像是指针指针,即&name <==> &&(name[0])
。由于scanf
正在寻找char*
类型的参数,并且它正在获取指向该参数的指针,因此类型检查器会抱怨。
答案 1 :(得分:3)
只需改变一下:
scanf("%s", &name);
为:
scanf("%39s", name);
和此:
char name[0];
为:
char name[40];
此外,您还必须以&#39; \ 0&#39;用:
name[39] = '\0';
答案 2 :(得分:3)
您的代码显示“未定义的行为”。这意味着一切都可能发生。任何东西。
您正在将零长度数组传递给scanf()
。此外,您没有在格式字符串中传递数组长度。这会导致缓冲区溢出漏洞(总是在零长度目标阵列的情况下)。
你需要这样的东西:
char name[51];
scanf("%50s", name);
注意%50s
现在指定目标数组的大小(少一个,为空终止符留出空间!),这可以避免缓冲区溢出。您仍然需要检查scanf()
的返回值,以及输入name
实际上是否太长(您不希望在不告诉用户的情况下截断用户的输入)。
如果您使用的是Linux,请查看名为valgrind
的工具。它是一个运行时内存错误检测器(除其他外),有时可以为你捕获这样的错误(而不是那么明显的错误,这是主要的观点)。它对许多C程序员来说都是不可或缺的。
答案 3 :(得分:1)
根据您希望的强大程度,您需要重新考虑这种方法。我想首先要知道你在声明char name[ 0 ]
时是否理解了你正在使用的类型。这是一个零大小的&#39;字节大小的字符数组。这是一个令人困惑的事情,如果编译器的行为不同,我就不会感到惊讶......
编译器抱怨的实际警告是类型不匹配。如果您获取数组中第一个字符的地址,您可以摆脱它(即在&( name[ 0 ] )
调用中使用scanf
。 name
的地址是它在堆栈上的位置 - 恰好是数组实现使用相同的位置来存储数组数据,并且编译器在单独使用时对name
的处理方式不同以便数组的地址与其第一个元素的地址相同......
使用char name[ 0 ]
会导致内存损坏,因为无法读取字符串,实现细节可能会运气不好并允许其工作。解决此问题的一种简单方法是将0替换为有意义的数字,并将其取为输入字符串的最大长度。说32,这样你就可以char name[ 32 ]
...但是,这并不能处理更长的字符串。
由于我们生活在一个拥有大量内存和大堆栈的世界中,你可以做char name[ 4096 ]
并使用4KB内存作为缓冲区,这对于实际使用来说绝对没问题。
现在......如果你想成为一个小肛门并处理病态病例,就像用户靠着一些按键睡觉几个小时,然后按下输入并添加一些巨大的8000字符长的字符串,有几种方法来处理动态内存分配&#39;也可能超出了这个答案的范围。
顺便说一句,根据我的理解char foo[ 0 ]
故意有效 - 它可能起源于黑客并且具有令人困惑的类型,但是并不常见地依赖于旧技巧来创建如上所述的可变大小的结构in this page from the GCC online docs
答案 4 :(得分:1)
2.scanf(&#34;%s&#34;,&amp; name); ----&gt; scanf(&#34;%s&#34;,名称); / * scanf将char *作为参数,因此您只需要传递字符串名称。 * /
我不认为scanf(&#34;%(长度 - 1)s&#34;,名称);需要。 因为%s用于读取字符串。这将在到达的第一个空白字符或指定的字段宽度(例如&#34;%39s&#34;)上停止,以先到者为准。
除了这些不经常使用。当然,您可以随意使用它们!
/
*
* Program: gretting2.c
* Utility: Display a greeting with your name.
* Author: Adrián Garro.
*/
#include <stdio.h>
int main () {
char name[100];
printf("-------------------\n");
printf("Write your name: \n");
printf("-------------------\n");
scanf("%s", name);
printf("------------------------------------\n");
printf("Hello %s, nice to meet you\n",name);
printf("------------------------------------\n");
}
答案 5 :(得分:0)
因为正确的方法是
scanf("%s", name);
/* ^ no ampersand
什么是
char name[0];
您应指定非零长度并将其用于scanf
长度说明符
scanf("%(length - 1)s", name);
/* ^ sunstitite with the value */
答案 6 :(得分:0)
OP发布的代码有几个问题
以下修复了大部分内容
我包括评论以指出问题所在
int main() {
//char name[0]; // this did not allow any room for the name
char name[100] = {'\0'}; // declare a 100 byte buffer and init to all '\0'
printf("-------------------\n");
printf("Write your name:, max 99 char \n"); // 99 allows room for nul termination byte
printf("-------------------\n");
//scanf("%s", &name); // this has no limit on length of input string so can overrun buffer
if( 1 == scanf("%99s", name) ) // 1) always check returned value from I/O functions
// 2) no '&' before 'name' because
// arrays degrade to pointer to array when variable
// name is used
// 3) placed max size limit on format conversion string
// so input buffer 'name' cannot be overflowed
{ // then scanf failed
perror( "scanf failed for name" );
return(-1); // indicate error
}
// implied else, scanf successful
printf("------------------------------------\n");
printf("Hello %s, nice to meet you\n",name);
printf("------------------------------------\n");
return(0); // indicate success
} //结束函数:main
答案 7 :(得分:-1)
您正在阅读“字符串”,因此正确的方法是:
scanf("%s", name);
为什么编译器会抱怨?在scanf
中提供参数时,可以提供变量的内存位置。例如:
int x;
scanf("%d", &x);
&x
是int *
,即指向整数的指针,因此x
将获得正确的值。
当你读一个字符串时,你实际上是在一起阅读许多 char
个变量。要存储它们,您需要一个char *
数组;好吧,name
本身就是char *
,所以不需要写&name
。后者是char **
,即char
的二维数组。
顺便说一句,您还需要为要读取的字符分配空间。因此,您必须编写char name[20]
(或任何其他数字)。您还需要在return 0;
中提供int main()
。