鱼C论坛

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

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

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

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

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

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

使用道具 举报

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

点评

我很赞同!: 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 其实都是匹配一个字符串的占位符,而 ...


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

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

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

使用道具 举报

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

int main(void) {
    char ch;
    scanf("%3c", &ch);
    printf("%c\n", ch);
    //system("pause");
    return 0;
}
$ gcc -g -Wall -o main main.c
$ ./main
xyz
x
*** stack smashing detected ***: terminated
zsh: IOT instruction (core dumped)  ./main
$ 

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

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

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

  This frame has 1 object(s):
    [32, 33) 'ch' (line 5) <== Memory access at offset 33 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc:342 in scanf_common
Shadow bytes around the buggy address:
  0x10001d267640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d267650: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d267660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d267670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d267680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10001d267690: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[01]f3
  0x10001d2676a0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d2676b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d2676c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d2676d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001d2676e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==818830==ABORTING
$ 
想知道小甲鱼最近在做啥?请访问 -> 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-11-17 17:48

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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