简体   繁体   English

qsort() 导致分段错误

[英]qsort() results in segmentation fault

I've been tasked with implementing a type safe dynamic vector structure in C;我的任务是在 C 中实现类型安全的动态向量结构; however, I seem to have a problem: Every time I use the qsort() and then try casting the variables to an int* (in both erase_value() and print_vector_int() ) I get a segmentation fault.但是,我似乎有一个问题:每次我使用qsort()然后尝试将变量转换为int* (在erase_value()print_vector_int()中)我都会遇到分段错误。

Example input:示例输入:

4
p 3
i 0 10
e 0 20
p 4

This works as intended but, as soon as I use qsort() , either the erase_value() or print_vector_int() break.这可以按预期工作,但是一旦我使用qsort() ,则erase_value()print_vector_int()中断。

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

#define MAX_STR_LEN 64

typedef struct Vector {
    void **data;
    size_t element_size;
    size_t size;
    size_t capacity;
} Vector;

// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void init_vector(Vector *vector, size_t block_size, size_t element_size) {
    vector->data = (void **)malloc(sizeof(void *) * block_size);
    vector->element_size = element_size;
    vector->capacity = block_size;
    vector->size = 0;
}

// If new_capacity is greater than the current capacity,
// new storage is allocated, otherwise the function does nothing.
void reserve(Vector *vector, size_t new_capacity) {
    if (vector->capacity < new_capacity) {
        vector->capacity = new_capacity;
        vector->data = (void **)realloc(vector->data, sizeof(void *) * (vector->capacity));
    }
}

// Resizes the vector to contain new_size elements.
// If the current size is greater than new_size, the container is
// reduced to its first new_size elements.

// If the current size is less than new_size,
// additional zero-initialized elements are appended
void resize(Vector *vector, size_t new_size) {
    if (new_size <= vector->size) {
        for (size_t i = vector->size; i > new_size; --i) {
            free(vector->data[i]);
        }
        vector->size = new_size;
    } else {
        if (new_size > vector->capacity) {
            vector->capacity = (vector->capacity) * 2;
            vector->data = (void **)realloc(vector->data, sizeof(void *) * vector->capacity);
        }
        for (size_t i = vector->size; i < new_size; ++i) {
            vector->data[i] = (void *)calloc(1, vector->element_size);
        }
        vector->size = new_size;
    }
}

// Insert new element at index (0 <= index <= size) position
void insert(Vector *vector, int index, void *value) {
    if (index >= vector->capacity) {
        vector->capacity = vector->capacity * 2;
        vector-> data = (void **)realloc(vector->data, sizeof(void *) * (vector->capacity));
    }
    
    if (index >= 0 && index <= vector->size) {
        vector->data[vector->size] = (void *)malloc(vector->element_size);
       for (size_t i = vector->size; i > index; --i) {
           memcpy(vector->data[i], vector->data[i - 1], vector->element_size);
       }
       memcpy(vector->data[index], value, vector->element_size);
       ++(vector->size);
    }
}

// Add element to the end of the vector
void push_back(Vector *vector, void *value) {
    if (vector->size == 0)
        insert(vector, 0, value);
    else
        insert(vector, vector->size, value);
}

// Remove all elements from the vector
void clear(Vector *vector) {
    for (int i = (vector->size) - 1; i >= 0; --i) {
        free(vector->data[i]);
    }
    vector->size = 0;
}

// Erase element at position index
void erase(Vector *vector, int index) {
    for (size_t i = index; i < vector->size - 1; ++i) {
        memcpy(vector->data[i], vector->data[i + 1], vector->element_size);
    }
    free(vector->data[vector->size - 1]);
    --(vector->size);
}

// Erase all elements that compare equal to value from the container
void erase_value(Vector *vector, void *value, int(*cmp)(const void *, const void *)) {
    for(size_t i = 0; i < vector->size; ++i) {
        if (cmp(vector->data[i], value) == 0)
            erase(vector, i);
    }
}

// Erase all elements that satisfy the predicate from the vector
void erase_if(Vector *vector, int (*predicate)(void *)) {
    for (size_t i = 0; i < vector->size; ++i) {
        if (predicate(vector->data[i]))
            erase(vector, i);
    }
}

// Request the removal of unused capacity
void shrink_to_fit(Vector *vector) {
    vector->capacity = vector->size;
    vector->data = (void **)realloc(vector->data, sizeof(void *) * (vector->capacity));
}

// Print integer vector
void print_vector_int(Vector *vector) {
    printf("%ld\n", vector->capacity);
    for (int i = 0;i < vector->size; ++i) {
        int item = *(int *)vector->data[i];
        printf("%d ", item);
    }
}

