Если у меня есть массив символов, содержащий строки с нулевым байтом (\0), завершающим каждую строку, как мне добавить в конец еще одну строку?

У меня есть массив символов, содержащий строки, каждая строка которых заканчивается нулевым байтом, например. {'S', 't', 'r', 'i', 'n', 'g', '\0', 'a', 'r', 'r', 'a', 'y', '\0'}.

Я хочу добавить строку в конец этого массива, не удаляя последнюю \0, давая мне (например) {'S', 't', 'r', 'i', 'n', 'g', '\0', 'a', 'r', 'r', 'a', 'y', '\0', 'n', 'e', 'w', '\0'}.

Я считаю, что strcat из string.h удалит последний нулевой байт, так что в данном случае это не работает.

Я также пробовал это, но, похоже, это не сработало:

char str[20] = {'S', 't', 'r', 'i', 'n', 'g', '\0'};
char new_str[6] = "Array";
str[8] = new_str;

Ни одна из строковых функций не будет работать с массивом, имеющим нулевые байты в середине, поскольку они считают, что строка заканчивается на первом нулевом байте.

Barmar 21.03.2024 19:41

Не существует стандартной библиотечной функции, которая работала бы с подобными массивами символов, разделенными нулями. Можете ли вы подробнее поговорить о крайних случаях? Представляет ли массив {'a', '\0', '\0'} строку "a", а затем пустую строку ""? Как код должен узнать, что после первого нулевого терминатора вместо конца массива есть еще одна строка?

JohnFilleau 21.03.2024 19:41
memcpy или strcpyможет быть твоим другом. Но вам нужно найти адрес назначения вручную.
Support Ukraine 21.03.2024 19:42

как узнать, сколько строк у вас в массиве символов? Счетчик боковой полосы? Двойной нулевой символ?/пустая строка Точный размер массива?

Support Ukraine 21.03.2024 19:44

Вы не можете «добавить [] в конец» массива в C. Массивы имеют одинаковую длину на протяжении всей своей жизни. Вы можете создать новый массив большего размера и скопировать в него исходные и новые данные, но это не то же самое, что добавление к существующему массиву. Вы также можете скопировать данные в существующие элементы массива, если они есть, текущие значения которых вам не нужны, но это не добавление в массив, а изменение (возможно, добавление) его логического содержимого.

John Bollinger 21.03.2024 19:44

Я не понимаю, что на самом деле вызывает здесь затруднения. Можете ли вы придумать способ получить указатель на место в памяти сразу после последнего нулевого терминатора? Можете ли вы придумать способ скопировать строку с нулевым символом в конце в какое-либо место в памяти? Если соединить эти две вещи вместе, видите ли вы, как это решит проблему? (Есть ли причина, по которой этот способ решения проблемы не очевиден?)

Karl Knechtel 21.03.2024 19:44

«Я тоже пробовал это, но, похоже, это не сработало:» — Хорошо, похоже, вы уже разобрали задачу таким образом и поняли, как выполнить первую часть (хотя вы неправильно посчитали нужную позицию — или, может быть, вы забыли, что массивы имеют 0-индекс?), но не вторую. Я не уверен, как можно быть знакомым с strcat, но не с strncpy и не с strcpy.

Karl Knechtel 21.03.2024 19:51

Учитывая, что в массиве может быть несколько строк, как узнать, где заканчивается последняя строка? Если массив был полностью инициализирован, поэтому после последней строки осталось ноль байтов, и ни одна из строк, которые будут записаны в него, не является пустой строкой, то конечная позиция может быть указана нулевым байтом после нулевого байта, который завершает последнюю строку. Так ли это? Если нет, то как узнать, где конец?

Eric Postpischil 21.03.2024 19:52

@JohnBollinger: Re: «Вы не можете «добавить [] в конец» массива в C»: вопрос не требует добавления в конец массива. Он просит добавить к «концу», который может быть «концом строк», а не «концом массива», и показывает пример, когда в массиве есть свободное место после конца строк в это.

