在做LeetCode第22题时,https://leetcode-cn.com/problems/generate-parentheses/
我一开始写了下面这些代码
void generateOneByOne(char *sublist, char ***result, int left, int right, int index, int* returnSize)
{
//终止条件,使用完所有的括号
if (left == 0 && right == 0){
int new_size = *returnSize + 1;
//如果原来为空
if ( *result == NULL ) {
*result = (char **)malloc( sizeof(char*) * new_size);
} else{
//如果不为空,则要重新分配下内存
*result = (char **)realloc(*result, new_size * sizeof(char *));
}
//增加新的数据
(*result)[*returnSize] = (char *)malloc( sizeof(char) * strlen(sublist) );
strcpy((*result)[*returnSize], sublist);
*returnSize = new_size;
}
if ( left > 0){
sublist[index] = '(';
index++;
generateOneByOne(sublist, result, left - 1, right, index, returnSize);
//恢复现场
index--;
}
if ( right > left){
sublist[index] = ')';
index++;
generateOneByOne(sublist, result, left, right - 1, index, returnSize);
index--;
}
}
//result, 一开始**result
char ** generateParenthesis(int n, int* returnSize){
int str_len = n * 2 + 1;
char *substring = (char *)malloc(sizeof(char) * str_len);
substring[str_len] = '\0';
char **result = NULL;
generateOneByOne(substring, &result, n, n,0, returnSize);
return result;
}
在本地运行的时候,是没有任何问题,而在LeetCode上运行时, 就报错了,
=================================================================
==29==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000014 at pc 0x000000401d79 bp 0x7ffe252ab3d0 sp 0x7ffe252ab3c8
WRITE of size 1 at 0x602000000014 thread T0
#2 0x7f36d55b22e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0)
0x602000000014 is located 0 bytes to the right of 4-byte region [0x602000000010,0x602000000014)
allocated by thread T0 here:
#0 0x7f36d6e612b0 in malloc (/usr/local/lib64/libasan.so.5+0xe82b0)
#3 0x7f36d55b22e0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202e0)
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[04]fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==29==ABORTING
错误太长,而且我也看不懂。于是我按照我的经验,检索了" AddressSanitizer: heap-buffer-overflow "相关内容,一个可靠回答在https://stackoverflow.com/questions/51579267/addresssanitizer-heap-buffer-overflow-on-address。
简单都说,就是通常的C编译器是不会检查边界问题的,也就是如果我定义了int a[10]
,我访问a[100]
也不会提示错误。但是,如果你在编译的时候加上-fsanitize=address
参数,程序运行的时候就会做边界检查,在越界的时候报错。
因此我的源代码中存在了我没有发现的越界行为,你能看出是哪里吗?
第一处是substring[str_len] = '\0'
, 大小为N的数组,最后一位是N-1。
第二处错误在(*result)[*returnSize] = (char *)malloc( sizeof(char) * strlen(sublist) );
中,新申请的内存大小应该是sizeof(char) * (strlen(sublist) + 1)
, 需要放在最后的'\0';
此外,对于这种“明明我可以”的报错,官方建议你绕行C/C++,换个编程语言
C/C++
The most frequent culprit causing undefined behavior is out-of-bounds array access. These bugs could be hard to debug, so good luck. Or just give up on C/C++ entirely and code in a more predictable language, like Java. :)