首页 > study > Scanf通过%x(%d)读入char出现栈溢出的诡异问题

Scanf通过%x(%d)读入char出现栈溢出的诡异问题

2011年7月11日 sigma 发表评论 阅读评论

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

有如下代码:

#include <stdio.h>
#include <stdlib.h>
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("\n");
	for(i = 7; i >= 0 ; i--)
	  {
		  printf("%2x ",a[i]);
	  }
	printf("\n");
	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]还是会冲掉其他数据。

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

本文作者: Sigma    在新浪微博关注SigmaSigmaWeibo    RSS订阅本博客
本文链接: http://www.sigma.me/2011/07/11/scanf-char.html
本博客采用知识共享署名—非商业性-禁止演绎使用3.0协议进行许可,转载请保留作者和原文链接。

分类: study 标签: , ,

  1. 2011年7月15日14:34 | #1

    都看不懂了呢

  2. 2011年7月15日07:04 | #2

    Dennis Ritchie 的一段话:

    character , short integer , an integer bit-field ,all either signed or not ,or an object of enumationtype, may be used in an expression whereever an integer may be used. if an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. The process is called integral promotion.

    这种类似的问题我也碰到过,unsigned ,signed ,long, shot 各种隐式,显式转换,

    出现很奇怪的错误,很纠结。这也是C语言的缺陷,类型。

  3. Spacenet
    2014年6月25日16:00 | #3

    将n字节数据存入仅m字节空间,n>m时,就会访存越界。比如这段代码转int到ASCII:
    _____________________
    #include

    int main()
    {
    char ch;

    scanf(“%d”, &ch);

    return 0;
    }
    _____________________

    拿到VC6 Release下运行,出错。

  4. leo000111
    2014年8月18日16:15 | #4

    而且可以推测的出你这机器是little-endian, big-endian的结果又会不一样, 至于会出现栈溢出的警告我想可能是编译器的差异吧

  1. 本文目前尚无任何 trackbacks 和 pingbacks.

无觅相关文章插件,快速提升流量