Записи массива перезаписываются в C - PullRequest
0 голосов
/ 08 ноября 2019

Я хочу преобразовать целочисленный массив в строковый массив. Например, если arr[] = {1, 2, 3, 4, 5}, я хочу получить arr2[] = {"1", "2", "3", "4", "5"}. Эта функция работает нормально, пока не выйдет из цикла for, где все записи массива перезаписываются значением последней записи. Есть идеи, почему это может происходить?

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

#define SIZE 5
int main(){
    int nums[] = {1, 2, 3, 4, 5};
    char **strings = (char **)malloc(SIZE * sizeof(char*));

    for(int i = 0; i < SIZE; i++){
        char temp[20];
        sprintf(temp, "%d", nums[i]);
        strings[i] = temp;
        //prints correct values here
        printf("%s\n", strings[i]);
    }

    for(int i = 0; i < SIZE; i++){
        //prints wrong values here
        printf("%s\n", strings[i]);
    }

    free(strings);
    return 0;
}

Ответы [ 3 ]

3 голосов
/ 08 ноября 2019

Проблема strings[i] = temp;. Это присваивает temp strings[i], но тогда temp является локальной переменной, ограниченной для блока цикла, и недопустимо после завершения блока.

Вам потребуется malloc память длякаждая строка содержит скопированное значение (и free, когда оно будет сделано). tmp также не требуется, поскольку мы можем sprintf непосредственно в указатель.

SIZE = 5, но ваш массив имеет только 4 члена, поэтому у нас есть доступ за пределами. Я предпочел бы охватить это данными, которые он представляет, а не сделать их глобальной константой. Я также предполагаю, что этот массив будет обрабатывать произвольные данные, потому что как есть, у него нет никаких преимуществ перед использованием i + 1 внутри вашего цикла.

malloc(12) - достаточно места для хранения 32-битного целогострока (sizeof char всегда 1, и нам нужно место для символов '-' и '\0'). Как указано в этом комментарии , вы можете использовать sizeof(int) * CHAR_BIT / 3 + 2 для вычисления правильного размера буфера, где CHAR_BIT определено в заголовке limits.h.

В качестве отступления, нет необходимости разыгрывать malloc, и рекомендуется использовать sizeof(*strings) в случае изменения типа указателя во время рефакторинга.

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

int main() {
    int nums[] = {1, 2, 3, 4};
    int nums_size = 4;
    char **strings = malloc(nums_size * sizeof(*strings));

    for (int i = 0; i < nums_size; i++) {
        strings[i] = malloc(12);
        sprintf(strings[i], "%d", nums[i]);
    }

    for (int i = 0; i < nums_size; i++) { 
        printf("%s\n", strings[i]);
        free(strings[i]);
    }

    free(strings);
    return 0;
}
2 голосов
/ 08 ноября 2019

Всегда позволяйте компилятору выполнять подсчет за вас.

Сначала я определю ниже макрос COUNTOF, который производит количество элементов (а не число байтов ) в объекте массива;это должно использоваться везде.

Во-вторых, библиотечная функция strdup() создает копию строкового буфера - она ​​считает длину, выделяет нужное количество байтов, а затем копирует в нее строку. Гораздо проще, чем кататься самостоятельно.

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

#define COUNTOF(x)   ( sizeof(x) / sizeof((x)[0]) )

int main(){
    int nums[] = {1, 2, 3, 4};
    char **strings = malloc(COUNTOF(nums) * sizeof(*strings));

    for(int i = 0; i < COUNTOF(nums); i++){
        char temp[40];  // bigger than any plausible int
        sprintf(temp, "%d", nums[i]);
        strings[i] = strdup(temp);   // HERE
        printf("%s\n", strings[i]);
    }

    for(int i = 0; i < COUNTOF(nums); i++){
        //now prints correct value here :-)
        printf("%s\n", strings[i]);
    }

    return 0;
}
0 голосов
/ 08 ноября 2019

На самом деле нет необходимости делать это во время выполнения. Вы можете сделать это во время компиляции, если у вас могут возникнуть некоторые трюки с «X-макросом».

#define INIT_LIST \
   X(1) \
   X(2) \
   X(3) \
   X(4) \

#define STR(n) #n

#include <stdio.h>

int main (void)
{
  #define X(n) n,
    int nums[] = {INIT_LIST};
  #undef X

  #define X(n) STR(n),
    const char* str[] = {INIT_LIST};
  #undef X

  for(size_t i=0; i<sizeof(nums)/sizeof(*nums); i++)
  {
    printf("%d %s\n", nums[i], str[i]);
  }
}

Где INIT_LIST должен содержать все инициализаторы в виде целых чисел. После предварительной обработки вышеупомянутое расширяется до следующего:

#include <stdio.h>

int main (void)
{
  int nums[] = {1,2,3,4};
  const char* str[] = {"1", "2", "3", "4"};

  for(size_t i=0; i<sizeof(nums)/sizeof(*nums); i++)
  {
    printf("%d %s\n", nums[i], str[i]);
  }
}

С точки зрения производительности это, естественно, будет астрономически быстрее, чем любое решение, использующее распределение кучи. Обратной стороной является то, что X-макросы могут быть немного сложными для чтения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...