一段跑了三年的采集程序突然崩了,日志停在读取第1025个字节。查下去是缓冲区开了1024,循环条件写成了i小于等于1024。数组越界就是访问了不存在的下标,看着是小错,后果可能只是报错,也可能是整个进程被静默破坏。
下标从0开始,合法范围是0到n减1
长度为5的数组,合法下标是0、1、2、3、4。访问a[5]就越界了。最常见的成因是差一错误:循环写i小于等于arr.length,或者取最后一个元素写成arr[arr.length]。
不同语言的表现差别很大
- Java:抛ArrayIndexOutOfBoundsException,带具体下标,最好排查。
- Python:抛IndexError,但负下标合法,a[-1]取最后一个。
- JavaScript:读越界返回undefined,写越界直接给数组加属性,不报错,最难发现。
- C/C++:不做检查,属于未定义行为,可能读到垃圾值,也可能直接段错误。
为什么C里越界特别危险
数组在内存里连续存放,越界写会把数据写到相邻变量或栈帧上。经典的栈溢出攻击就是覆盖函数返回地址。就算没被利用,也可能悄悄改掉旁边的计数器,几天后才在别处出错。所以C代码里凡是拷贝,都要先确认长度,strncpy和snprintf比strcpy、sprintf安全。
怎么防
循环用小于而不是小于等于;取长度用sizeof(arr)/sizeof(arr[0])只对栈上数组有效,传参后退化成指针会算错;C++用vector的at()会做检查;Java和Python里对可能为空的集合先判长度。
看到越界报错,先看循环边界和长度来源,多数是长度取错而不是下标算错。C项目里建议编译加-fsanitize=address,越界能当场定位到行号。