Realloc работает только дважды - PullRequest
0 голосов
/ 09 октября 2019

По какой-то причине, когда я перераспределяю массив, чтобы добавить элемент в массив, он работает только дважды перед сегментированием. Segfault происходит, когда я пытаюсь распечатать строки внутри массива. В настоящее время у меня есть массив, который NULL завершен.

void apparr(char** arr, char* line) {
    int length = 0;
    // find the length of the array
    while(arr[length] != NULL) {
        length++;
    }
    // realloc with 2 extra spaces (1 for line, 1 for NULL)
    arr = realloc(arr, sizeof(char*) * (length+2));
    // set last element (which was NULL) to line
    arr[length] = line;
    // set the NULL terminator
    arr[length+1] = NULL;
}

Я понятия не имею, где я могу пойти не так, мое единственное предположение будет о том, как я называю realloc. Тем не менее, я понимаю, что это не работает для 1 изменения размера, но я понятия не имею, почему это работает для двух изменений размера, а затем segfaults, когда я печатаю обратно массив.

Как это можно использовать в main:

int main(int argc, char** argv){
char** hist = malloc(sizeof(char**));
char* linep1;
char* linep2;
char* linep3;
char* linep4;
linep1 = (char*)malloc(strlen("test")*sizeof(char));
linep2 = (char*)malloc(strlen("test2")*sizeof(char));
linep3 = (char*)malloc(strlen("test3")*sizeof(char));
linep4 = (char*)malloc(strlen("test4")*sizeof(char));   
strcpy(linep1, "test");
strcpy(linep2, "test2");
strcpy(linep3, "test3");
strcpy(linep4, "test4");
apphist(hist, linep1);
apphist(hist, linep2);
//apphist(hist, linep3); //uncommenting this line causes nothing to be printed
//apphist(hist, linep4); //uncommenting this line causes only test4 to be printed
int x = 0;
while (hist[x] != NULL) {
    printf("%s\n", hist[x]);
    x++;
}
}

Ответы [ 5 ]

2 голосов
/ 09 октября 2019
  1. В основной функции вам нужно установить первый элемент hist как NULL, как вы проверяете его в функции apphist

    char** hist = malloc(sizeof(char*));
    *hist = NULL;
    
  2. Функция apphist только локально изменяет значение arr. Чтобы отразить изменения в основной функции, вам нужно передать указатель на arr, т. Е. На 3D-указатель char ***arr

  3. . Вы всегда должны проверять результат realloc ивыполнить действия при ошибке.

Код функции указан ниже.

void apparr(char*** arr2, char* line) {
    int length = 0;
    char **arr = *arr2;
    while(arr[length] != NULL) {
        length++;
    }
    arr = realloc(arr, sizeof(char*) * (length+2));
    if (arr == NULL) {
        exit(1); // handle error 
    }        
    *arr2 = arr;   
    arr[length] = line;
    arr[length+1] = NULL;
}
Кроме того, вы можете вернуть указатель на указатель на символ и обновить значение в main.
char** apparr(char** arr, char* line) {
    int length = 0;
    char **temp;
    while(arr[length] != NULL) {
        length++;
    }
    temp = realloc(arr, sizeof(char*) * (length+2));
    if (temp == NULL) {
        exit(1); // handle error 
    }
    arr = temp;
    arr[length] = line;
    arr[length+1] = NULL;
    return (arr);
}    

//in main
hist = apphist(hist, linep1);
hist = apphist(hist, linep2);
0 голосов
/ 09 октября 2019

Все ошибки и возможные подводные камни уже упоминались другими.

Ниже приведена более общая реализация функции append-element-to-array:

#include <stdlib.h>
#include <errno.h> /* for EINVAL */

int apparr(char *** parr, char * line) {
  size_t length = 0;

  if (NULL == *parr) {
    if (NULL != line) {
      errno = EINVAL;
      return -1;
    }
  } else {
    // find the length of the array
    while (NULL != (*parr)[length]) {
      ++length;
    }
  }

  {
    // realloc with 2 extra spaces (1 for line, 1 for NULL)
    void * pv = realloc(*parr, (length+1) * sizeof **parr);
    if (NULL == pv) {
      return -1; /* By convention -1 indicates failure. */
    }

    *parr = pv;
  }

  (*parr)[length] = line;
  if (0 < length) {
    (*parr)[length + 1] = NULL;
  }

  return 0; /* By convention 0 indicates success. */
}

