学习总结

对于多维数组,是否能用单循环遍历所有的数据?
例:arr[5][5];
可以用整型i的整除和求余来控制数组的位置。

如何计算出时间间隔
CTime tmToday; //今天的日期
CTime tmPreTwoDay; //前两天的日期
CTimeSpan tmspanTwoDay(1,0,0,0); //两天的时间距离
//CTimeSpan m_timespan(3,4,5,6); // 3天,4小时,5分,6秒
CString a;
//获取日期间隔。
tmToday=CTime::GetCurrentTime();//获得当前日期
tmPreTwoDay=CTime::GetCurrentTime()
CTimeSpan tmspanTwoDay(i,0,0,0); //两天的时间距离
tmspanTwoDay=tmToday-tmPreTwoDay;
a.Format("%02d %02d %02d",tmPreTwoDay.GetYear()%100,tmPreTwoDay.GetMonth(),tmPreTwoDay.GetDay());
AfxMessageBox(a);
}

怎么避免数组越界?
避免越界首先要尽量显式的指定出数组的边界。
对数组做越界检查,确保索引值位于合法的范围之内。if(num<ARRAY_NUM)
正确的应用操作符。
简单地讲,sizeof 是一个单目操作符,不是函数。其作用就是返回一个操作数所占的内存字节数。其中,操作数可以是一个表达式或括在括号内的类型名,操作数的存储大小由操作数的类型来决定。例如,对于数组 int a[5],可以使用“sizeof(a)”来获取数组的长度,使用“sizeof(a[0])”来获取数组元素的长度。

但需要注意的是,sizeof 操作符不能用于函数类型、不完全类型(指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void 类型等)与位字段。

数组越界有什么危害?
数组的越界可以包括,下标越界,指针范围越界。
下标越界会取得错误的变量值。指针越界会使程序整体的变量取值向后,会造成缓冲区溢出,破坏变量,程序崩溃,程序流程执行错误,代码返回值改变功能失效等。
例如

#define PASSWORD "123456"
int Test(char *str)
{
    int flag;
    char buffer[7];
    flag=strcmp(str,PASSWORD);
    strcpy(buffer,str);
    return flag;
}
int main(void)
{
    int flag=0;
    char str[1024];
    while(1)
    {
        printf("请输入密码:  ");
        scanf("%s",str);
        flag = Test(str);
        if(flag)
        {
            printf("密码错误!\n");
        }
            else
            {
                printf("密码正确!\n");
            }
    }
    return 0;
}

由于程序将用户输入的字符串原封不动地复制到 Test() 函数的数组 char buffer[7] 中。因此,当用户的输入大于 7 个字符的缓冲区尺寸时,就会发生数组越界错误,这也就是大家所谓的缓冲区溢出(Buffer overflow)漏洞。但是要注意,如果这个时候我们根据缓冲区溢出发生的具体情况填充缓冲区,不但可以避免程序崩溃,还会影响到程序的执行流程,甚至会让程序去执行缓冲区里的代码。示例运行结果为:

请输入密码:12345
密码错误!
请输入密码:123456
密码正确!
请输入密码:1234567
密码正确!
请输入密码:aaaaaaa
密码正确!
请输入密码:0123456
密码错误!
请输入密码:

在示例代码中,flag 变量实际上是一个标志变量,其值将决定着程序是进入“密码错误”的流程(非 0)还是“密码正确”的流程(0)。当我们输入错误的字符串“1234567”或者“aaaaaaa”,程序也都会输出“密码正确”。但在输入“0123456”的时候,程序却输出“密码错误”,这究竟是为什么呢?

其实,原因很简单。当调用 Test() 函数时,系统将会给它分配一片连续的内存空间,而变量 char buffer[7] 与 int flag 将会紧挨着进行存储,用户输入的字符串将会被复制进 buffer[7] 中。如果这个时候,我们输入的字符串数量超过 6 个(注意,有字符串截断符也算一个),那么超出的部分将破坏掉与它紧邻着的 flag 变量的内容。

当输入的密码不是宏定义的“123456”时,字符串比较将返回 1 或 -1。我们都知道,内存中的数据按照 4 字节(DWORD)逆序存储,所以当 flag 为 1 时,在内存中存储的是 0x01000000。如果我们输入包含 7 个字符的错误密码,如“aaaaaaa”,那么字符串截断符 0x00 将写入 flag 变量,这样溢出数组的一个字节 0x00 将恰好把逆序存放的 flag 变量改为 0x00000000。在函数返回后,一旦 main 函数的 flag 为 0,就会输出“密码正确”。这样,我们就用错误的密码得到了正确密码的运行效果。

而对于“0123456”,因为在进行字符串的大小比较时,它小于“123456”,flag的值是 -1,在内存中将按照补码存放负数,所以实际存储的不是 0x01000000 而是 0xffffffff。那么字符串截断后符 0x00 淹没后,变成 0x00ffffff,还是非 0,所以没有进入正确分支。

其实,本示例只是用一个字节淹没了邻接变量,导致程序进入密码正确的处理流程,使设计的验证功能失效。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值