在C中,为什么%s不给它赋值就工作?

在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("

根据我的知识和一些线程,如果要在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("%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功能

  • fprintf函数将输出写入流所指向的流, 由指定如何使用的格式指向的字符串控制 随后的参数将转换为输出如果有 格式参数不足,行为未定义。如果 当参数保留时,格式已用尽,多余的参数 被评估(一如既往),但在其他方面被忽略。fprintf 函数在遇到格式字符串结尾时返回
  • 函数的
    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上