【问题标题】:Mergesort - Segmentation faultMergesort - 分段错误
【发布时间】:2017-05-17 00:24:44
【问题描述】:

代码目录结构,

./Computing$ ls -LR
.:
list file.txt  mergeSort.c  program.exe type.h

./list:
arrayImpl.c  linkedListImpl.c  list.h

编译过程:

$./Computing
gcc -Wall -Werror -DARRAY -I. mergeSort.c ./list/*.c -o program

Here 是包含文件的完整代码,mergeSort.clist/*type.h

List的给定表示,

typedef struct List{
  void **array;

  /* Housekeeping - Array enhancement/shrink */
  int lastItemPosition;
  int size;
}List;

下面在list->array上执行合并排序,其中aux维护array的浅拷贝

void mergeSort(List *list, size_t listSize, isLess less){

  if(list != NULL){

    void **aux = malloc(list->size * sizeof(void*)); //Auxillary shadow copy
    if(aux != NULL){
      printf("Size of list: %d\n", listSize);
      mSort(list->array, aux, 0, listSize-1, less);
    }else{

      fprintf(stderr, "mergeSort() - Malloc failure");
      exit(EXIT_FAILURE);
    }
  }else{

    fprintf(stderr, "mergeSort() - List is NULL");
  }
}

static void mSort(void **array, void **aux, int low, int high, isLess less){

  if(high <= low) return;
  int mid = (low + high)/2;

  mSort(array, aux, low, mid, less);
  mSort(array, aux, mid+1, high, less);
  merge(array, aux, low, mid, high, less);
}

static void merge(void **array, void **aux, int low, int mid, int high, isLess less){

  for(int index = 0; index <= high; index++){
    aux[index] = array[index]; //Shallow copy
  }
  printf("Low-%d, Mid-%d, High-%d\n", low, mid, high);
  int leftIndex = low; int rightIndex = mid+1;
  printf("leftIndex-%d, rightIndex-%d\n", leftIndex, rightIndex);

  for(int index = 0; index <= high; index++){

    if(leftIndex > mid)    /* right array exhausted */  array[index] = aux[rightIndex++];
    else if(rightIndex > high) /*left array exhausted*/ array[index] = aux[leftIndex++];
    else if( less(aux[rightIndex], aux[leftIndex]) )    array[index] = aux[rightIndex++];
    else                                                array[index] = aux[leftIndex++];
  }

}

使用代码在哪里,


bool less(const void *key, const void *item){
  printf("\nIn less function\n");
  printf("left-%d, Right-%d\n\n", ((Record *)key)->age, ((Record *)item)->age);
  if( ((Record *)key)->age < ((Record *)item)->age ){
    printf("Return true\n");
    return true;
  }else{
    printf("Return false\n");
    return false;
  }
}

int main(){
  FILE *pFile = fopen("file.txt", "r");
  checkHandleFailure(pFile, FILE_HANDLE_FAILURE);
  char buf[MAX_RECORD_SIZE];
  DBCache *cache = initCache(pFile);


  readHeader(pFile, cache, buf);

  readData(pFile, cache, buf);

  printRecords(cache);

  printf("Before calling mergesort() \n");
  mergeSort(cache->records, listGetSize(cache->records), less);
}


实际输出为:

$ ./program.exe
Age,LastName,FirstName
------------------------------
50,B,A

30,A,B

20,X,D

10,F,A

90,V,E

60,N,M

Records#: 6
Before calling mergesort()
Size of list: 6
Low-0, Mid-0, High-1
leftIndex-0, rightIndex-1

In less function
left-30, Right-50

Return true
Low-0, Mid-1, High-2
leftIndex-0, rightIndex-2

In less function
left-20, Right-30

Return true
Low-3, Mid-3, High-4
leftIndex-3, rightIndex-4

In less function
left-90, Right-10

Return false
Low-3, Mid-4, High-5
leftIndex-3, rightIndex-5
Segmentation fault (core dumped)
In less function


如何解决这个问题?

Cygwin 不支持使用 gdb 的 coredump 跟踪格式,提供了跟踪(上图)

【问题讨论】:

  • 如果您发布相关的堆栈跟踪将更容易提供帮助
  • @yankee2905 请在查询中阅读我的注释(下方)。我可以分享完整的代码
  • 我们无法知道cache-&gt;records 是否有效。为什么使用void?为什么要发送两次大小,list-&gt;sizelistSize
  • @Stargateur 因为List 是接受任何特定类型的订购项目的抽象。 List&lt;T&gt; 在 java 中。 Kya kare bhai,面试官在 Arista 网络上提出这样的问题
  • @overexchange 为什么要两颗星? void * 在这种情况下就足够了。

标签: c sorting pointers mergesort double-pointer


【解决方案1】:

合并中的两个 for 循环应该从低开始,而不是从零开始。从 0 开始的第二个 for 循环可能会导致分段错误。从 0 开始的第一个 for 循环不应导致分段错误,但会消耗额外的时间。