int int_cmp(const void *v1, const void *v2) {
    const int aa = *(const int *)v1;
    const int bb = *(const int *)v2;
    return aa - bb;
}

int is_even(void *value) {
    const int aa = *(int *)value;
    return aa % 2 == 0;
}

void read_int(void *value) {
    scanf("%d", (int *)value);
}

void vector_test(Vector *vector, int n, void (*read)(void *),
         int (*cmp)(const void *, const void *), int (*predicate)(void *)) {
    char op[2];
    int index;
    size_t size;
    void *v = malloc(vector->element_size);
    for (int i = 0; i < n; ++i) {
        scanf("%s", op);
        switch (op[0]) {
          case 'p': // push_back
            read(v);
            push_back(vector, v);
            break;
          case 'i': // insert
            scanf("%d", &index);
            read(v);
            insert(vector, index, v);
            break;
          case 'e': // erase
            scanf("%d", &index);
            read(v);
            erase(vector, index);
            erase_value(vector, v, cmp);
            break;
          case 'd': // erase (predicate)
            erase_if(vector, predicate);
            break;
          case 'r': // resize
            scanf("%zu", &size);
            resize(vector, size);
            break;
          case 'c': // clear
            clear(vector);
            break;
          case 'f': // shrink
            shrink_to_fit(vector);
            break;
          case 's': // sort
            qsort(vector->data, vector->size,
                  vector->element_size, cmp);
            break;
          default:
            printf("No such operation: %s\n", op);
            break;
        }
    }
    free(v);
}

int main(void) {
    int n;
    Vector vector_int;
    scanf("%d", &n);
    init_vector(&vector_int, 4, sizeof(int));
    vector_test(&vector_int, n, read_int, int_cmp, is_even);
    print_vector_int(&vector_int);
    free(vector_int.data);
    return 0;
}

I suspect it might be a problem with the int_cmp() , since q-sorting a sorted list does not result in a seg-fault.我怀疑这可能是int_cmp()的问题,因为对排序列表进行 q 排序不会导致段错误。

There are multiple problems in your code:您的代码中有多个问题:

  • you use qsort on an array of pointers to allocated blocks, not an array of elements.您在指向已分配块的指针数组上使用qsort ,而不是元素数组。 The geometry of your vector is inappropriate for qsort with the comparison function as coded.您的向量的几何形状不适合使用编码的比较函数的qsort

  • in function resize , the loop to free the elements starts at vector->size and accesses the element at this offset, which was not allocated.在函数resize中,释放元素的循环从vector->size开始,并访问此偏移处的元素,该偏移未分配。 The correct way to write a downward loop is:编写向下循环的正确方法是:

     for (size_t i = vector->size; i-- > new_size;) { free(vector->data[i]); }
  • in erase_value and erase_if you should not increment i when the matching element is deleted.erase_valueerase_if中,当匹配元素被删除时,你不应该增加i

  • int_cmp should not rely on subtracting the values: this subtraction can overflow for large values of opposite signs. int_cmp不应该依赖于减去这些值:对于相反符号的大值,这种减法可能会溢出。

You should change the implementation of you vector to allocate an array of elements instead of an array of pointers to allocated arrays of bytes.您应该更改向量的实现以分配元素数组而不是指向已分配字节数组的指针数组。

Here is a modified version:这是修改后的版本:

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

typedef struct Vector {
    void *data;
    size_t element_size;
    size_t size;
    size_t capacity;
} Vector;

#define VP(v, i)  ((void *)((unsigned char *)(vector)->data + (i) * (vector)->element_size))

// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void init_vector(Vector *vector, size_t capacity, size_t element_size) {
    vector->data = calloc(capacity, element_size);
    vector->element_size = element_size;
    vector->capacity = capacity;
    vector->size = 0;
}

// Free vector data
void free_vector(Vector *vector) {
    free(vector->data);
    vector->data = NULL;
    vector->size = vector->capacity = 0;
}

// If new_capacity is greater than the current capacity,
// new storage is allocated, otherwise the function does nothing.
void reserve(Vector *vector, size_t new_capacity) {
    if (vector->capacity < new_capacity) {
        vector->data = realloc(vector->data, new_capacity * vector->element_size);
        vector->capacity = new_capacity;
    }
}

