在C中,为什么%s不给它赋值就工作?
根据我的知识和一些线程,如果要在C中打印字符串,必须执行以下操作:在C中,为什么%s不给它赋值就工作?,c,C,根据我的知识和一些线程,如果要在C中打印字符串,必须执行以下操作: printf("%s some text", value); r1="%s"; r2=password; call scanf; r1="%s is your password"; call printf; 并且将显示该值,而不是%s 我写了这段代码: char password[] = "default"; printf("Enter name: \n"); scanf("%s", password); printf("
printf("%s some text", value);
r1="%s";
r2=password;
call scanf;
r1="%s is your password";
call printf;
并且将显示该值,而不是%s
我写了这段代码:
char password[] = "default";
printf("Enter name: \n");
scanf("%s", password);
printf("%s is your password", password); // All good - the print is as expected
scanf("%s", password);
printf("%s is your password",password);
但我注意到,我可以在没有价值部分的情况下做完全相同的事情,而且它仍然有效:
printf("%s is your password");
所以我的问题是为什么
%s
占位符在我不给它赋值的情况下得到一个值,以及它如何知道应该给它什么值?这是未定义的行为,任何事情都可能发生,包括看起来正确的事情。但是它是不正确的。
如果使用正确的选项,编译器可能会告诉您问题所在
标准说(强调是我的):
7.21.6.1fprintf功能
printf()。(技术上称为‘可变函数’——我简称‘varargs’。)
在C中调用函数时,函数的参数被推送到堆栈(*)上,但是varargs特性的设计无法让被调用函数知道传入了多少参数
当执行printf()
函数时,它会扫描格式字符串,%s会告诉它在变量参数列表的下一个位置查找字符串。因为列表中没有更多的参数,所以代码“从数组的末尾走出来”,并获取它在内存中看到的下一个内容。我怀疑发生的情况是,内存中的下一个位置仍然有您上次调用scanf
时的password
地址,而且由于该地址指向一个字符串,而您告诉printf
打印一个字符串,所以您很幸运,它成功了
尝试将另一个函数调用(例如:printf(“%s%s%s\n”、“X”、“Y”、“Z”)
放在对scanf(“%s”,password);
和printf(“%s是您的密码”);
的调用之间,您几乎肯定会看到不同的行为
免费建议:C有很多尖角和未定义的位,但是一个好的编译器(以及静态分析或“lint”工具)可以警告您许多常见错误。如果您要在C中工作,请学习如何将编译器警告最大化,了解所有错误和警告的含义(当它们发生时,而不是一次全部!)并强迫自己编写编译时没有任何警告的C代码。这将为您节省大量不必要的麻烦
(*)为了简单起见在这里进行概括-有时参数可以在寄存器中传递,有时事情是内联的,诸如此类。所以,有很多帖子告诉你不应该执行printf(“s是你的密码”)
,你只是幸运而已。我想从你的问题中你多少知道这一点。但是很少有人告诉你你幸运的可能原因
为了了解可能发生的情况,我们必须了解函数参数是如何传递的。函数调用方必须将参数放在约定的位置,函数才能找到参数。因此,对于参数1…N,我们称这些位置为r1
…rN
。(这种协议是我们称之为“函数调用约定”的一部分)
这意味着该代码:
char password[] = "default";
printf("Enter name: \n");
scanf("%s", password);
printf("%s is your password", password); // All good - the print is as expected
scanf("%s", password);
printf("%s is your password",password);
可由编译器转换为此伪代码
r1="%s";
r2=password;
call scanf;
r1="%s is your password";
r2=password;
call printf;
如果现在从printf
调用中删除第二个参数,则伪代码如下所示:
printf("%s some text", value);
r1="%s";
r2=password;
call scanf;
r1="%s is your password";
call printf;
请注意,call scanf;
之后,r2
可能未被修改,仍然设置为password
,因此call printf;
有效
您可能认为您已经发现了一种优化代码的新方法,即消除了r2=password;
赋值中的一种。这可能适用于旧的“哑”编译器,但不适用于现代编译器
现代编译器已经在安全的情况下这样做了。而且并不总是安全的。它不安全的原因可能是scanf
和printf
有不同的调用约定,r2
可能在背后被修改过,等等
为了更好地了解编译器正在做什么,我建议在不同的优化级别上查看编译器的汇编输出
而且,请始终使用-Wall
进行编译。编译器通常擅长于在你做蠢事时告诉你。在我看来似乎只是一个编译器问题,似乎指向垃圾。如果格式说明符说格式字符串后面有一个参数,而没有su,那么可能的行为重复是未定义的ch参数。一种可能的结果可能是以您认为有意义的方式运行。它是靠运气工作的。由于您没有提供所需的参数,因此它会拾取碰巧存在的任何偏离值。在这种情况下,它很可能拾取上一次调用中的password
参数。但您不能依赖于此。a至于为什么这可能恰好显示了password
的正确值:函数scanf
和printf
都使用一个常量字符串和password
作为参数。因此,如果scanf
中的password
仍在printf
期望其第二个参数的空间中,printf
将发生在f上