У меня есть массив символов, содержащий строки, каждая строка которых заканчивается нулевым байтом, например. {'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;
Не существует стандартной библиотечной функции, которая работала бы с подобными массивами символов, разделенными нулями. Можете ли вы подробнее поговорить о крайних случаях? Представляет ли массив {'a', '\0', '\0'}
строку "a"
, а затем пустую строку ""
? Как код должен узнать, что после первого нулевого терминатора вместо конца массива есть еще одна строка?
memcpy
или strcpy
может быть твоим другом. Но вам нужно найти адрес назначения вручную.
как узнать, сколько строк у вас в массиве символов? Счетчик боковой полосы? Двойной нулевой символ?/пустая строка Точный размер массива?
Вы не можете «добавить [] в конец» массива в C. Массивы имеют одинаковую длину на протяжении всей своей жизни. Вы можете создать новый массив большего размера и скопировать в него исходные и новые данные, но это не то же самое, что добавление к существующему массиву. Вы также можете скопировать данные в существующие элементы массива, если они есть, текущие значения которых вам не нужны, но это не добавление в массив, а изменение (возможно, добавление) его логического содержимого.
Я не понимаю, что на самом деле вызывает здесь затруднения. Можете ли вы придумать способ получить указатель на место в памяти сразу после последнего нулевого терминатора? Можете ли вы придумать способ скопировать строку с нулевым символом в конце в какое-либо место в памяти? Если соединить эти две вещи вместе, видите ли вы, как это решит проблему? (Есть ли причина, по которой этот способ решения проблемы не очевиден?)
«Я тоже пробовал это, но, похоже, это не сработало:» — Хорошо, похоже, вы уже разобрали задачу таким образом и поняли, как выполнить первую часть (хотя вы неправильно посчитали нужную позицию — или, может быть, вы забыли, что массивы имеют 0-индекс?), но не вторую. Я не уверен, как можно быть знакомым с strcat
, но не с strncpy
и не с strcpy
.
Учитывая, что в массиве может быть несколько строк, как узнать, где заканчивается последняя строка? Если массив был полностью инициализирован, поэтому после последней строки осталось ноль байтов, и ни одна из строк, которые будут записаны в него, не является пустой строкой, то конечная позиция может быть указана нулевым байтом после нулевого байта, который завершает последнюю строку. Так ли это? Если нет, то как узнать, где конец?
@JohnBollinger: Re: «Вы не можете «добавить [] в конец» массива в C»: вопрос не требует добавления в конец массива. Он просит добавить к «концу», который может быть «концом строк», а не «концом массива», и показывает пример, когда в массиве есть свободное место после конца строк в это.
Не путайте инициализаторы массивов в объявлениях с присваиваниями в выражениях. C не имеет присваивания всего массива.
@EricPostpischil, конец может быть концом последовательности строк. Неясно, проводит ли ФП такое различие, как вы, и мой комментарий обращает их внимание на тот факт, что необходимо проводить различие. Также подчеркивается, что даже в случае добавления логического содержимого приходится работать в пределах исходного размера массива.
@EricPostpischil Я совершенно ясно прочитал в вопросе: «Я хочу добавить строку в конец этого массива».
str[8] = new_str;
неверно в C. 1) такие строки копировать нельзя. 2) Индекс 8 неправильный, потому что индекс нужно начинать с нуля; Но в данном конкретном случае strcpy(&str(7), new_str);
подойдет... Но для общего кода это слишком просто.
Я голосую за закрытие, потому что нам нужно больше информации. Самый важный вопрос: как мы можем узнать количество строк?
@KarlKnechtel: Да, и он показывает массив символов в тексте, а не в коде C. Код C показывает массив, определенный с дополнительным доступным пространством. Итак, ОП знает, что место необходимо, и эти различные комментарии, язвительные по поводу мелочей, являются неприятностями, которые не помогают. Как только OP укажет, как будет известен конец строк, можно будет ответить на вопрос, а затем в ответе может случайно упомянуть, что необходимо пространство.
«с нулевым байтом (\0), завершающим каждую строку» == hello\0\0 (== бутерброд с хлебом) Поскольку строка по определению завершается нулевым байтом. Нулевой байт является конечным разделителем. Ваш пост смешивает концепцию строки с другими вещами. Было бы лучше поговорить об объединении двух (или более) объектов в памяти («которые случайно могут быть строками...») с условиями, о которых вы говорите. Введение строкового типа вносит изменения в ваше сообщение, поскольку все, что управляет строками, будет отклонять невозможные строки, такие как конкатенация символов с нулевыми символами между ними.
Я написал и самостоятельно ответил на правильную версию вопроса на Codidact.
Получите указатель на элемент после нулевого байта и используйте его в качестве цели strcpy()
.
char str[20] = {'S', 't', 'r', 'i', 'n', 'g', '\0'};
char new_str[6] = "Array";
strcpy(&str[strlen(str)+1], new_str);
В более общем случае вам необходимо отслеживать последнюю строку в массиве, чтобы можно было найти элемент после нулевого терминатора.
В задаче говорится, что существует массив, содержащий строки во множественном числе, и приводится пример (в первом предложении, а не в последующем коде) с двумя строками. Найти первый нулевой байт недостаточно.
Не знаю, почему это получает DV. Это возможный подход к проблеме, и пример основан непосредственно на примере, представленном в вопросе. Однако предоставленные сведения не представляют собой полного решения для случая, когда изначально в массиве имеется больше, чем строка. Здесь действительно нужно что-то большее, чем strlen()
(возможно, подсчет текущего количества строк, чтобы, например, сделать соответствующее количество вызовов strlen()
).
@JohnBollinger Есть много вещей, которые здесь не работают: это не учитывает случай, когда несколько строк уже находятся в буфере (или нет строк!) - необходимо будет отдельно отслеживать «конец данных» . Это также не учитывает потенциальное переполнение буфера, и поэтому показывать такой код опасно, особенно новичку. Но помимо всего этого, он пытается ответить на вопрос, который вместо этого следует закрыть, поскольку он слишком расплывчатый и несфокусированный. Комментарии показывают, что ОП не четко определил, в чем заключается реальная проблема, которую необходимо решить.
Для конкретного примера это прекрасное решение. Но для общей задачи, т.е. «Добавить строку в конец массива символов, содержащего несколько строк», нам нужен более сложный код.
Предложите какой-нибудь тип дозорного в конце последней строки. Нулевая инициализация работает, но необходимо зарезервировать место для двойного нулевого символа в конце строки. Если последняя строка имеет только один нулевой символ на границе массива, невозможно отличить ее от конца последней строки без сохранения исходного размера массива для сравнения. Довольно шаткая настройка хранилища, но, эй, это C, у вас есть свобода заставить его работать.
@DavidC.Rankin К вашему сведению, именно так Unix реализует данные, на которые указывают argv
и envp
. Я думаю, что в конце у них даже может быть двойной нулевой страж, но не уверен. Я не думаю, что что-то из этого есть в спецификации, это просто традиционная реализация.
@Barmar, да, а также все функции execl..
, в которых дозорный NULL
используется в качестве конечного указателя. Упрощает зацикливание while (ptr) { ... }
. Но при самостоятельном перемещении необходимо убедиться, что страж размещен правильно, особенно при удалении элементов. (где возникла путаница в моем комментарии, я представлял себе втиснение нескольких строк в один массив символов - немного отличается от вопроса о char **
и char *[]
)
Если оставшаяся часть массива обнулена (что и должно быть в вашем примере), проще всего (-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 );
Не нужно беспокоиться о том, где что-то начинается или заканчивается, легко расширять, легко сортировать, просто во многих отношениях с этим намного проще иметь дело.
Подход «сканировать назад на наличие ненулевых значений» также исключает возможность хранения пустых строк.
Ни одна из строковых функций не будет работать с массивом, имеющим нулевые байты в середине, поскольку они считают, что строка заканчивается на первом нулевом байте.