И используйте еекак это:

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

int apparr(char *** parr, char * line) {

int main(int argc, char** argv) {
  char ** hist = NULL;
  char * linep1;
  char * linep2;
  char * linep3;
  char * linep4;

  if (-1 == apparr(&hist, NULL)) {
    perror("apphist() failed initially\n");
    exit(EXIT_FAILURE);
  }

  linep1 = malloc(strlen("test") + 1);
  linep2 = malloc(strlen("test2") + 1); /* +1 for the c-string's 0-termination; sizeof (char) is 1 by definition */
  linep3 = malloc(strlen("test3") + 1);
  linep4 = malloc(strlen("test4") + 1);

  strcpy(linep1, "test");
  strcpy(linep2, "test2");
  strcpy(linep3, "test3");
  strcpy(linep4, "test4");

  if (-1 == apphist(&hist, linep1)) {
    perror("apphist() failed for line 1\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep2) {
    perror("apphist() failed for line 2\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep3) {
    perror("apphist() failed for line 3\n");
    exit(EXIT_FAILURE);
  }

  if (-1 == apphist(&hist, linep4)  {
    perror("apphist() failed for line 4\n");
    exit(EXIT_FAILURE);
  }

  {
    size_t x = 0;
    while (hist[x] != NULL) {
      printf("%s\n", hist[x]);
      ++x;
    }
  }
}
0 голосов
/ 09 октября 2019

В вашем коде есть несколько ошибок:

  1. Это ваша основная ошибка. Вы не заменяете значение в указанном указателе. Правильно использовать указатель на указатель, но вам нужно разыменовать его. Для этого вам нужно передать указатель на hist и разыменовать его при перераспределении:

    *arr = realloc(*arr, sizeof(char*) * (length + 2));
    
  2. Список указателей не инициализирован, после первого выделения вам нужнодля установки первого указателя:

    hist[0] = NULL;
    
  3. Выделены 1 строки для ваших тестовых строк:

    linep1 = malloc((strlen("test") + 1) * sizeof(char));
    linep2 = malloc((strlen("test2") + 1) * sizeof(char));
    linep3 = malloc((strlen("test3") + 1) * sizeof(char));
    linep4 = malloc((strlen("test4") + 1) * sizeof(char));
    

Дополнительные примечания:

  • Включения отсутствуют для полного минимального воспроизводимого примера .
  • Имя apparr() неверно, вы набираете apphist() в main().
  • Проверьте возвращаемые значения любого выделения для NULL, что означает, что выделение не удалось.
  • Вы не используете argc и argv, поэтому пишите int main(void)
  • Первое распределение имеет «неправильный» тип, но оба являются указателями, поэтому он одинакового размера: char** hist = malloc(sizeof(char*));
  • Нет необходимости приводить указатели, возвращаемые malloc(), так как он возвращает указатель на void. Указатели на void и другие указатели можно назначать туда и обратно без приведения.
  • Вы можете заменить пары malloc() / strcpy() на strdup().
  • Вы можете даже позвонитьapphist() со строкой в ​​качестве "немедленного" значения, подобного этому: apphist(hist, "test");
  • main() должно возвращать int, EXIT_SUCCESS - правильное значение.
  • Вы можете поставитьнекоторые const в параметрах и объявлениях, чтобы сделать вещи более безопасными. Но подумайте о , что является постоянным.
0 голосов
/ 09 октября 2019

В общем случае, я думаю, вам нужно только вызвать realloc с длиной + 1

arr = realloc(arr, sizeof(char*) * (length+1));

Это потому, что у вас уже есть место для завершающего указателя NULL из предыдущего состояния. С кодом, который вы предлагаете, происходит следующее:

// состояние до realloc

String String NULL

// вызов apparr ()

String String NULL неопределенный undefined // realloc

String String String неопределенный undefined // arr [length] = line;

String String String NULL undefined // arr [length + 1] = NULL;

Первый раз, когда он будет работать (оставляя висячий распределенный узел в конце), но во второй раз он может произойти сбой несколькими способами из-за дополнительного выделения.

0 голосов
/ 09 октября 2019

Я думаю, вам следует разыменовать arr перед использованием в realloc. Другое наблюдение;sizeof (char *) обычно составляет 4 в 32-битной архитектуре и 8 в 64-битной вместо 1.

...