gpt4 book ai didi

c - 在&#f;和printf&格式说明符中的用法

转载 作者:行者123 更新时间:2023-12-03 04:18:58 24 4
gpt4 key购买 nike

#include <stdio.h>
#include <stdlib.h>

int main() {
char a;
printf("What? \t");

scanf("%s", &a);

printf("U have to %s", a);
return 0;
}


每当我构建并运行此代码并在 %s中输入值时,都会出现错误,调试程序将停止工作并关闭。但是当我像这样使用&符号时:

#include <stdio.h>
#include <stdlib.h>

int main() {
char a;
printf("What? \t");

scanf("%s", &a);

printf("U have to %s", &a);
return 0;
}


printf ...中它起作用。这是为什么?格式说明符之间也有所不同,例如,当一个人在 &中使用 printf%c时,不需要在 %d中放入 scanf(与号)符号。为什么会发生这种情况,并且它与数据类型有关,并且哪种格式说明符可以得出此结果?

(对不起,我的英语不好。我不是英语母语人士,这是我第一次来)。

最佳答案

您在这里拥有的是一个经典的代码示例,该代码似乎可以工作,但是原因不正确。

让我们回顾一下有关printfscanf的一些信息。格式说明符%d用于类型int的值。您可以读取这样的整数:

int i;
scanf("%d", &i);


您可以像这样将其打印出来:

printf("%d\n", i);


为什么一个人使用 &而一个人不使用?好吧,C使用了所谓的“按值传递”。如果我们写

scanf("%d", i);    /* WRONG */


我们会将 i的值传递给 scanf。但是我们不想将 i的(旧)值传递给 scanf,我们希望 scanf读取一个新值,并将其存储到 i中。换句话说,实际上,我们希望 scanfi的新值传回给我们。为此,我们改为传递 scanf指向变量 i的指针,在该变量中我们希望它存储刚读取的整数。这就是 &的作用-它生成指向 i的指针。

另一方面,当我们调用 printf时,常规的参数传递方式就可以了。我们确实希望将 i的值传递给 printf,以便它可以将其打印出来。如果我们打电话给

printf("%d\n", &i);    /* WRONG */


这是行不通的,因为 printf需要一个 int,在这里我们错误地将其传递给 int指针。

因此,现在我们了解到,对于具有 %d的整数, printf需要一个 int,而 scanf需要一个指向 int的指针。

让我们谈谈角色。格式 %c用于字符。我们可以用 scanf读取一个字符:

char c;
scanf("%c", &c);


我们可以用 printf打印它:

printf("%c\n", c);


同样,模式完全相同。 scanf需要一个指针,以便它可以填写值,因此我们传递 &c。但是 printf只需要该值,因此我们传递普通的 c

现在我们来看看字符串。 C中的字符串是字符数组。同样,C语言中的字符串始终以特殊的空字符 '\0'终止,该空字符标记字符串的结尾。因此,如果我们想声明一个变量,它可以包含最长9个字符的字符串,则可以编写

char s[10];


这给我们留出了9个字符的空间,再加上终止的 '\0'

但是数组在C语言中是特殊的:无论何时将数组传递给函数,或者何时执行任何需要数组“值”的操作,获取的内容(编译器自动为您生成的内容)都指向该指针。数组的第一个元素。

这意味着要使用 scanf%s读取字符串,我们可以调用:

scanf("%s", s);


您问:“但是 &在哪里?” “我认为您在呼叫 &时总是需要一个 scanf!”

好吧,不完全是。调用 scanf时始终需要一个指针。实际上,当您调用 scanf("%s", s)时,就好像您已经编写了

scanf("%s", &s[0]);


当将 %sscanf一起使用时,它期望指向多个字符中第一个字符的指针,即指向字符数组开头的指针,并应在此处开始写入读取的字符串。 (它怎么知道数组有多大?如果用户键入的字符串太长而无法放入数组中,该怎么办?我们稍后会谈到这些点。)

当然,您也可以使用 %s打印字符串,它看起来像这样:

printf("%s\n", s);


同样,就像您写过一样

printf("%s\n", &s[0]);