Eric Postpischil 21.03.2024 19:55

Не путайте инициализаторы массивов в объявлениях с присваиваниями в выражениях. C не имеет присваивания всего массива.

John Bollinger 21.03.2024 19:55

@EricPostpischil, конец может быть концом последовательности строк. Неясно, проводит ли ФП такое различие, как вы, и мой комментарий обращает их внимание на тот факт, что необходимо проводить различие. Также подчеркивается, что даже в случае добавления логического содержимого приходится работать в пределах исходного размера массива.

John Bollinger 21.03.2024 19:59

@EricPostpischil Я совершенно ясно прочитал в вопросе: «Я хочу добавить строку в конец этого массива».

Karl Knechtel 21.03.2024 20:01
str[8] = new_str; неверно в C. 1) такие строки копировать нельзя. 2) Индекс 8 неправильный, потому что индекс нужно начинать с нуля; Но в данном конкретном случае strcpy(&str(7), new_str); подойдет... Но для общего кода это слишком просто.
Support Ukraine 21.03.2024 20:03

Я голосую за закрытие, потому что нам нужно больше информации. Самый важный вопрос: как мы можем узнать количество строк?

Support Ukraine 21.03.2024 20:11

@KarlKnechtel: Да, и он показывает массив символов в тексте, а не в коде C. Код C показывает массив, определенный с дополнительным доступным пространством. Итак, ОП знает, что место необходимо, и эти различные комментарии, язвительные по поводу мелочей, являются неприятностями, которые не помогают. Как только OP укажет, как будет известен конец строк, можно будет ответить на вопрос, а затем в ответе может случайно упомянуть, что необходимо пространство.

Eric Postpischil 21.03.2024 20:15

«с нулевым байтом (\0), завершающим каждую строку» == hello\0\0 (== бутерброд с хлебом) Поскольку строка по определению завершается нулевым байтом. Нулевой байт является конечным разделителем. Ваш пост смешивает концепцию строки с другими вещами. Было бы лучше поговорить об объединении двух (или более) объектов в памяти («которые случайно могут быть строками...») с условиями, о которых вы говорите. Введение строкового типа вносит изменения в ваше сообщение, поскольку все, что управляет строками, будет отклонять невозможные строки, такие как конкатенация символов с нулевыми символами между ними.

nostromo 21.03.2024 20:17

Я написал и самостоятельно ответил на правильную версию вопроса на Codidact.

Karl Knechtel 21.03.2024 20:38
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
17
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Получите указатель на элемент после нулевого байта и используйте его в качестве цели strcpy().

char str[20] = {'S', 't', 'r', 'i', 'n', 'g', '\0'};
char new_str[6] = "Array";
strcpy(&str[strlen(str)+1], new_str);

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

В задаче говорится, что существует массив, содержащий строки во множественном числе, и приводится пример (в первом предложении, а не в последующем коде) с двумя строками. Найти первый нулевой байт недостаточно.

Eric Postpischil 21.03.2024 19:50

Не знаю, почему это получает DV. Это возможный подход к проблеме, и пример основан непосредственно на примере, представленном в вопросе. Однако предоставленные сведения не представляют собой полного решения для случая, когда изначально в массиве имеется больше, чем строка. Здесь действительно нужно что-то большее, чем strlen() (возможно, подсчет текущего количества строк, чтобы, например, сделать соответствующее количество вызовов strlen()).

John Bollinger 21.03.2024 19:50

@JohnBollinger Есть много вещей, которые здесь не работают: это не учитывает случай, когда несколько строк уже находятся в буфере (или нет строк!) - необходимо будет отдельно отслеживать «конец данных» . Это также не учитывает потенциальное переполнение буфера, и поэтому показывать такой код опасно, особенно новичку. Но помимо всего этого, он пытается ответить на вопрос, который вместо этого следует закрыть, поскольку он слишком расплывчатый и несфокусированный. Комментарии показывают, что ОП не четко определил, в чем заключается реальная проблема, которую необходимо решить.

