2010年7月18日

  以前在项目中,发现了一个由于数组越界访问的BUG。数组越界访问的结果是未定义的,在大多数情况下,会出现空指针异常。

    但是如果在结构体中,两个数组的内存分配是连续的。为了示例方便,我将代码做了简化,例如如下面的test结果体中,数组a和数组b的内存是连续的,也就是说a[9]后面跟的就是b[0].

    因此在访问a的时候,如果数组越界,就会访问到数组b中,例如a[29]=b[19]; 因此如果不对a的数组访问进行下标校验,将会从b中取得数据,如果在对a赋值时越界,将会对b中的数据进行修改,这将会带来调试的巨大麻烦,表现形式是b数据在某个地方被修改了。

 1struct test
 2{
 3    int a[10];
 4    int b[20];
 5}
;
 6int main()
 7{
 8    struct test t;
 9    int result;
10    for(int i=0;i<20;i++)
11    {
12        t.b[i]=i+1;
13    }

14    result=*(t.a+29);
15    printf("the result is %d",result);
16    return 0;
17}
因此,切记在访问或者操作数组时,一定要对数组下标进行校验,例如上面的例子可以修改为:
1if(index<10&&index>=0
2{   
3    result=a[index]); 
4}

5
posted @ 2010-07-18 01:31 role0523 阅读(746) | 评论 (2)编辑 收藏
编译环境gcc4.4.1
如下代码
    int a = 4;
    a 
+= (a++);
    printf(
"%d\n",a);

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

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

    a 
= 4;
    (a
+++= a;//a++不能作为左值参加运算。
    printf("%d\n",a);

    a 
= 4;
    (
++a) += (a++);
    printf(
"%d\n",a);
打印的值为9,10,10,11。汇编代码如下:
# a += (a++);
00401326    movl   $0x4,0x1c(%esp)
0040132E    mov    
0x1c(%esp),%eax
00401332    add    %eax,%eax
00401334    mov    %eax,0x1c(%esp)
00401338    incl   0x1c(%esp)
0040133C    mov    
0x1c(%esp),%eax
00401340    mov    %eax,0x4(%esp)
00401344    movl   $0x46f024,(%esp)
0040134B    call   
0x41c750 <printf>
#  a 
+= (++a) ;
00401350    movl   $0x4,0x1c(%esp)
00401358    incl   0x1c(%esp)
0040135C    mov    
0x1c(%esp),%eax
00401360    add    %eax,%eax
00401362    mov    %eax,0x1c(%esp)
00401366    mov    0x1c(%esp),%eax
0040136A    mov    
%eax,0x4(%esp)
0040136E    movl   $
0x46f024,(%esp)
00401375    call   0x41c750 <printf>
#  (
++a) += a;
0040137A    movl   $
0x4,0x1c(%esp)
00401382    incl   0x1c(%esp)
00401386    mov    0x1c(%esp),%eax
0040138A    add    
%eax,%eax
0040138C    mov    
%eax,0x1c(%esp)
00401390    mov    0x1c(%esp),%eax
00401394    mov    %eax,0x4(%esp)
00401398    movl   $0x46f024,(%esp)
0040139F    call   
0x41c750 <printf>
#(
++a) += (a++);
004013A4    movl   $
0x4,0x1c(%esp)
004013AC    incl   
0x1c(%esp)
004013B0    mov    
0x1c(%esp),%eax
004013B4    add    
%eax,%eax
004013B6    mov    
%eax,0x1c(%esp)
004013BA    incl   
0x1c(%esp)
004013BE    mov    
0x1c(%esp),%eax
004013C2    mov    
%eax,0x4(%esp)
004013C6    movl   $
0x46f024,(%esp)
004013CD    call   
0x41c750 <printf>

可以看出,在运算过程中,++a优先级> +=  > a++
1)先运算a+a,然后再自增a
2),3)先运算++a,然后再a+a
4)先运算++a,然后a+a,然后a++
posted @ 2010-07-18 01:29 role0523 阅读(355) | 评论 (3)编辑 收藏

定义如下一个struct

struct T
{

    
int a;
    
int b[0];
    
int c;
};
打印各个成员的地址和大小,结果如下:
编译环境Code::Blocks 10.05
    struct T t;

    printf(
"%p:%d\n",&(t.a),sizeof(t.a));
    printf(
"%p:%d\n",&(t.b),sizeof(t.b));
    printf(
"%p:%d\n",&(t.c),sizeof(t.c));

