指针数组越界的思考

遇到了一个奇怪的问题,在for循环内,if判断指针指向的内容,for循环次数超越指针数组范围,没有报错!!!

C语言编译器是不会对数组下标进行越界检查的,于是重新回顾一下内存溢出和内存越界:

  1. 内存溢出:你要分配的内存超出了系统能给你的,系统不能满足需求,于是产生了溢出;
  2. 内存越界:你想系统申请一块内存,在使用的这块内存的时候,超过出了你申请的范围。

然后在内存越界的基础上,我这次又有了新的理解:

  1. 越界访问:仅仅越界读取内容,不一定会造成段错误(linux 越界读取,是不会出现段错误的),或者程序崩溃死机问题;这个真的是看运行环境!
  2. 越界修改:越界写入内容或者修改,一定会产生段错误 Segmentation fault (core dumped),导致程序崩溃死机。

 

先看一下内核版本: 是基于这个内核版本进行试验。

zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$ uname -i
x86_64
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$ uname -a
Linux ubuntu140453200 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$

下面的例子是从项目代码拷贝出来的,操作是一样的,也是多循环了128次。

在ASR或者高通平台上面跑是没有问题的。但是问题以奇怪的方式显现出来,当打开串口抓取log时,程序就卡在for循环的判断,if(p[i] == ';') 判断分号这里,然后超过30秒被系统kill掉,返回ERROR。还有云端设置主动上报时,也会卡死在这里。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LENGTH 1024

int main(int argc, char *argv[])
{
    int i = 0;
    char *p = NULL;
    char buf[MAX_LENGTH] = {0};
    memset(buf, 0, sizeof(buf));

    p = buf;
    for(i=0; i < 256; i++)
    {
        p[i] = 0x35;
    }
    p[i] = '\0';

    for(i = 0; i < (MAX_LENGTH+128); i++)
    {
        if(p[i] == ';')
        {
            printf("find ;   index %d \n", i);
            break;
        }
        if(i >= MAX_LENGTH)
        {
            printf(" index %d\n", i);
        }
    }
    return 0;
}

运行结果:
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$./pointer_2
 index 1024
 index 1025
 ...
 ...
 ...
 index 1150
 index 1151
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$

        需要加判断跳出for循环,程序的修改如下:

for(i = 0; i < (MAX_LENGTH+128); i++)
{
    if(p[i] == ';')
    {
        printf("find ;   index %d \n", i);
        break;
    }
    if(i >= MAX_LENGTH)
    {
        printf(" index %d\n", i);
    }
    //这里加上判断是否到达字符串的结尾,到达结尾跳出循环,越界了又没有跳出循环,奇奇怪怪的问题就来了
    if(p[i] == '\0'){
        break;
    }
}

越界访问与越界修改差异很大,于是我有突发奇想,试试memset,对数组进行越界尝试,动手写了一个程序,发现越界时也没有报错,程序还是执行下去了,没有在我期待的地方报错,出现  Aborted (core dumped)  或者Segmentation fault (core dumped) 或者 bus error 这样的错误。

有两个期待:

  1. 无论执行前面的memset还是后面的memset,都应该出现段错误Segmentation fault (core dumped),或者是总线错误;
  2. 注释掉第一个memset,如果是没有出现段错误,那么后面第二次打印与第一次打印应该有差异,理由是memset会对后面的堆栈空间进行修改。

结果这两个期待都没出现!!!

 int a = -1;
 char b[16]="123456";
 //memset(b, 0,32); //注意这里越界访问了,只有16字节空间,却修改了32字节
 int c = -1;

 printf("a=%d,c=%d\n", a,c);
 printf("a=%d,c=%d\n", a,c);

 a = 1;
 c = 2;
 memset(b, 0,32); //注意这里越界访问了,只有16字节空间,却修改了32字节
 printf("a=%d,c=%d\n", a,c);
 printf("a=%d,c=%d\n", a,c);


运行结果:
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$./pointer_2
a=-1,c=-1
a=-1,c=-1
a=1,c=2
a=1,c=2
zhang@ubuntu140453200:/mnt/external/zhang/test/linux_c$

原因分析与调试:

  1. 在linux系统中分配内存空间并不是连续的,是存在了优化,这一点可以加 %p打印地址(结果证明不是连续的)
  2. 如果想取消这种优化,使用限制字符 volatile 
  3. 编译时选用 -o0 参数,这样才能取消内部优化机制
  4. 使用gdb单步调试

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值