Karl Knechtel 21.03.2024 20:00

Для конкретного примера это прекрасное решение. Но для общей задачи, т.е. «Добавить строку в конец массива символов, содержащего несколько строк», нам нужен более сложный код.

Support Ukraine 21.03.2024 20:09

Предложите какой-нибудь тип дозорного в конце последней строки. Нулевая инициализация работает, но необходимо зарезервировать место для двойного нулевого символа в конце строки. Если последняя строка имеет только один нулевой символ на границе массива, невозможно отличить ее от конца последней строки без сохранения исходного размера массива для сравнения. Довольно шаткая настройка хранилища, но, эй, это C, у вас есть свобода заставить его работать.

David C. Rankin 21.03.2024 22:23

@DavidC.Rankin К вашему сведению, именно так Unix реализует данные, на которые указывают argv и envp. Я думаю, что в конце у них даже может быть двойной нулевой страж, но не уверен. Я не думаю, что что-то из этого есть в спецификации, это просто традиционная реализация.

Barmar 21.03.2024 23:16

@Barmar, да, а также все функции execl.., в которых дозорный NULL используется в качестве конечного указателя. Упрощает зацикливание while (ptr) { ... }. Но при самостоятельном перемещении необходимо убедиться, что страж размещен правильно, особенно при удалении элементов. (где возникла путаница в моем комментарии, я представлял себе втиснение нескольких строк в один массив символов - немного отличается от вопроса о char ** и char *[])

David C. Rankin 22.03.2024 01:28

Если оставшаяся часть массива обнулена (что и должно быть в вашем примере), проще всего (-ish) начать с конца и считать в обратном направлении, пока не найдете первый байт, отличный от 0, а затем ваш новый старт позиция будет равна этому индексу (или указателю) плюс 2:

for ( size_t i = 18; i > 0; --i ) // save room for that last terminator
{
  if ( arr[i] ) // character is non-zero
    index = i + 2;
}

тогда вы можете использовать strcpy, чтобы добавить новую строку в конец (при условии, что вы сначала убедились, что есть место):

strcpy( &arr[index], "new" );

Буууууууууууу...

Этот метод гарантированно вызовет изжогу, особенно если вы хотите изменить что-нибудь, кроме последней строки. Если вы полны решимости хранить все строки в одном массиве char, вам следует создать вторичную таблицу для отслеживания начального индекса, длины и любых других данных каждой строки, которые вас интересуют; это значительно облегчит поиск и добавление строк:

char strspace[SOME_SIZE] = {0}; 

/**
 * Create a table to track your strings
 */
struct st { 
  size_t index;
  size_t len;
} st[SOME_SIZE];

size_t count = 0;

...

st[count].index = count > 0 ? st[count-1].index + st[count - 1].len : 0;
st[count].len = strlen( newStr ) + 1;
strcpy( &strspace[st[count++].index], newStr );

В этом случае я сохраняю терминатор 0, чтобы упростить использование библиотеки обычных строк для данных, хранящихся в массиве, например:

strcpy( someString, &strspace[st[i].index] );
strlen( &strspace[st[x].index );

и т. д. Отсутствие сохранения терминатора освободит немного места, но вам придется проделать дополнительную работу, если вы хотите использовать библиотеку строк для этих строк. Это зависит от вас.

Если вам нужно изменить или удалить строки в середине массива, вам также нужно будет отслеживать доступные блоки в таблице, возможно, при необходимости объединять пустое пространство.

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

char **strs = malloc( sizeof *strs * num_strings );
...
strs[i] = malloc( sizeof *strs[i] * (strlen( newStr ) + 1 );
if ( strs[i] )
  strcpy( strs[i], newStr );

Не нужно беспокоиться о том, где что-то начинается или заканчивается, легко расширять, легко сортировать, просто во многих отношениях с этим намного проще иметь дело.

Подход «сканировать назад на наличие ненулевых значений» также исключает возможность хранения пустых строк.

Karl Knechtel 24.03.2024 23:21

Другие вопросы по теме