最近需要将数组转换为字符串,就想到了库函数snprintf,sprintf,在使用sprintf的时候出现了一个奇怪的问题,程序如下:- #include <stdio.h>
- #include <string.h>
- #define N 5
- int main(int argc,char *argv[])
- {
- int i = 0;//i = 2;
- int a[N] = {0,1,2,3,4};
- char str[N];
- //memset(str,'\0',N 1);
-
- for(; i < N; i)
- {
- sprintf(str i,"%d",a[i]);
- printf("%d,%c,%d\n",a[i],str[i],i);
- }
-
- puts(str);
-
- return 0;
- }
snprintf会在末尾加上一个'\0',来标记字符串的结束,编译运行程序,得到的是一个死循环,通过gdb调试查看其汇编代码,发现一些异常.
- gdb ./tas
-
- set disassembly-flavor intel
-
- disassemble main
在数据初始化部分的代码片断如下:
- 0x0804848e < 10>: mov DWORD PTR [esp 0x2c],0x0
- 0x08048496 < 18>: mov DWORD PTR [esp 0x10],0x0
- 0x0804849e < 26>: mov DWORD PTR [esp 0x14],0x1
- 0x080484a6 < 34>: mov DWORD PTR [esp 0x18],0x2
- 0x080484ae < 42>: mov DWORD PTR [esp 0x1c],0x3
- 0x080484b6 < 50>: mov DWORD PTR [esp 0x20],0x4
- 0x080484be < 58>: jmp 0x8048522 <main 158>
- 0x080484c0 < 60>: mov eax,DWORD PTR [esp 0x2c]
- 0x080484c4 < 64>: mov ecx,DWORD PTR [esp eax*4 0x10]
- 0x080484c8 < 68>: mov edx,0x8048610
- 0x080484cd < 73>: mov ebx,DWORD PTR [esp 0x2c]
- 0x080484d1 < 77>: lea eax,[esp 0x27]
- 0x080484d5 < 81>: add eax,ebx
- 0x080484d7 < 83>: mov DWORD PTR [esp 0x8],ecx
- 0x080484db < 87>: mov DWORD PTR [esp 0x4],edx
- 0x080484df < 91>: mov DWORD PTR [esp],eax
- 0x080484e2 < 94>: call 0x8048364 <sprintf@plt>
从代码上基本可以发现在分配变量地址的问题
- a[0] --> esp + 0x10
- a[1] --> esp + 0x14
- a[2] --> esp + 0x18
- a[3] --> esp + 0x1c
- a[4] --> esp + 0x20
-
- str[0] --> esp + 0x27
- .....
- str[5] --> esp + 0x2b
-
- i ----> esp + 0x2c
在C语言书中提到字符串数组末尾都会默认添加一个'\0',这里似乎没看到,于是,通过gdb调试,打印地址,来验证假设:
- p &i
- $1 = (int *) 0xbffff82c
-
- p &str[4]
- $2 = 0xbffff82b "\b\001"
-
- p &str[5]
- $3 = 0xbffff82c "\001"
地址和i一样,末尾可能真的没有加零,如果将上面的注释替换掉的话,通过执行memset后,i的值已经变成0了。后来发现只有未初始化的字符串末尾均未加'\0',这个之前都没有注意过。