0022FF38:4
0022FF3C:
0
0022FF3C:
4
可见定义的空数组,是不占任何空间的。其返回的内存地址是下一个可分配的地址。
posted @ 2010-07-18 01:12 role0523 阅读(403) | 评论 (0)编辑 收藏

如果在返回值为int的函数里,漏掉return,会发生什么情况?

编译工具:Code::Blocks 10.05,代码如下所示


#include 
<cstdio>

int f() 
{
}
int main ()
{

    printf(
"%d ",f());
    printf(
"%d ",f());
    printf(
"%d ",f());

    
return 0;
}
编译时,提示
In function 'int f()':|
warning: no return statement in function returning non-void|
但是能编译

Debug版本下结果如下:
1 2 2
Release版本下结果
0 0 0

下面分析一下为什么为发生这种情况:

从code:: blocks里面看到的 函数f的汇编代码
00401318    push   %ebp
00401319    mov    %esp,%ebp  //正确的似乎是 mov %ebp %esp
0040131B    leave
0040131C    ret
如果函数f()如下定义
int f()
{
    
return 100;
}
则汇编代码是
00401318    push   %ebp
00401319    mov    %esp,%ebp
0040131B    mov    $
0x64,%eax
00401320    leave
00401321    ret
接下来我们看一下整个main函数的汇编代码
00401322    push   %ebp
00401323    mov    %esp,%ebp
00401325    and    $0xfffffff0,%esp
00401328    sub    $0x10,%esp
0040132B    call   
0x401770 <__main>
00401330    call   0x401318 <f()>
00401335    mov    %eax,0x4(%esp)
00401339    movl   $0x403024,(%esp)
00401340    call   0x4019ac <printf>
00401345    call   0x401318 <f()>
0040134A    mov    
%eax,0x4(%esp)//将返回值传递给%esp+0x04
0040134E    movl   $0x403024,(%esp)
00401355    call   0x4019ac <printf>
0040135A    call   
0x401318 <f()>
0040135F    mov    
%eax,0x4(%esp)
00401363    movl   $0x403024,(%esp)
0040136A    call   
0x4019ac <printf>
0040136F    mov    $
0x0,%eax
00401374    leave
00401375    ret


从中我们可以看出,函数f()是将返回值通过EAX寄存器中,传递给调用它的代码。
如过我们漏掉了return intVal语句,在Debug模式下可能会导致,调用f()的代码得到的是上次的其他函数被调用的返回值。
在进入main之后EAX通常会被初始化为0x01.所以第一个f() 返回值为1.
printf 打印了“1 ”两个字符之后,返回值为2,所以第二个f()返回值为2。
printf 打印了“2 ”两个字符之后,返回值为2,所以第三个f()返回值为2。


在Release版本下,函数f()被优化,printf直接打印数字0。
 
  401320:    push   %ebp
  
401321:    mov    %esp,%ebp
  
401323:    and    $0xfffffff0,%esp
  
401326:    sub    $0x10,%esp
  
401329:    call   0x401760
  40132e:    movl   $
0x0,0x4(%esp)
  
401335:    
  
401336:    movl   $0x403024,(%esp)
  40133d:    call   
0x40199c
  
401342:    movl   $0x0,0x4(%esp)
  
401349:    
  40134a:    movl   $
0x403024,(%esp)
  
401351:    call   0x40199c
  
401356:    movl   $0x0,0x4(%esp)
  40135d:    
  40135e:    movl   $
0x403024,(%esp)
  
401365:    call   0x40199c
  40136a:    xor    
%eax,%eax
  40136c:    leave  
  40136d:    ret      
posted @ 2010-07-18 01:06 role0523 阅读(5059) | 评论 (0)编辑 收藏
仅列出标题  

导航

<2024年3月>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

常用链接

留言簿

随笔档案

搜索

最新评论

  • 1. re: i++与++i
  • @role0523
    基本上是没有去区别的,有时候++i能被优化掉,但很多时候编译器能发现他们是一样的,所以没有区别。
  • --chaogu
  • 2. re: i++与++i
  • @chaogu
    忘记在谁的blog上看到在比较i++和++i哪个操作更高效,于是就看看他们汇编指令的条数。其实大多数时候都是一样的。
  • --role0523
  • 3. re: 数组越界的陷阱
  • @陈梓瀚(vczh)
    嵌入式项目中 vector还是比较慎用的
  • --role0523
  • 4. re: 数组越界的陷阱
  • 不如用vector代替你的if
  • --陈梓瀚(vczh)
  • 5. re: i++与++i
  • 用汇编表达了一个很早就被默认了的定理,好还是不好呢?
  • --chaogu

阅读排行榜

评论排行榜