static void merge(....)

    /* ... */

    for(int index = low; index <= high; index++){   // low not 0
        aux[index] = array[index];
    }

    /* ... */

    for(int index = low; index <= high; index++){   // low not 0
        if(leftIndex > mid) /* ... */
        /* ... */

【讨论】:

  • 在您建议更改后,是的代码现在正在运行
【解决方案2】:

要进行通用合并排序,您不能使用void **,因为您也会强制用户使用嵌套指针。所以你的实现在这里不是通用的。

我建议你一个使用指针算法的实现,它不容易理解。但我认为这比使用int 更好。我在比较函数中更改的函数较少,因为它在 C 中更惯用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef int msort_cmp(void const *a, void const *b);

static void msort_merge(char *begin, char const *end, char *copy_begin,
                        char *copy_mid, char const *copy_end, size_t size,
                        msort_cmp *cmp) {
  memcpy(copy_begin, begin, (size_t)(end - begin));

  for (char *right = copy_mid; begin < end; begin += size) {
    if (copy_begin >= copy_mid) {
      memcpy(begin, right, size);
      right += size;
    } else if (right >= copy_end) {
      memcpy(begin, copy_begin, size);
      copy_begin += size;
    } else if (cmp(right, copy_begin) > 0) {
      memcpy(begin, right, size);
      right += size;
    } else {
      memcpy(begin, copy_begin, size);
      copy_begin += size;
    }
  }
}

static void msort_partition(char *begin, char const *end, char *copy,
                            size_t size, msort_cmp *cmp) {
  size_t bytes = (size_t)(end - begin);
  if (bytes > size) {
    size_t tmp = bytes / 2;
    size_t offset = tmp - tmp % size;

    char *mid = begin + offset;
    msort_partition(begin, mid, copy, size, cmp);
    msort_partition(mid, end, copy, size, cmp);

    msort_merge(begin, end, copy, copy + offset, copy + bytes, size, cmp);
  }
}

static int msort_aux(char *begin, char const *end, size_t size,
                     msort_cmp *cmp) {
  char *copy = malloc((size_t)(end - begin));
  if (copy == NULL) {
    return 1;
  }

  msort_partition(begin, end, copy, size, cmp);

  return 0;
}

static int msort(void *begin, void const *end, size_t size, msort_cmp *cmp) {
  return msort_aux(begin, end, size, cmp);
}

static int cmp_int_aux(int const *a, int const *b) {
  if (*a < *b) {
    return 1;
  } else if (*a > *b) {
    return -1;
  } else {
    return 0;
  }
}

static int cmp_int(void const *a, void const *b) { return cmp_int_aux(a, b); }

static void print_int(int const *array, size_t size) {
  printf("array: ");
  for (size_t i = 0; i < size; i++) {
    printf(" %d", array[i]);
  }
  printf("\n");
}

static int cmp_pint(void const *a, void const *b) {
  return cmp_int(*(int *const *)a, *(int *const *)b);
}

static void print_pint(int *const *parray, size_t size) {
  printf("parray: ");
  for (size_t i = 0; i < size; i++) {
    printf(" %d", *parray[i]);
  }
  printf("\n");
}

#define SIZE 42

int main(void) {
  int array[SIZE];

  srand((unsigned int)time(NULL));
  for (size_t i = 0; i < SIZE; i++) {
    array[i] = rand() % SIZE - SIZE / 2;
  }

  print_int(array, SIZE);
  msort(array, array + SIZE, sizeof *array, &cmp_int);
  print_int(array, SIZE);

  int *parray[SIZE];
  for (size_t i = 0; i < SIZE; i++) {
    array[i] = rand() % SIZE - SIZE / 2;
    parray[i] = array + i;
  }

  print_pint(parray, SIZE);
  msort(parray, parray + SIZE, sizeof *parray, &cmp_pint);
  print_pint(parray, SIZE);
}

【讨论】:

  • 看到cmp()我可以说你的代码不是通用的
  • 为什么用户(main()) 会关心parrayparray+SIZE 之间的内存大小?您正在公开实施细节
  • User(main()) 只是假设知道为List 中的每个元素分配多少内存。 List 抽象不应该关心,1)哪个对象进来了? 2) 为该对象数组分配多少内存(merge_aux())?
  • 我们学习写抽象的最好方法是批评code。相信我,继续前进
  • @overexchange 仔细查看int (*msort_cmp)(void const *a, void const *b); 和演员(msort_cmp)&amp;cmp。我将此实现与intint * 一起使用,您是否在说它不是通用的之前阅读了所有内容并进行了测试?在我看来,我没有公开实现细节,比如 C++ 中的算法。我负责开始和结束,我可以做其他事情,很容易改变。您不清楚您的第三条评论。抱歉,但我认为我的代码比你的要好得多,相信我。停止垃圾评论,只做一个!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多