让我们考虑以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(){
char again = 'Y';
int code;
do{
printf("Please inform your option:\n1 - New record\n2 - Delete record\n3 - ecovery record\n4 - Search records\n");
scanf("%d", &code);
switch(code){
case 1:
printf("Option %d\n",code);
break;
case 2:
printf("Option %d\n",code);
break;
case 3:
printf("Option %d\n",code);
break;
case 4:
printf("Opcao %d\n",code);
break;
default:
printf("code invalido!");
}
do{
printf("Do you wnat to again? [Y - Yes / N - No]: ");
scanf("%s", &again);
again = toupper(again);
}while(again != 'Y' && again != 'N');
printf("(DEBUG)Option after reading the string %d\n",code);
}while(again == 'S');
return 0;
}
我知道代码中有错误,因为我使用带有“%s”的scanf来读取单个字符的信息。最好使用“%c”。
但是,这段代码对我来说有趣的是,在执行“ scanf(”%s“,&again);”之后,变量“ code”的值变为零。而且我不确定为什么会这样。
主要主要假设是,由于我正在读取带有“%s”的字符串,因此在此过程中scanf将两个字符的信息存储在内存中:用户提供的字符和'\ 0'。而且我认为信息'\ 0'被存储在分配给变量“ code”的存储区中。
这有意义吗?
最诚挚的问候。
是的,这很有意义。但是直觉是一回事-让我们检查一下!
编译大量调试信息:
tmp$ gcc -ggdb test.c
启动gdb,运行程序:
tmp$ gdb ./a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...done.
(gdb) run
Starting program: /tmp/a.out
Please inform your option:
1 - New record
2 - Delete record
3 - ecovery record
4 - Search records
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b04260 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
好的,所以我按Ctrl-C来中断程序。让我们在code
上添加一个观察点。
(gdb) bt
#0 0x00007ffff7b04260 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
#1 0x00007ffff7a875e8 in _IO_new_file_underflow (fp=0x7ffff7dd18e0 <_IO_2_1_stdin_>) at fileops.c:592
#2 0x00007ffff7a8860e in __GI__IO_default_uflow (fp=0x7ffff7dd18e0 <_IO_2_1_stdin_>) at genops.c:413
#3 0x00007ffff7a69260 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>,
argptr=argptr@entry=0x7fffffffdbe8, errp=errp@entry=0x0) at vfscanf.c:634
#4 0x00007ffff7a785df in __isoc99_scanf (format=<optimized out>) at isoc99_scanf.c:37
#5 0x00000000004006c1 in main () at test.c:12
(gdb) frame 5
#5 0x00000000004006c1 in main () at test.c:12
12 scanf("%d", &code);
(gdb) watch code
Hardware watchpoint 1: code
(gdb) cont
Continuing.
现在,当我们提供“ 2”作为输入时,我们可以看到值发生了变化:
2
Hardware watchpoint 1: code
Old value = 32767
New value = 2
0x00007ffff7a6cde7 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>,
argptr=argptr@entry=0x7fffffffdbe8, errp=errp@entry=0x0) at vfscanf.c:1902
1902 vfscanf.c: No such file or directory.
好,那是第一个scanf
。让我们继续第二个,并给出“ n”作为答案。
(gdb) cont
Continuing.
Option 2
Do you wnat to again? [Y - Yes / N - No]: n
Hardware watchpoint 1: code
Old value = 2
New value = 0
_IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffdbe8,
errp=errp@entry=0x0) at vfscanf.c:1194
1194 in vfscanf.c
(gdb) bt
#0 _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffdbe8,
errp=errp@entry=0x0) at vfscanf.c:1194
#1 0x00007ffff7a785df in __isoc99_scanf (format=<optimized out>) at isoc99_scanf.c:37
#2 0x000000000040076d in main () at test.c:34
是,肯定会覆盖其中的code
!
我们在写什么样的价值?
(gdb) disass
Dump of assembler code for function _IO_vfscanf_internal:
[...]
0x00007ffff7a6a74e <+7886>: lea 0x1(%rax),%rbx
0x00007ffff7a6a752 <+7890>: movb $0x0,(%rax)
=> 0x00007ffff7a6a755 <+7893>: je 0x7ffff7a6a77f <_IO_vfscanf_internal+7935>
0x00007ffff7a6a757 <+7895>: mov -0x620(%rbp),%r12
0x00007ffff7a6a75e <+7902>: mov %rbx,%rsi
0x00007ffff7a6a761 <+7905>: mov (%r12),%rdi
[...]
movb
表示我们正在写一个字节。它是零的立即值(即常数)。它看起来确实像字符串终结者一样走动和嘎嘎!
如果我们想确保[[确实,我们可以尝试找到此库函数的确切源文件。
(gdb) disass /s $pc-3,+10
Dump of assembler code from 0x7ffff7a6a752 to 0x7ffff7a6a75c:
vfscanf.c:
1192 in vfscanf.c
0x00007ffff7a6a752 <_IO_vfscanf_internal+7890>: movb $0x0,(%rax)
1193 in vfscanf.c
1194 in vfscanf.c
=> 0x00007ffff7a6a755 <_IO_vfscanf_internal+7893>: je 0x7ffff7a6a77f <_IO_vfscanf_internal+7935>
0x00007ffff7a6a757 <_IO_vfscanf_internal+7895>: mov -0x620(%rbp),%r12
End of assembler dump.
就我而言,这很容易:我可以从Ubuntu的apt仓库安装“ glibc-source”软件包。您可能会遇到困难,具体取决于您的系统类型。无论如何,请查看第1192行。这绝对是空终止符。
glibc-2.23$ find . -name vfscanf.c ./stdio-common/vfscanf.c glibc-2.23$ less -N ./stdio-common/vfscanf.c [...] 1189 1190 str = __mempcpy (str, buf, n); 1191 #endif 1192 *str++ = '\0'; 1193 1194 if ((flags & MALLOC) && str - *strptr != strsize) 1195 { 1196 char *cp = (char *) realloc (*strptr, str - *strptr);