【问题标题】:Scanf a char in C with an example用一个例子在 C 中扫描一个字符
【发布时间】:2014-01-26 17:10:02
【问题描述】:

我在Cplusplus做一个教程

有这个例子:

/* scanf example */
#include <stdio.h>

int main ()
{
  char str [80];
  int i;

  printf ("Enter your family name: ");
  scanf ("%79s",str);  
  printf ("Enter your age: ");
  scanf ("%d",&i);
  printf ("Mr. %s , %d years old.\n",str,i);
  printf ("Enter a hexadecimal number: ");
  scanf ("%x",&i);
  printf ("You have entered %#x (%d).\n",i,i);

  return 0;
}

我在我的 Macbook 上尝试过,当我执行“制作示例”时出现此错误;

example.c: In function ‘main’:
example.c:25: warning: format ‘%79s’ expects type ‘char *’, but argument 2 has type ‘char (*)[80]’

当我执行“valgrind ./example”时,我会收到此消息;

==1326== Memcheck, a memory error detector
==1326== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==1326== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==1326== Command: ./example
==1326== 
--1326-- ./example:
--1326-- dSYM directory is missing; consider using --dsymutil=yes
Enter your family name: Park
Enter your age: 20
Mr. Park, 20 years old.
==1326== 
==1326== HEAP SUMMARY:
==1326==     in use at exit: 8,280 bytes in 3 blocks
==1326==   total heap usage: 3 allocs, 0 frees, 8,280 bytes allocated
==1326== 
==1326== LEAK SUMMARY:
==1326==    definitely lost: 0 bytes in 0 blocks
==1326==    indirectly lost: 0 bytes in 0 blocks
==1326==      possibly lost: 0 bytes in 0 blocks
==1326==    still reachable: 8,280 bytes in 3 blocks
==1326==         suppressed: 0 bytes in 0 blocks
==1326== Rerun with --leak-check=full to see details of leaked memory
==1326== 
==1326== For counts of detected and suppressed errors, rerun with: -v
==1326== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

问题 1。 “make example”给出了一个错误,但程序做了它应该做的事情。 那么这里的错误是什么?

问题 2 我做了很少的研究,我了解到你不能在 C 中分配一个 char,因为 char 是不可变的。这就是 char 与 int、float 或 double 的不同之处。如果是这样,我怎么能在这里分配 str ?

问题 3 什么是 Char 数组?我读了几篇文章,但我不明白它是如何工作的!!!

char str [80];

意味着 str 最多可以占用 80 个字节,对吗? 那是什么

scanf ("%79s",str);  

?

非常感谢你们的帮助,祝 2014 年快乐! :)

【问题讨论】:

  • ad 1) 这是一个警告,而不是错误。我认为该消息非常明确地说明了它的警告内容,即str 的类型是字符数组,而指针是预期的。
  • 声明iunsigned int i;
  • 鉴于警告,您确定您之前在scanf 调用中没有&amp;str 吗?你看,&amp;str 的类型是 char (*)[80],但 str 是完全正确的,不应该给你警告。

标签: c char variable-assignment arrays


【解决方案1】:

这个:

scanf ("%79s", str);

的意思是“读取一个字符串 (s),但从我给你的指针开始,不要使用超过 79 个字符的空间”,有点。这是一种在读取字符串时防止缓冲区溢出的方法。它使用79 而不是80 为字符串终止符留出空间。

The documentation 说:

字符串输入转换存储一个终止空字节('\0')来标记输入的结束;最大字段宽度不包括此终止符。

【讨论】:

  • 这就是为什么使用scanf() 是危险的,应该避免。
  • @unwind 感谢您的回答! :)
【解决方案2】:

我在 Cplusplus 做教程

请不要。该网站充满了不良、不正确和/或误导性示例。帮自己一个忙,然后选择其他网站。

我在我的 Macbook 上尝试过,当我执行“制作示例”时出现此错误;

可能你拼错了源代码并且你写了&amp;str而不是str(我认为这是一个坏习惯:当你遇到scanf()时不加思索地抛出地址运算符)。这会产生一个指向数组的指针,而转换说明符需要一个指向字符的指针。

“make 示例”给出了错误,但程序执行了它应该执行的操作。那么这里的错误是什么?

程序没有没有做它应该做的事情。相反,它调用未定义的行为,并且它假装正常工作。

我做了一些研究,发现不能在 C 中分配 char,因为 char 是不可变的

错了。

什么是 Char 数组?

您示例中的字符数组是str。就是这样:一个 80 个字符的数组。不幸的是,数组在 C 中具有一些奇怪的属性,例如在传递给函数时隐式衰减为指针。这就是scanf() 希望您传递指针而不是数组参数的原因:当您编写时

scanf("format string", str);

那么实际传递的是指向str(又名&amp;str[0])中第一个元素的指针。


