当我编译并运行这种选择的随机字符数组时,我得到一个分段错误(核心转储)错误。我认为这与在我的选择排序部分中访问未分配的内存有关。有人可以帮忙吗?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRING_LEN 80
#define ARRAY_LEN 10000
void *emalloc(size_t s) {
void *result = malloc(s);
if (NULL == result) {
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
void selection_sort(char *words[], int n) {
char *temp;
int i, j;
for (i = 0; i < n; i++) {
for (j = i+1; i < n; j++) {
if (strcmp(words[i], words[j]) < 0) {
temp = words[i];
words[i] = words[j];
words[j] = temp;
}
}
}
}
int main(void) {
char word[STRING_LEN];
char *wordlist[ARRAY_LEN];
int num_words;
int i;
num_words = 0;
while (num_words < ARRAY_LEN && 1 == scanf("%79s", word)) {
wordlist[num_words] = emalloc((strlen(word) + 1) * sizeof wordlist[0][0]);
strcpy(wordlist[num_words], word);
num_words++;
}
selection_sort(wordlist, num_words);
for (i = 0; i < num_words; i++) {
printf("%s\n", wordlist[i]);
}
for (i = 0; i < num_words; i++) {
free(wordlist[i]);
}
return EXIT_SUCCESS;
}
答案 0 :(得分:3)
我不是只指出你简单但难以发现的错误,而是指导我如何调试它,以便你可以学习如何使用调试器并自己解决这些类型的问题(正如俗话所说,教一个人钓鱼。
首先,我编译了你的代码(警告级别被提升),并确保我可以重现这个问题:
$ clang test.c -o test -Wall -Wextra -pedantic # No warnings, great!
$ echo -e 'foo\nbar\nbaz' | ./test
Segmentation fault: 11
好吧,它崩溃了。这个问题是可以重现的。现在,使用调试符号(-g
)重新编译并设置测试向量:
$ clang test.c -o test -Wall -Wextra -pedantic -g
$ echo -e 'foo\nbar\nbaz' > input
现在运行调试器,看看会发生什么。我在Mac OS X上使用LLDB,但完全相同的命令可以在GDB中使用:
$ lldb ./test
Current executable set to './test' (x86_64).
(lldb) run < input
Process 68367 launched: './test' (x86_64)
Process 68367 stopped
* thread #1: tid = 0x553a95, 0x00007fff918e0db5 libsystem_platform.dylib`_platform_strcmp + 181, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x00007fff918e0db5 libsystem_platform.dylib`_platform_strcmp + 181
libsystem_platform.dylib`_platform_strcmp + 181:
-> 0x7fff918e0db5: movdqu (%rsi,%rcx), %xmm1
0x7fff918e0dba: pcmpeqb %xmm1, %xmm0
0x7fff918e0dbe: pcmpeqb %xmm2, %xmm1
0x7fff918e0dc2: pandn %xmm0, %xmm1
我们在空指针(strcmp()
部分)上的address=0x0
函数内部崩溃了。现在我们是如何到达那里的?为什么会这样?这是堆栈跟踪:
(lldb) bt
* thread #1: tid = 0x553a95, 0x00007fff918e0db5 libsystem_platform.dylib`_platform_strcmp + 181, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
* frame #0: 0x00007fff918e0db5 libsystem_platform.dylib`_platform_strcmp + 181
frame #1: 0x0000000100000c96 test`selection_sort(words=0x00007fff5fbec1a0, n=3) + 86 at test.c:23
frame #2: 0x0000000100000e03 test`main + 243 at test.c:45
让我们前往我们称之为strcmp()
的地方:
(lldb) up 1
frame #1: 0x0000000100000c96 test`selection_sort(words=0x00007fff5fbec1a0, n=3) + 86 at test.c:23
20
21 for (i = 0; i < n; i++) {
22 for (j = i+1; i < n; j++) {
-> 23 if (strcmp(words[i], words[j]) < 0) {
24 temp = words[i];
25 words[i] = words[j];
26 words[j] = temp;
现在让我们看看我们呼叫strcmp()
的内容(p
是print
的简写):
(lldb) p words[i]
(char *) $0 = 0x0000000100103920 "foo"
(lldb) p words[j]
(char *) $1 = 0x0000000000000000
Welp,这是我们的空指针。那是怎么发生的?什么是i
和j
?
(lldb) p i
(int) $2 = 0
(lldb) p j
(int) $3 = 3
哦不! j
是3,但它不应该是3或更多,因为n
是3(因为我们在输入文件中有3行)。我们的循环不应该阻止j
超过n
...?仔细看看:
22 for (j = i+1; i < n; j++) {
糟糕 - 这就是错误。当它应该是i < n
时,循环测试为j < n
。 j
无限制地增长,直到它崩溃或发生其他不良事件(如果输入数据是专门用来利用它的话,就像攻击者接管你的系统一样)。
以下是一些有助于了解如何使用命令行调试程序(如LLDB和GDB)的链接: