选择排序指针char数组的分段错误(核心转储)错误

时间:2014-08-19 02:56:01

标签: c pointers arrays selection-sort

当我编译并运行这种选择的随机字符数组时,我得到一个分段错误(核心转储)错误。我认为这与在我的选择排序部分中访问未分配的内存有关。有人可以帮忙吗?

#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;
}

1 个答案:

答案 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()的内容(pprint的简写):

(lldb) p words[i]
(char *) $0 = 0x0000000100103920 "foo"
(lldb) p words[j]
(char *) $1 = 0x0000000000000000

Welp,这是我们的空指针。那是怎么发生的?什么是ij

(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 < nj无限制地增长,直到它崩溃或发生其他不良事件(如果输入数据是专门用来利用它的话,就像攻击者接管你的系统一样)。

以下是一些有助于了解如何使用命令行调试程序(如LLDB和GDB)的链接: