CPU底层那些事(数组越界也不一定就会出现堆栈溢出)

本文详细分析了数组越界现象,包括向低端地址和高端地址越界的后果。低端地址越界在某些情况下可能不会立即引发错误,但高端地址越界会导致程序崩溃。讨论了如何通过修改越界后的地址来避免错误,并提醒数组越界在字符串拷贝等操作中易发生,强调了检查和预防数组越界的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组作为一种数据类型。数组越界会咋样?会导致堆栈溢出吗?其实数组越界分两种,一种是数组向高端地址越界、一种是数组向地段地址越界。

1.数组向低端地址越界。
(1)定义一个数组,并依次给数组赋值。
代码:

#include <stdio.h>
#include <windows.h>
void fun1()
{
    long a[2];
    a[0] = 1;
    a[1] = 2;
    a[-1] = 3;
    a[-2] = 4;
}
int main()
{
    fun1();
    printf("123");
	system("pause");
    return 0;
}

底层代码:

fun1():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-16], 1
        mov     QWORD PTR [rbp-8], 2
        mov     QWORD PTR [rbp-24], 3
        mov     QWORD PTR [rbp-32], 4
        nop
        pop     rbp
        ret
.LC0:
        .string "123"
main:
        push    rbp
        mov     rbp, rsp
        call    fun1()
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0
        pop     rbp
        ret

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:数组向低端地址越界,是没有异常发生的,程序可以运行。

(2)fun2()对函数进行赋值。
代码:

void fun1()
{
    long a[2];
    a[0] = 1;
    a[1] = 2;
    a[-1] = 3;
    a[-2] = 4;
}
void fun2()
{
    long a = 1;
    long b = 2;
    long c = 3;
    long d = 4;
}

底层汇编:

fun1():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-16], 1
        mov     QWORD PTR [rbp-8], 2
        mov     QWORD PTR [rbp-24], 3
        mov     QWORD PTR [rbp-32], 4
        nop
        pop     rbp
        ret
fun2():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], 1
        mov     QWORD PTR [rbp-16], 2
        mov     QWORD PTR [rbp-24], 3
        mov     QWORD PTR [rbp-32], 4
        nop
        pop     rbp
        ret

在这里插入图片描述

总结:
(1)从fun1()和fun2()函数可以看出两个底层汇编是一模一样的。a[-1]和a[-2]相当于偷偷拓展的两个变量。
(2)之所以暂时安全,是因为越界元素所在的内存是一块“无主之地,空闲堆栈”。
(3)fun1()的数组合法使用的堆栈。此时的读写不回影响任何人。其实定义变量的过程就是向下(低端地址方向),拓展函数堆栈(rbp)的过程。

2.数组向高端地址越界
代码:

#include <stdio.h>
void fun3()
{
    long b[1];
    b[0] = 1;
    b[2] = 2;
}
int main()
{
    fun3();
    printf("哈哈哈");
    return 0;
}

底层代码:

fun3():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], 1
        mov     QWORD PTR [rbp+8], 2
        nop
        pop     rbp
        ret
.LC0:
        .string "\345\223\210\345\223\210\345\223\210"
main:
        push    rbp
        mov     rbp, rsp
        call    fun3()
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     eax, 0
        pop     rbp

        ret

在这里插入图片描述

总结:
(1)数组向高端地址越界,程序运行不成功,报错segmentation fault(139).
(2)之所以会越界,是因为fun3()函数堆栈的上方为高地址方向,可不是无主之地,这里存放了引导cpu跳转的地址信息,当fun3()函数执行完,cpu就靠这个地址信息,回到调用者main函数那边。让cpu跳转至2所在地方,显然导致异常。

3.如何解决数组异常呢。
代码:

#include <stdio.h>
void fun3()
{
    long b[1];
    b[0] = 1;
    b[2] = 0x401146;
}
int main()
{
    fun3();
    printf("哈哈哈");
    return 0;
}

底层汇编:

printf@plt-0x10:
printf@plt:
fun3():
 push   rbp
 mov    rbp,rsp
 mov    QWORD PTR [rbp-0x8],0x1
 mov    QWORD PTR [rbp+0x8],0x401146
 nop
 pop    rbp
 ret    
main:
 push   rbp
 mov    rbp,rsp
 call   401126 <fun3()>
 mov    edi,0x402004
 mov    eax,0x0
 call   401030 <printf@plt>
 mov    eax,0x0
 pop    rbp
 ret    
 nop    DWORD PTR [rax+0x0]

在这里插入图片描述
在这里插入图片描述

总结:恢复到原来的地址后,运行成功。

总结:
(1)数组向高端地址越界,会修改对战中的关键数据,程序往往会立刻崩溃。
(2)数组向低端地址越界,程序往往不会马上崩溃,可一旦与其他函数变量冲突,就会出现越界。
(3)数组越界一般很隐蔽,例如,拷贝字符串。用全局变量索引数组元素,都是数组越界的重灾区。(strcpy、strcat、memcpy、memmove)
extern int g;
Buffer[g] = 0;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值