另外,如果你再接受一条好建议:不要使用scanf()它很危险,它的用法是违反直觉的,而且它一般没有任何好处.要接受用户输入,请考虑改用fgets()

【讨论】:

  • 还添加了有关初始化的建议。会很有帮助的。
  • @Koushik 对不起?你的意思是什么初始化?
  • ...在你问为什么 cplusplus.com 是“坏”之前 read this
  • @H2CO3 他使用的变量。这里它(不)还可以,但总的来说它总是一个好主意,不是吗?。
  • @Koushik 不,不一定。这一切都取决于上下文。有时它很好,有时它是多余的。
【解决方案3】:

错误

example.c:25:警告:格式“%79s”需要类型“char *”,但参数 2 的类型为“char (*)[80]”

只有在你写过类似的东西时才有意义

scanf ("%79s", &str); // wrong

而不是

scanf ("%79s",str);  // right

您发布的代码是您实际编译的吗?

关于您的具体问题:

问题 1.“make 示例”给出了错误,但程序执行了它应该执行的操作。那么这里的错误是什么?

首先,一点背景知识:除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,类型为“N-element”的表达式T" 的数组将被转换 ("decay") 为 "pointer to T" 类型的表达式,表达式的值将是数组第一个元素的地址。

当你写作时

scanf("%79s", str);

表达式 str 的类型为“char 的 80 元素数组”;由于str 不是sizeof 或一元&amp; 运算符的操作数,因此将其转换为“指向char”或char * 类型的表达式,其值为第一个地址数组中的元素。

如果你写过

scanf("%79s", &str);

表达式str 是一元&amp; 运算符的操作数,因此不会发生转换;相反,表达式&amp;str 的类型是“指向char80 元素数组 的指针”,或者char (*)[80](看起来很熟悉?),表达式的值是数组。

现在,恰好数组的地址和数组的第一个元素的地址相同;表达式str&amp;str 将产生相同的,但两个表达式的类型不同。

所以,假设您已经编写了scanf("%79s, &amp;str),代码仍然可以“工作”(&amp;str 的值与 str 相同),但由于不需要 scanf 的类型%s 转换说明符,编译器发出警告。

问题 2 我做了很少的研究,我了解到你不能在 C 中分配一个 char,因为 char 是不可变的。这就是 char 与 int、float 或 double 的不同之处。如果是这样,我怎么能在这里分配 str ?

要么您误解了,要么您正在阅读一个非常错误的参考资料。 C 中的字符串不是不可变的;试图修改字符串 literal 的内容会调用未定义的行为(有些平台会抛出异常,有些则不会),但这与说它们是不可变的不同,就像 Java 字符串一样不可变。

C 中的字符串存储为char 的数组。您可以使用strcpy 库函数或直接修改数组元素来更新字符串内容。例如:

char str[80];
...
strcpy( str, "a string" );

将字符串文字"a string" 的内容复制到str。你当然可以更新这个字符串,比如

str[0] = 'A'; // str now contains "A string"

或附加另一个字符串:

strcat( str, " with more stuff" ); // str now contains "A string with more stuff"
"a string" 这样的

字符串文字 被存储为char 的数组,这样它们的存储空间在程序启动时分配并保持到程序退出,并且它们在整个程序中可见。尝试修改字符串文字的内容会调用未定义的行为;它可能有效,也可能无效,编译器可能会抛出错误,您可能会在运行时出错。所以你不想做类似的事情

char *p = "This is a test";
strcpy( p, "This is another test" );
问题 3 什么是 Char 数组?我读了几篇文章,但我不明白它是如何工作的!!!

char str [80];
意味着 str 最多可以占用 80 个字节,对吗?

占用80个chars; char 映射到本机 8 位字节通常是真的,但也有例外(主要是非常古老的架构或古怪的嵌入式系统)。

那么什么是

scanf ("%79s",str);
?

在该调用中,所有scanf 接收到的都是指向目标缓冲区第一个元素的指针;它不知道目标缓冲区实际上有多大。如果你写了

scanf("%s", str);

如果用户输入的字符串长度超过 80 个字符,scanf 会在str 结束后立即将这些额外字符存储到内存中,这可能会破坏一些重要的内容并导致崩溃(或更糟糕的是,恶意软件利用)。 %79s 指示 scanf 从输入流中最多读取 79 个字符。为什么是 79 而不是 80?您需要为 0 终止符保留一个元素(在 C 中,string 是由 0 值字节终止的字符序列)。

【讨论】:

  • 非常感谢您的回答! :D 你回答的最后一部分正是我所需要的!我是 C 的新手,与 Python 相比,C 不太直观。再次感谢你! P.S.-我打错了 &str,它应该只是 str。
猜你喜欢
  • 2022-01-03
  • 2017-11-23
  • 2020-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-30
  • 1970-01-01
  • 2022-01-05
相关资源
最近更新 更多