当将 %sprintf一起使用时,它会期望一个指向应该开始打印的几个字符中的第一个的指针,直到找到终止的 '\0'字符为止。

所以 %s对于 printfscanf是特殊的,因为字符串是特殊的(因为数组是特殊的)。使用 %d%c以及几乎所有其他格式说明符,在调用 &时通常需要一个 scanf,而在调用 &时通常不希望该 printf。但是对于 %s,通常不希望 &printf使用 scanf

(而且,如果我们仔细考虑一下,则例外不是那么多, scanf%s不需要 &。请记住,规则确实是 scanf始终需要指针。唯一的 scanf%s不需要 &的原因是,当您传递数组时,会自动获得一个指向数组第一个元素的指针,因此 printf%s的确是例外:< cc>和 printf确实希望有一个指针,而 %sprintf设计为希望有一个指针的原因是,没有办法不给它一个:它必须接受一个指针,因为对于字符串,那是你总是给它的东西。)

因此,使用 %s的规则是 %s期望指向多个字符中第一个字符的指针,而 scanf也期望指向多个字符中的第一个字符的指针。

因此,现在,有了所有这些背景知识,我们就可以查看您的代码。你基本上写了

char c;
scanf("%s", &c);


乍一看,这似乎有点,几乎是正确的。 printfscanf需要一个指向字符的指针,而您给了它 %s,它是一个指向字符的指针。但是 &c确实想要一个指向几个字符中第一个字符的指针。但是您给它提供了一个指向单个字符的指针。因此,当用户键入字符串时,键入的第一个字符将存储在 %s中,而其余字符以及终止的 c将被写入未分配的内存中,该内存位于变量 '\0'的右侧。 。他们将覆盖(“智能”)可能用于其他用途的内存。这是一个严重的问题,但可能不会立即显现出来。

最后,您尝试使用 c重新打印出来。你第一次尝试

printf("%s\n", c);    /* WRONG */


但这根本没有用。原因是带有 printf%s需要一个指向 printf的指针,但是您给它一个普通的 char。假设 char包含字母 c。这最终将要求 'A'转到地址65并开始打印字符,直到找到终止的 printf。为什么要地址65?因为65是 '\0'的ASCII码。但是在内存中可能没有一个正确的,以零结尾的字符串,它从地址65开始。实际上,您的程序很有可能根本没有权限读取地址65。

所以你尝试了

printf("%s\n", &c);    /* ALSO WRONG */


这似乎可行。之所以“起作用”是因为,如果scanf成功将完整的字符串存储到 A中,并且未分配的内存位于它的右侧,并且如果以某种方式破坏了该内存没有导致(太多)其他问题,那么当您通过将指针 c指向 &c,printf可以找到这些字符,组成一个字符串,然后将它们打印出来。

因此它“起作用”,但是如我所说,出于错误的原因:在此过程中,它遍及了整个内存,因此它不是“拥有”的,迟早其他事情将不起作用。

您应该如何扫描和打印字符串?如前所述,一种方法是这样的:

char s[10];
scanf("%s", s);

printf("%s\n", s);


现在,当 printf获得指向数组 scanf的第一个元素的指针时,它将具有10个字符。

我们确实确实要担心用户键入超过9个字符的可能性。但是有一个解决方法:我们可以告诉 s允许读取一个字符串多长时间,允许将多少个字符写入我们处理过的数组:

scanf("%9s", s);


其中的 scanf告诉 9不允许用户读取超过9个字符。而且由于9小于10,因此仍有终止 scanf字符的空间。

关于 '\0'还有更多可以说的。正如chqrlie在评论中指出的那样,检查其返回值很重要,以确保它成功转换了所需的任意数量的值。关于空格有一些奇怪的规则。除非您知道自己在做什么,否则就无法将对 scanf的调用与对诸如 scanfgetchar之类的其他输入读取功能的调用混合在一起-您会得到奇怪的结果。最后,scanf如此顽强,(最后)如此缺乏真正有用的功能,因此根本不值得使用。但是这些是第二天的话题,因为这个答案已经是 tl;dr了。

关于c - 在&#f;和printf&格式说明符中的用法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50074855/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com