昨天使用scanf(fscanf)碰到一个很诡异的问题,问题抽象出来是这样的:

有如下代码:

#include 
#include 
int main()
{
	FILE *fin;
	int i;
	unsigned char a[8];
	fin=fopen("test_file","r");

	for(i = 7; i >= 0 ; i--)
//	for(i = 0; i <= 7 ; i++)
	  {
		  fscanf(fin,"%x",a+i);
		  printf("%2x ",a[i]);
	  }
	printf("\
");
	for(i = 7; i >= 0 ; i--)
	  {
		  printf("%2x ",a[i]);
	  }
	printf("\
");
	fclose(fin);
	return 0;
}

其中test_file的内容是:11 22 33 44 55 66 77 88 99

执行结果是

11 22 33 44 55 66 77 88 
 0  0  0  0  0  0  0 88 

倘若用注释的行数代替原来那个for循环,则两行结果一致(只是顺序反了)。

对这种现象一直不解,后来才发现是scanf和%x的鬼,在%x(和%d)的scanf中,会将输入的数据按照数据类型写到对应指针的连续四字节中,由于第一个for循环是从高地址往低地址写,后面写得低地址数据会将高地址数据冲掉,反过来,虽然冲了但是没有关系,不过最后还是回冲掉其他的内存。
示意如下图
内存:
a[0] |___|___|___|___|___|___|___|___| a[7]
%x对应的32位:                            
                                             |___|___|___|___|
在第一种情况,i从7减到0,每次写到内存的数据都是32(有效数据只有低8位,即四格中的第一个,其他三格都是0),导致后面scanf的数据会把前面的冲掉,只剩下0。(第一个a[7]scanf回冲掉其他内存的数据,出现栈溢出)
而第二种情况,由于i是从0到7,后面的不会冲掉前面的,但是最后的a[7]还是会冲掉其他数据。

令人惊奇的是,我在实验室的服务器上跑这段代码没有任何异常(虽然结果不对),但在我自己的机器跑完后却会告知有可能出现了栈溢出。