// Resizes the vector to contain new_size elements.
// If the current size is greater than new_size, the container is
// reduced to its first new_size elements.
// If the current size is less than new_size,
// additional zero-initialized elements are appended
void resize(Vector *vector, size_t new_size) {
    if (new_size > vector->size) {
        if (new_size > vector->capacity) {
            size_t new_capacity = vector->capacity * 2;
            while (new_size > new_capacity) {
                new_capacity = new_capacity * 2;
            }
            vector->data = realloc(vector->data, new_capacity * vector->element_size);
            vector->capacity = new_capacity;
        }
        memset(VP(vector, vector->size), 0, (new_size - vector->size) * vector->element_size);
    }
    vector->size = new_size;
}

// Insert new element at index (0 <= index <= size) position
void insert(Vector *vector, size_t index, void *value) {
    if (index <= vector->size) {
        if (vector->size == vector->capacity) {
            resize(vector, vector->size + 1);
        }
        memmove(VP(vector, index + 1), VP(vector, index),
                (vector->size - index) * vector->element_size);
        memmove(VP(vector, index), value, vector->element_size);
        vector->size += 1;
    }
}

// Add element to the end of the vector
void push_back(Vector *vector, void *value) {
    insert(vector, vector->size, value);
}

// Remove all elements from the vector
void clear(Vector *vector) {
    vector->size = 0;
}

// Erase element at position index
void erase(Vector *vector, size_t index) {
    if (index < vector->size) {
        memmove(VP(vector, index), VP(vector, index + 1),
                (vector->size - index - 1) * vector->element_size);
        vector->size -= 1;
    }
}

// Erase all elements that compare equal to value from the container
void erase_value(Vector *vector, void *value, int (*cmp)(const void *, const void *)) {
    for (size_t i = 0; i < vector->size;) {
        if (cmp(VP(vector, i), value) == 0) {
            erase(vector, i);
        } else {
            i++;
        }
    }
}

// Erase all elements that satisfy the predicate from the vector
void erase_if(Vector *vector, int (*predicate)(void *)) {
    for (size_t i = 0; i < vector->size;) {
        if (predicate(VP(vector, i))) {
            erase(vector, i);
        } else {
            i++;
        }
    }
}

// Print all elements of the vector
void print_vector(Vector *vector, void (*print)(void *)) {
    printf("%zu %zu {", vector->size, vector->capacity);
    for (size_t i = 0; i < vector->size; i++) {
        printf(" ");
        print(VP(vector, i));
    }
    printf(" }\n");
}

// Request the removal of unused capacity
void shrink_to_fit(Vector *vector) {
    vector->capacity = vector->size;
    vector->data = realloc(vector->data, vector->capacity * vector->element_size);
}

// Print integer vector
void int_print(void *p) {
    int item = *(int *)p;
    printf("%d", item);
}

int int_cmp(const void *v1, const void *v2) {
    const int aa = *(const int *)v1;
    const int bb = *(const int *)v2;
    return (aa > bb) - (aa < bb);
}

int is_even(void *value) {
    const int aa = *(const int *)value;
    return aa % 2 == 0;
}

void read_int(void *value) {
    int *vp = (int *)value;
    *vp = 0;
    scanf("%d", vp);
}

void vector_test(Vector *vector, int n,
                 void (*read)(void *),
                 int (*cmp)(const void *, const void *),
                 int (*predicate)(void *),
                 void (*print)(void *))
{
    char op[2];
    size_t index, size;
    void *v = malloc(vector->element_size);
    for (int i = 0; i < n; ++i) {
        if (scanf("%1s", op) != 1)
            break;
        switch (op[0]) {
        case 'p': // push_back
            read(v);
            push_back(vector, v);
            break;
        case 'i': // insert
            scanf("%zu", &index);
            read(v);
            insert(vector, index, v);
            break;
        case 'e': // erase
            scanf("%zu", &index);
            read(v);
            erase(vector, index);
            erase_value(vector, v, cmp);
            break;
        case 'd': // erase (predicate)
            erase_if(vector, predicate);
            break;
        case 'r': // resize
            scanf("%zu", &size);
            resize(vector, size);
            break;
        case 'c': // clear
            clear(vector);
            break;
        case 'f': // shrink
            shrink_to_fit(vector);
            break;
        case '=': // print
            print_vector(vector, print);
            break;
        case 's': // sort
            qsort(vector->data, vector->size,
                  vector->element_size, cmp);
            break;
        default:
            printf("No such operation: %s\n", op);
            break;
        }
    }
    free(v);
}

int main() {
    Vector vector_int[1];
    int n = 0;

    if (scanf("%d", &n) == 1) {
        init_vector(vector_int, 4, sizeof(int));
        vector_test(vector_int, n, read_int, int_cmp, is_even, int_print);
        print_vector(vector_int, int_print);
        free_vector(vector_int);
    }
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM