而对于“0123456”,因为在进行字符串的大小比较时,它小于“123456”,flag的值是 -1,在内存中将按照补码存放负数,所以实际存储的不是 0x01000000 而是 0xffffffff。那么字符串截断后符 0x00 淹没后,变成 0x00ffffff,还是非 0,所以没有进入正确分支。
其实,本示例只是用一个字节淹没了邻接变量,导致程序进入密码正确的处理流程,使设计的验证功能失效。
尽量显式地指定数组的边界
在 C 语言中,为了提高运行效率,给程序员更大的空间,为指针操作带来更多的方便,C 语言内部本身不检查数组下标表达式的取值是否在合法范围内,也不检查指向数组元素的指针是不是移出了数组的合法区域。因此,在编程中使用数组时就必须格外谨慎,在对数组进行读写操作时都应当进行相应的检查,以免对数组的操作超过数组的边界,从而发生缓冲区溢出漏洞。
要避免程序因数组越界所发生的错误,首先就需要从数组的边界定义开始。尽量显式地指定数组的边界,即使它已经由初始化值列表隐式指定。示例代码如下所示:
int a[]={1,2,3,4,5,6,7,8,9,10};
很显然,对于上面的数组 a[],虽然编译器可以根据始化值列表来计算出数组的长度。但是,如果我们显式地指定该数组的长度,例如:
int a[10]={1,2,3,4,5,6,7,8,9,10};
它不仅使程序具有更好的可读性,并且大多数编译器在数组长度小于初始化值列表的长度时还会发生相应警告。
当然,也可以使用宏的形式来显式指定数组的边界(实际上,这也是最常用的指定方法),如下面的代码所示:
#define MAX 10
…
int a[MAX]={1,2,3,4,5,6,7,8,9,10};
除此之外,在 C99 标准中,还允许我们使用单个指示符为数组的两段“分配”空间,如下面的代码所示:
int a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};
在上面的 a[MAX] 数组中,如果 MAX 大于 10,数组中间将用 0 值元素进行填充(填充的个数为 MAX-10,并从 a[5] 开始进行 0 值填充);如果 MAX 小于 10,“[MAX-5]”之前的 5 个元素(1,2,3,4,5)中将有几个被“[MAX-5]”之后的 5 个元素(6,7,8,9,10)所覆盖,示例代码如下所示:
#define MAX 10
#define MAX1 15
#define MAX2 6
int main(void)
{
int a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};
int b[MAX1]={1,2,3,4,5,[MAX1-5]=6,7,8,9,10};
int c[MAX2]={1,2,3,4,5,[MAX2-5]=6,7,8,9,10};
int i=0;
int j=0;
int z=0;
printf("a[MAX]:n");
for(i=0;i<MAX;i++)
{
printf("a[%d]=%d ",i,a[i]);
}
printf("nb[MAX1]:n");
for(j=0;j<MAX1;j++)
{
printf("b[%d]=%d ",j,b[j]);
}
printf("nc[MAX2]:n");
for(z=0;z<MAX2;z++)
{
printf("c[%d]=%d ",z,c[z]);
}
printf("n");
return 0;
}










