所以我对编程很新,大约3天前我开始使用C语言。 我正在创建这个简短的程序来测试自己。这只是一个非常基本的C程序,要求你的名字和姓氏。这是代码:
#include <stdio.h>
int main()
{
char first[20];
char last[20];
printf("Please enter your first name:");
scanf("%s",&first);
printf("\nand Please enter your last name:");
scanf("%s",&last);
printf("Greetings fellow %s %s!\n",first,last);
return(0);
}
但是当我去编译时,我每次都会得到这个错误:
checkname.c:9: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
checkname.c:11: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
我知道我可以使用gets()
,但显然它很糟糕,我不应该使用它。这段代码有什么问题?我不明白问题是什么。
答案 0 :(得分:7)
我知道我可以使用
gets()
,但显然它很糟糕,我不应该使用它。
确实,确切地说 - 它容易出现缓冲区溢出错误。并且scanf()
同样是邪恶的(部分是出于同样的原因 - 指定缓冲区长度并不容易,而且无论如何,它都比必要的更复杂,很难做到正确,而且对于简单的原始I来说它是多么昂贵/ O),也不要使用它。使用fgets()
来获取用户输入,通过让你传递要写入的缓冲区的长度,它可以避免被击中:
char buf[0x40];
fgets(buf, sizeof(buf), stdin);
既然您知道“如何”,我会向您解释“为什么”。
当传递给函数时,数组被称为衰减成指针。如果你将char []
传递给函数,它会看到char *
指向到数组的第一个元素。这就是为什么你不必为(char)数组(概念上是C中的字符串)显式使用“addressof”(&
)运算符。
如果你使用它,你不会得到指向数组的第一个元素的指针,而是指向指向数组本身的指针,那不是你的意思想。
(一般情况下,它可以工作,因为它显然适合你,但理论上它会调用undefined behavior,因为传入的表达式的实际类型和%s
转换说明符所期望的类型不匹配。)
This document是关于数组和指针的好读物,请务必阅读并理解它。
答案 1 :(得分:2)
您正在使用数组。在输入数组
时,不要将&
与scanf
一起使用
例如:scanf("%s",first);
数组已经通过引用传递。您不需要放置&
,因为数组已作为指向C
答案 2 :(得分:1)
尝试将scanf("%s",&first);
更改为scanf("%s",first);
(last
的相似度)。
在正常情况下(包括这些),数组的名称将计算为数组中第一个元素的地址。在这种情况下,这是一个指向char的指针,它是scanf
转换所需的%s
类型。
在开头的&
,您获得相同的指针值(即,相同的地址),但作为指向数组的指针而不是指向元素的指针数组。由于这与%s
所期望的类型不匹配,结果是未定义的行为(尽管它是正确的地址,这通常适用于大多数典型的编译器/ CPU)。
答案 3 :(得分:1)
删除&amp;来自scanf
陈述。
scanf("%s", first); // first is pointer to start of first
scanf("%s", last); // last is pointer to start of last
在C中,数组 的名称是指向数组开头的指针 。
格式参数scanf
需要指针参数或参数。
scanf( "%s", first );
scanf format-arg input-args
例如,如果整数参数用于scanf
,则&amp; (需要地址)运营商是必需的。
int n;
scanf("%d", &n); // &n is pointer to n
答案 4 :(得分:0)
scanf需要一个地址,但是char数组[]已经是一个地址,而scanf不需要参考符号&amp;因为名称本身就是一个地址