Изменение одного символа в массиве char * без преобразования в char[][]

#c #arrays #segmentation-fault

#c #массивы #ошибка сегментации

Вопрос:

У меня есть одномерный массив строковых литералов разной длины, например:

 char *map[] = {
"ABC",
"ABCDEF",
...
};
  

Я хотел бы изменить определенный символ в массиве на map[y][x]='X'; , который не работает (и не должен) работать в соответствии с Википедией. Я также читал, что объявление его as char map[][] исправит ошибку. Однако это ОЧЕНЬ большой массив, поэтому преобразование его в char map[][] будет непрактичным. Есть ли другой способ выполнить то, что я хочу?

Комментарии:

1. если вы не возражаете потратить немного места, я бы согласился с ответом @VladfromMoscow. Это самый простой способ перенести ваш код.

2. @RSahu, ответ Влада работает, но при печати массива также выводится некоторый мусор (при печати).

Ответ №1:

 #include <stdio.h> 

#define CNV(x) (char []){ x }

int main(void){
    char *map[] = {
    CNV("ABC"),
    CNV("ABCDEF"),
    //...
    };
    map[1][2]='c';
    printf("%sn", map[1]);//ABcDEF
    return 0; 
} 
  

Комментарии:

1. На самом деле это лучше, чем ответ Влада, поскольку он позволяет мне его распечатать

Ответ №2:

Вы не можете изменять строковые литералы. Они неизменяемы в C / C .

Более правильное ваше определение массива будет выглядеть как

 const char *map[] = {
"ABC",
"DEF",
...
};
  

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

Например

 char map[][4] = {
    "ABC",
    "DEF",
    ...
    };
  

Или вы могли бы использовать массив указателей, изменяя требуемый элемент, назначая ему новый строковый литерал.

Комментарии:

1. Я забыл упомянуть, что строки имеют разную длину.

2. @theunamedguy Смотрите мой обновленный пост. Примите во внимание, что нет никакой разницы, храните ли вы строки в массиве или храните указатели на строковые литералы, если все строковые литералы разные.

3. Это то, что я искал. 1

4. Однако, когда я пытаюсь его распечатать, он также печатает некоторый мусор.

5. @theunamedguy Вы не указали самый левый размер массива. Он должен быть достаточно большим, чтобы вместить каждый строковый литерал, включая его завершающий ноль. Смотрите определение двумерного массива в моем сообщении. Вы должны заменить значение 4 на фактический размер максимального строкового литерала.

Ответ №3:

Если вы хотите иметь возможность изменять строки, содержащиеся в вашем массиве, вам придется работать с динамически выделяемой памятью. Я бы выделил массив char* , а затем прогнал и выделил память для каждого, за которым следует вызов strncpy , или аналогичный.

 char** ptrArray = malloc(sizeof(char*)*NUM_STRINGS); 
...
ptrArray[0] = malloc(sizeof(someString)*sizeof(char));
strncpy(ptrArray[0], someString, sizeof(someString)); 
  

Это действительно зависит от контекста вашего приложения.

Комментарии:

1. sizeof(char) по определению 1 .

2. Это сработало бы, но потребовало бы инициализации всего массива при запуске.

3. Я всегда стараюсь поместить его туда. Я понимаю, что такое символ, но я не осознавал, что он является стандартным для всех компиляторов таким образом. Я никогда не сталкивался sizeof(char) > 1 , но на всякий случай, я полагаю, я всегда включал его.

4. @theunamedguy, это зависит от контекста вашего приложения. Вы всегда можете realloc , если вам нужно больше (или меньше, если вы осторожны) строк. Выделение памяти для каждого указателя и копирование данных, конечно, можно выполнить в любое время.

5. @sherrellbc: sizeof фундаментально измеряется в единицах char . Оценка sizeof(char) похожа на вопрос: «Сколько метров в метре?»

Ответ №4:

Это массив указателей на строки, которые расположены в секции памяти, доступной только для чтения:

 char* map[] =
{
    "ABC",
    "DEF",
    ...
};
  

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


Это массив указателей на строки, которые расположены в секции памяти для чтения / записи:

 char map[][MAX_STR_LEN 1] =
{
    "ABC",
    "DEF",
    ...
};
  

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


Пожалуйста, обратите внимание, что во второй схеме у вас есть в основном два варианта:

  1. Объявите массив как глобальную переменную, и в этом случае он будет находиться в разделе данных исполняемого образа и загружаться в память вместе с ним (т. Е. Код инициализации не выполняется).

  2. Объявите массив как локальную переменную, и в этом случае функция будет включать дополнительный фрагмент кода для инициализации массива при каждом его вызове (копирование всех строк из RO-data-section в стек).

Ответ №5:

 #include <stdio.h>

char s1[] = "abcd";
char s2[] = "efghi";
char s3[] = "jklmnop";

char *s[] = {
  s1, s2, s3
};

int main(void)
{
  s[0][0] = '$';
  printf("%s %s %sn", s[0], s[1], s[2]);
  return 0;
}
  

Редактировать: приведенный выше код заменяет указатель на строки указателем на (доступные для записи) массивы символов, что приводит к желаемому поведению, доступному для записи.