在printf和格式说明符中使用&符号(&)

时间:2018-04-28 09:14:20

标签: c

#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(&符号)。 1}}。为什么会发生这种情况,是否与数据类型有关,哪种格式说明符会得出结果呢?

(抱歉我的英语不好。我不是母语为英语的人,这是我第一次来这里)。

3 个答案:

答案 0 :(得分:5)

这里有一个典型的代码示例似乎有效,但出于错误的原因。

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

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

你可以像这样打印出来:

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

为什么一个使用&而一个不使用?好吧,C使用的是&#34;传递的值&#34;。如果我们写了

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

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

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

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

它不会起作用,因为printf需要int,而我们在这里错误地将指针传递给int

所以现在我们已经了解到,对于%d的整数,printf想要intscanf想要指针指向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中是特殊的:无论何时将数组传递给函数,或者每当你做任何需要&#34;值&#34;你得到的是什么(编译器自动为你生成的)是一个指向数组第一个元素的指针。

这意味着要阅读包含scanf%s的字符串,我们只需致电:

scanf("%s", s);

&#34;但&?&#34;在哪里? &#34;我认为在致电&时总是需要scanf!&#34;

嗯,不太好。调用scanf时,您总是需要指针。事实上,当你打电话给scanf("%s", s)时,就好像你已经写过了

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

%sscanf一起使用时,它需要一个指向几个字符中第一个字符的指针,即指向字符数组开头的指针,它应该开始写字符串它读。 (它是如何知道数组有多大?如果用户键入的字符串太长而无法放入数组中,我们会在一瞬间达到这些点。)

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

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

这又是,就像你写过

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

%sprintf一起使用时,它需要指向应该开始打印的几个字符中的第一个字符,直到找到终止'\0'个字符。

所以%sprintfscanf一样特殊,因为字符串很特殊(因为数组很特殊)。对于%d%c以及几乎所有其他格式说明符,当您致电&时通常需要scanf,而您通常不需要& printf 1}}当你致电%s时。但是对于&,您通常不希望printfscanf {/ 1}}。

(如果我们更仔细地考虑一下,那么scanf%s并不需要&,例外情况并非如此。请记住,规则确实如此, scanf始终需要指针。scanf%c不需要&的唯一原因是,当您传递数组时,您会获得指向数组的指针#39;自动成为第一个元素。因此printf%s实例异常:printf%s 期待指针, printf%s被设计为期望指针的原因是没有办法不给它一个:它接受指针,因为字符串,这是你总是最终给它的东西。)

因此%s的规则是scanf期望指向几个字符中的第一个字符的指针,printf期望指向几个字符中的第一个字符的指针。

现在,在完成所有背景的情况下,我们可以查看您的代码。你基本上写了

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

起初,这似乎有点,有点,几乎是正确的。 scanf%s想要一个指向一个角色的指针,你给它&c,这是一个指向一个角色的指针。但是%s确实想要一个指针到几个字符中的第一个。但你给了它一个指向一个字符的指针。因此,当用户键入一个字符串时,键入的第一个字符将存储在c中,但其余字符和终止'\0'将被写入未分配的内存,位于右侧的某个位置。变量c。他们会覆盖(&#34; clobber&#34;)内存,这些内存可能用于其他内容。这是一个严重的问题,但它可能不会马上变得明显。

最后,您尝试使用printf再次打印出来。你第一次尝试

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

但这根本不起作用。原因是%s printf期望指针指向char,但您给它一个简单的char。假设c包含字母'A'。这最终会要求printf转到地址65并开始打印字符,直到找到终止'\0'。为什么要解决65?因为65是A的ASCII码。但是在内存中从地址65开始可能不是一个正确的,以null结尾的字符串;事实上,您的程序很可能无法从地址65中读取。

那么你试过

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

并且似乎正常工作。它&#34;工作&#34;因为,如果 scanf成功地将一个完整的字符串存储到c并且将未分配的内存存储在它的右边,并且如果破坏该内存在某种程度上不会导致(太多)其他问题,然后当您将指针&c传递给printf时,printf可以找到这些字符,组成一个字符串,并打印出来。

所以它&#34;工作&#34;,但正如我所说,出于错误的原因:在这个过程中它会踩踏它不会拥有的#34;并且更早或者之后,其他东西将不会起作用。

你应该如何扫描和打印字符串?正如我们之前看到的那样,一种方式是这样的:

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

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

现在当scanf获得指向数组s的第一个元素的指针时,它有10个字符可供使用。

我们确实不得不担心用户输入超过9个字符的可能性。但是有一个解决方法:我们可以告诉scanf它允许读取的字符串有多长,它允许写入我们递送的数组的字符数:<\ n / p>

scanf("%9s", s);

那里的9告诉scanf它不允许从用户那里读取超过9个字符。由于9小于10,因此终止'\0'字符的空间仍然存在。

关于scanf还有更多可以说的。正如chqrlie在评论中指出的那样,检查其返回值非常重要,以确保它能够成功转换任意数量的值。关于空白,它有一些奇怪的规则。除非您知道自己在做什么,否则无法通过调用其他输入阅读功能(例如scanfgetchar - 来混淆与fgets的通话 - 您&#39} ;会得到奇怪的结果。而且,最后,scanf是如此的笨拙和(最终)缺乏真正有用的功能,它根本不值得使用。但这些是另一天的主题,因为这个答案已经tl;dr了。

答案 1 :(得分:1)

%s格式说明符需要指向字符串的指针。与scanf一起使用时,它必须是char数组,其中包含您输入的字符的足够字符以及表示字符串结尾的尾随空字节。在printf()中,它必须是以空值终止的char数组。

使用指向char变量的指针不起作用,因为它没有空字节空间。您通过在变量外部写入来导致未定义的行为。

char word[100];
scanf("%s", word);
printf("%s\n", word);

您可以使用%c来读取和写入单个字符,而不是多个字符的字符串。

char letter;
scanf("%c", &letter);
printf("%c\n", letter);

答案 2 :(得分:0)

声明中char a; a是一个字符变量&amp;扫描char变量使用%c格式说明符。

scanf("%s",a);/* %s expects base address of char buffer, not single char */
scanf(" %c",&a);/* this is correct */

如果您想使用%s进行扫描,那么您的输入应该是char buf[10]之类的字符缓冲区。例如

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

当你使用%c或%d时,你不需要在&amp;(&符号)上签名吗?不需要向&提供地址printf()因为printf()作业是打印不扫描。例如

char input;
scanf("%c",&input);/* here you need &, As scanf() will store input char into
                      address you provided i.e &input */
printf("%c",input);/*here no need &input, bcz input char already stored,
                     printf will just print the char*/

好吧,如果您打印地址,可以使用%p

printf("%p",a);/*a is char buffer */