C func 为任意长行动态分配内存;通过单元测试,原地崩溃
C func dynamically allocates mem for arbitrarily long line; passes unit testing, crashes in situ
我有一个函数 dgets
,其唯一目的是从 stdin
中获取任意长度的字符串。这是我用于单元测试的文件的全文:
#include <stdio.h>
#include <stdlib.h>
int dgets(char** lnptr, int lastIndex)
/*
RETRIEVE A LINE OF ARBITRARY LENGTH FROM STDIN
*/
{
char c;
int i = 0;
while( (c = getchar()) && c != EOF && c != '\n' ) {
if( i >= lastIndex-1 ) {
lastIndex *= 2;
*lnptr = (char*)realloc(*lnptr, lastIndex);
}
*(*lnptr + i) = c;
++i;
}
if( c == '\n' ) {
*(*lnptr + i) = c;
*lnptr = (char*)realloc(*lnptr, i+1);
*(*lnptr + i+1) = '[=10=]';
}
return i;
}
int main()
{
char* line;
line = (char*)calloc(10, sizeof(char));
while( dgets(&line, 10)) {
printf("****************\n%s****************\n", line);
free(line);
line = (char*)calloc(10, sizeof(char));
}
return 0;
}
该程序完全按照预期执行,错误或错误完全为零。这很好,因为我想在更大的程序中使用它。将 dgets
的代码复制并粘贴到较大的程序中,并在使用 -Wall
成功编译后(1 个警告,gcc 未报告错误),运行 较大的程序导致 segmentation fault (core dumped)
.
用 gdb 检查核心文件会产生这个:
$ coredumpctl gdb 14527
PID: 14527 (a.out)
UID: 1000 (demiurge)
GID: 1000 (demiurge)
Signal: 11 (SEGV)
Timestamp: Wed 2015-08-05 15:48:28 CDT (1min 58s ago)
Command Line: ./a.out
Executable: /home/demiurge/learning_c/KR_exercises/chapter_1/a.out
Control Group: /user.slice/user-1000.slice/session-c1.scope
Unit: session-c1.scope
Slice: user-1000.slice
Session: c1
Owner UID: 1000 (demiurge)
Boot ID: 629b0a58691c4ec488bd0b84276df9d4
Machine ID: 4c9566d65e864c6da8e41bfaf8ed2cb4
Hostname: exmachina
Coredump: /var/lib/systemd/coredump/core.a\x2eout.1000.629b0a58691c4ec488bd0b84276df9d4.14527.1438807708000000.lz4
Message: Process 14527 (a.out) of user 1000 dumped core.
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f253e36cd80 in _int_realloc () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007f253e36cd80 in _int_realloc () from /usr/lib/libc.so.6
#1 0x00007f253e36e0f0 in realloc () from /usr/lib/libc.so.6
#2 0x0000000000400753 in dgets (lnptr=0x7ffeaf001050, lastIndex=40) at ./1.13.c:41
#3 0x00000000004009a2 in main () at ./1.13.c:107
(gdb) frame 3
#3 0x00000000004009a2 in main () at ./1.13.c:107
107 while( dgets(&line, SIZE) ) {
(gdb) frame 2
#2 0x0000000000400753 in dgets (lnptr=0x7ffeaf001050, lastIndex=40) at ./1.13.c:41
41 *lnptr = (char*)realloc(*lnptr, lastIndex);
对 gdb 的进一步检查确认段错误发生在 dgets
中第一次调用 realloc
的第二次迭代中。所以我的问题是:Why is realloc
triggering a segfault after dgets
passes unit testing?
大慈大悲的StackOverlord们帮我理解一下!
附录:
好的,下面是调用 dgets
之前运行的代码:
int main()
{
char* line;
int* lengths;
int longest;
line = (char*)calloc(SIZE, sizeof(char));
lengths = (int*)calloc(SIZE, sizeof(int));
//get a line from stdin
while( dgets(&line, SIZE) ) {
//count how many words are of length n
longest = countWordLengths(line, &lengths);
//do a little housekeeping
free((void*)line);
line = (char*)calloc(SIZE, sizeof(char));
if( line == NULL ) { return 1; }
}
.
.some other stuff happens here, but the code never gets this far.
.
}
好吧,这是给所有收到错误消息的新手们的
Get and use Valgrind. 这是一个免费的开源堆分析器,它将向您展示代码中堆损坏的位置,进而提供对这些堆损坏发生的位置和方式的一些见解。
在这种特殊情况下,我的代码中出现了多个堆损坏,这主要是因为传递给 realloc
的参数不正确。我想做的是调整一块存储整数的内存块的大小,如下所示:
*lnptr = (int*) realloc (*lnptr, newSize * sizeof(int));
但是我原来写的是这样的:
*lnptr = (int*) realloc (*lnptr, newSize);
看到 realloc
中的第二个参数了吗?这导致的问题是 *lnptr
最终只有 newSize
字节宽,如果我只需要存储 newSize char
就很好,如果我想存储 [=17 就很糟糕了=]s.
吸取教训;仔细阅读友好的手册。而且,如果您要动态分配内存,请使用 Valgrind。
我有一个函数 dgets
,其唯一目的是从 stdin
中获取任意长度的字符串。这是我用于单元测试的文件的全文:
#include <stdio.h>
#include <stdlib.h>
int dgets(char** lnptr, int lastIndex)
/*
RETRIEVE A LINE OF ARBITRARY LENGTH FROM STDIN
*/
{
char c;
int i = 0;
while( (c = getchar()) && c != EOF && c != '\n' ) {
if( i >= lastIndex-1 ) {
lastIndex *= 2;
*lnptr = (char*)realloc(*lnptr, lastIndex);
}
*(*lnptr + i) = c;
++i;
}
if( c == '\n' ) {
*(*lnptr + i) = c;
*lnptr = (char*)realloc(*lnptr, i+1);
*(*lnptr + i+1) = '[=10=]';
}
return i;
}
int main()
{
char* line;
line = (char*)calloc(10, sizeof(char));
while( dgets(&line, 10)) {
printf("****************\n%s****************\n", line);
free(line);
line = (char*)calloc(10, sizeof(char));
}
return 0;
}
该程序完全按照预期执行,错误或错误完全为零。这很好,因为我想在更大的程序中使用它。将 dgets
的代码复制并粘贴到较大的程序中,并在使用 -Wall
成功编译后(1 个警告,gcc 未报告错误),运行 较大的程序导致 segmentation fault (core dumped)
.
用 gdb 检查核心文件会产生这个:
$ coredumpctl gdb 14527
PID: 14527 (a.out)
UID: 1000 (demiurge)
GID: 1000 (demiurge)
Signal: 11 (SEGV)
Timestamp: Wed 2015-08-05 15:48:28 CDT (1min 58s ago)
Command Line: ./a.out
Executable: /home/demiurge/learning_c/KR_exercises/chapter_1/a.out
Control Group: /user.slice/user-1000.slice/session-c1.scope
Unit: session-c1.scope
Slice: user-1000.slice
Session: c1
Owner UID: 1000 (demiurge)
Boot ID: 629b0a58691c4ec488bd0b84276df9d4
Machine ID: 4c9566d65e864c6da8e41bfaf8ed2cb4
Hostname: exmachina
Coredump: /var/lib/systemd/coredump/core.a\x2eout.1000.629b0a58691c4ec488bd0b84276df9d4.14527.1438807708000000.lz4
Message: Process 14527 (a.out) of user 1000 dumped core.
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f253e36cd80 in _int_realloc () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007f253e36cd80 in _int_realloc () from /usr/lib/libc.so.6
#1 0x00007f253e36e0f0 in realloc () from /usr/lib/libc.so.6
#2 0x0000000000400753 in dgets (lnptr=0x7ffeaf001050, lastIndex=40) at ./1.13.c:41
#3 0x00000000004009a2 in main () at ./1.13.c:107
(gdb) frame 3
#3 0x00000000004009a2 in main () at ./1.13.c:107
107 while( dgets(&line, SIZE) ) {
(gdb) frame 2
#2 0x0000000000400753 in dgets (lnptr=0x7ffeaf001050, lastIndex=40) at ./1.13.c:41
41 *lnptr = (char*)realloc(*lnptr, lastIndex);
对 gdb 的进一步检查确认段错误发生在 dgets
中第一次调用 realloc
的第二次迭代中。所以我的问题是:Why is realloc
triggering a segfault after dgets
passes unit testing?
大慈大悲的StackOverlord们帮我理解一下!
附录:
好的,下面是调用 dgets
之前运行的代码:
int main()
{
char* line;
int* lengths;
int longest;
line = (char*)calloc(SIZE, sizeof(char));
lengths = (int*)calloc(SIZE, sizeof(int));
//get a line from stdin
while( dgets(&line, SIZE) ) {
//count how many words are of length n
longest = countWordLengths(line, &lengths);
//do a little housekeeping
free((void*)line);
line = (char*)calloc(SIZE, sizeof(char));
if( line == NULL ) { return 1; }
}
.
.some other stuff happens here, but the code never gets this far.
.
}
好吧,这是给所有收到错误消息的新手们的
Get and use Valgrind. 这是一个免费的开源堆分析器,它将向您展示代码中堆损坏的位置,进而提供对这些堆损坏发生的位置和方式的一些见解。
在这种特殊情况下,我的代码中出现了多个堆损坏,这主要是因为传递给 realloc
的参数不正确。我想做的是调整一块存储整数的内存块的大小,如下所示:
*lnptr = (int*) realloc (*lnptr, newSize * sizeof(int));
但是我原来写的是这样的:
*lnptr = (int*) realloc (*lnptr, newSize);
看到 realloc
中的第二个参数了吗?这导致的问题是 *lnptr
最终只有 newSize
字节宽,如果我只需要存储 newSize char
就很好,如果我想存储 [=17 就很糟糕了=]s.
吸取教训;仔细阅读友好的手册。而且,如果您要动态分配内存,请使用 Valgrind。