鱼C论坛

 找回密码
 立即注册
查看: 1722|回复: 6

[已解决]关于scanf函数的问题

[复制链接]
发表于 2023-1-27 09:51:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
如果在键盘上输入
XYZ<回车>
为什么最后的值是X?而Y和Z不会把X覆盖掉呢
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(void)
  4. {
  5.   char ch;
  6.   scanf("%3c", &ch);
  7.   printf("%c\n", ch);
  8.   system("pause");
  9.   return 0;
  10. }
复制代码
最佳答案
2023-1-27 10:35:52
很有意思的问题
这是因为可能和很多人一开始学到的不同的, %c 和 %s 其实都是匹配一个字符串的占位符,而非 %c 用于单个字符而 %s 用于字符串
两者的区别在于 %s 匹配连续非空字符组成的字符串,且会跳过前缀空白字符,而 %c 不忽略或跳过任何字符,且默认具有1的 field width,因此看起来像读入一个字符
您的代码实际上是读入了一个长度为3的字符串,因此 ch 中存储的恰为第一个字符 X,而后续的两个字符存储到了非法的内存中,这一点可以通过内存检查工具等方式确认
可以尝试以下代码 (动态内存分配是为了帮助内存检查工具,尽管实际上它并不需要)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(){
  4.     char* buffer = malloc(3);
  5.     scanf("%3c", buffer);
  6.     printf("%.3s", buffer);
  7.     free(buffer);
  8.     return 0;
  9. }
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-1-27 10:35:52 | 显示全部楼层    本楼为最佳答案   
很有意思的问题
这是因为可能和很多人一开始学到的不同的, %c 和 %s 其实都是匹配一个字符串的占位符,而非 %c 用于单个字符而 %s 用于字符串
两者的区别在于 %s 匹配连续非空字符组成的字符串,且会跳过前缀空白字符,而 %c 不忽略或跳过任何字符,且默认具有1的 field width,因此看起来像读入一个字符
您的代码实际上是读入了一个长度为3的字符串,因此 ch 中存储的恰为第一个字符 X,而后续的两个字符存储到了非法的内存中,这一点可以通过内存检查工具等方式确认
可以尝试以下代码 (动态内存分配是为了帮助内存检查工具,尽管实际上它并不需要)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(){
  4.     char* buffer = malloc(3);
  5.     scanf("%3c", buffer);
  6.     printf("%.3s", buffer);
  7.     free(buffer);
  8.     return 0;
  9. }
复制代码

点评

我很赞同!: 5.0
我很赞同!: 5
懂了,蟹蟹!!!  发表于 2023-1-27 12:21
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-1-27 11:55:36 | 显示全部楼层
dolly_yos2 发表于 2023-1-27 10:35
很有意思的问题
这是因为可能和很多人一开始学到的不同的, %c 和 %s 其实都是匹配一个字符串的占位符,而 ...


确实,学习到了
  1. #include <stdio.h>

  2. int main()
  3. {
  4.     char str[4];
  5.     scanf("%3c", str);
  6.     str[3] = '\0';
  7.     printf("%s", str);

  8.     return 0;
  9. }
复制代码
输入
  1. xyz
复制代码
输出
  1. xyz
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-1-27 12:16:11 | 显示全部楼层
  1. $ cat main.c
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. int main(void) {
  5.     char ch;
  6.     scanf("%3c", &ch);
  7.     printf("%c\n", ch);
  8.     //system("pause");
  9.     return 0;
  10. }
  11. $ gcc -g -Wall -o main main.c
  12. $ ./main
  13. xyz
  14. x
  15. *** stack smashing detected ***: terminated
  16. zsh: IOT instruction (core dumped)  ./main
  17. $
复制代码


首先,这个代码是错误的
可以看到核心转存了
输出x是因为,在ch的这个位置存储的确实是x,y和z写到紧挨着ch的后面了,如果有哪个倒霉蛋在这个位置的话,那他就被覆盖了

  1. cat main.c
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. int main(void) {
  5.     char ch;
  6.     scanf("%3c", &ch);
  7.     printf("%c\n", ch);
  8.     //system("pause");
  9.     return 0;
  10. }
  11. $ gcc-debug -o main main.c
  12. $ ./main
  13. xyz
  14. =================================================================
  15. ==818830==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffce937b4f1 at pc 0x7fc05dc73f29 bp 0x7ffce937b370 sp 0x7ffce937aaf8
  16. WRITE of size 3 at 0x7ffce937b4f1 thread T0
  17.     #0 0x7fc05dc73f28 in scanf_common /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:342
  18.     #1 0x7fc05dc74ae3 in __interceptor___isoc99_vscanf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1527
  19.     #2 0x7fc05dc74bf7 in __interceptor___isoc99_scanf /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1548
  20.     #3 0x557058487256 in main /tmp/main.c:6
  21.     #4 0x7fc05d23c28f  (/usr/lib/libc.so.6+0x2328f)
  22.     #5 0x7fc05d23c349 in __libc_start_main (/usr/lib/libc.so.6+0x23349)
  23.     #6 0x5570584870e4 in _start (/tmp/main+0x10e4)

  24. Address 0x7ffce937b4f1 is located in stack of thread T0 at offset 33 in frame
  25.     #0 0x5570584871c8 in main /tmp/main.c:4

  26.   This frame has 1 object(s):
  27.     [32, 33) 'ch' (line 5) <== Memory access at offset 33 overflows this variable
  28. HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
  29.       (longjmp and C++ exceptions *are* supported)
  30. SUMMARY: AddressSanitizer: stack-buffer-overflow /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:342 in scanf_common
  31. Shadow bytes around the buggy address:
  32.   0x10001d267640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  33.   0x10001d267650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  34.   0x10001d267660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  35.   0x10001d267670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  36.   0x10001d267680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  37. =>0x10001d267690: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[01]f3
  38.   0x10001d2676a0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  39.   0x10001d2676b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  40.   0x10001d2676c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  41.   0x10001d2676d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  42.   0x10001d2676e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  43. Shadow byte legend (one shadow byte represents 8 application bytes):
  44.   Addressable:           00
  45.   Partially addressable: 01 02 03 04 05 06 07
  46.   Heap left redzone:       fa
  47.   Freed heap region:       fd
  48.   Stack left redzone:      f1
  49.   Stack mid redzone:       f2
  50.   Stack right redzone:     f3
  51.   Stack after return:      f5
  52.   Stack use after scope:   f8
  53.   Global redzone:          f9
  54.   Global init order:       f6
  55.   Poisoned by user:        f7
  56.   Container overflow:      fc
  57.   Array cookie:            ac
  58.   Intra object redzone:    bb
  59.   ASan internal:           fe
  60.   Left alloca redzone:     ca
  61.   Right alloca redzone:    cb
  62. ==818830==ABORTING
  63. $
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-27 12:21:08 | 显示全部楼层
人造人 发表于 2023-1-27 12:16
首先,这个代码是错误的
可以看到核心转存了
输出x是因为,在ch的这个位置存储的确实是x,y和z写到紧 ...


原来是这样,看似储存一个字符,实则也影响了这个字符的地址后的其它数据(被 y 和 z 覆盖了),看来必须谨慎。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-1-27 12:23:19 | 显示全部楼层
人造人 发表于 2023-1-27 12:16
首先,这个代码是错误的
可以看到核心转存了
输出x是因为,在ch的这个位置存储的确实是x,y和z写到紧 ...

懂了懂了,原来如此
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-27 05:03

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表