Как я могу реализовать функцию, которая будет объединять что-то с char* (не массивом char)? Пример того, что я хочу:
#include <stdio.h>
#include <string.h>
int main() {
char* current_line;
char temp[1];
sprintf(temp, "%c", 'A');
// This causes a seg fault. I of course don't want that, so how do I get this to work properly?
strcat(current_line, temp);
return 0;
}
Как я могу исправить это, чтобы оно работало правильно (и, пожалуйста, скажите мне, нужно ли мне что-то добавить к моему вопросу или указать мне правильное направление, потому что я ничего не смог найти)?
Редактировать: я сделал это, но он ошибается
char* charpointercat(char* mystr, char* toconcat) {
char ret[strlen(mystr) + 1];
for(int i = 0; mystr[i] != '\0'; i++) {
ret[i] = mystr[i];
}
return ret;
}
Результат будет сохранен в char*
Вы не можете хранить строку в char*
. char*
может содержать только указатель на символ. Что вы можете сделать, так это создать char *
, который указывает на первый символ в блоке памяти, представляющем строку. Вам нужно как-то выделить эту память, что почти наверняка потребует создания массива.
Тогда как работает char* a = "Hello, world!"
?
@Dock: в примере вашего предыдущего комментария строковый литерал"Hello, world!"
хранится в доступной только для чтения области памяти, которая загружалась из вашего исполняемого файла. Ваша строка кода заставит a
указывать на это место в памяти только для чтения. Однако вам нужна память, в которую можно записать то, что вы хотите сделать.
ХОРОШО. Я просто не хотел, чтобы произошло переполнение буфера, поэтому я избегал использования массива.
В char* a = "Hello, world!";
строка «Привет, мир!» будет где-то в памяти, и технически a
будет содержать указатель на «H» в начале. Когда кто-то говорит, что char *
содержит строку, обычно они имеют в виду именно это.
Из первого кода, который вы опубликовали, кажется, что вы хотите объединить char в конец строки... Этот код вернет новую строку, состоящую из первого, за которым следует второй, он не изменит параметр.
char* charpointercat(char* mystr, char toconcat) {
char *ret = (char*) malloc(sizeof(char)*(strlen(mystr) + 2));
int i;
for(i = 0; mystr[i] != '\0'; i++) {
ret[i] = mystr[i];
}
ret[i] = toconcat;
ret[i + 1] = '\0';
return ret;
}
Это должно работать:
char* charpointercat(char* mystr, char* toconcat) {
size_t l1,l2;
//Get lengths of strings
l1=strlen(mystr);
l2=strlen(toconcat);
//Allocate enough memory for both
char * ret=malloc(l1+l2+1);
strcpy(ret,mystr);
strcat(ret,toconcat);
//Add null terminator
ret[l1+l2]='\0';
return ret;
}
int main(){
char * p=charpointercat("Hello","World");
printf("%s",p);
//Free the memory
free(p);
}
Почему бы просто не использовать strcpy
вместо первого цикла и strcat
вместо второго цикла?
Это не будет работать так, как вы ожидаете, если l1
, l2
или их сумма плюс один больше, чем INT_MAX
. Возможно, вы хотите использовать size_t
?
Оба очень хорошие моменты. Я рассматривал strcpy, не рассматривал strcat.
Нашел интересный пример, когда охотился за причиной не использовать strcat
.
Хорошо, только что закончил бенчмаркинг всего. Для 10 000 000 итераций использование двух циклов заняло 0,51 с, использование strcpy
и одного цикла — 0,44 с, а использование strcpy
и strcat
— 0,38 с. Это имеет смысл — strcpy
обеспечит наибольший выигрыш в эффективности, потому что ему не нужно находить терминатор null
, а strcat
находит, поэтому он дает предельный выигрыш.
Да, вы правы, ваш предыдущий код был, в принципе, эффективнее, потому что strcat
приходится заново проходить первую строку, чтобы найти конец. Кроме того, ваш предыдущий код записывал только один нулевой завершающий символ, так что это также было более эффективно. Так что на самом деле у вас была веская причина не использовать функции strcpy
и strcat
, даже если в данном случае, вероятно, лучше использовать эти две функции, потому что простота, вероятно, важнее эффективности для вопроса. Однако в вашем текущем решении строка ret[l1+l2]='\0';
больше не нужна.
Причина, по которой strcpy
и strcat
более эффективны, чем ваше первое решение, несмотря на то, что ваше решение в принципе более эффективно, вероятно, заключается в том, что эти две функции сильно оптимизированы на уровне языка ассемблера. Вы запускали тест с полной активной оптимизацией компилятора (-O3
на большинстве компиляторов)?
Нет - я использовал оптимизацию по умолчанию. Очень, очень интересный материал — мне нравится изобретать велосипед, поэтому в следующий раз я должен сравнить его с -03
и посмотреть, что у меня получится!
@EvanHendler: Обратите внимание, что флаг не -03
, а -O3
, то есть буква О, а не цифра. В своем последнем комментарии вы написали номер. Это не сработает, если вы воспользуетесь номером.
@AndreasWenzel методом проб и ошибок - я бы туда попал;)
Давайте продолжим обсуждение в чате.
У вас есть 3 проблемы:
current_line
!temp
.charpointercat
.Первое должно быть очевидным, и было объяснено в комментариях:char *current_line
содержит только указатель на некоторые байты, но вам нужно выделить фактические байты, если вы хотите сохранить что-то с помощью такой функции, как stracat
.
Для второго обратите внимание, что sprintf(temp, "%c", 'A');
требуется как минимум char temp[2]
, поскольку он будет использовать один байт для «A» и один байт для завершающего нулевого символа.
Поскольку sprintf
не знает, насколько велико temp
, оно пишет за его пределами, и именно так вы получаете segfault.
Что касается вашего charpointercat
, после выхода из функции ret
больше не существует.
Если быть точнее:
Массив в C представлен указателем (адресом памяти) его первого элемента (ячейки).
Итак, строка return ret;
возвращает не копию всех байтов в ret
, а только указатель на первый байт.
Но этот адрес памяти действителен только внутри функции charpointercat
.
Как только вы попытаетесь использовать его снаружи, это «неопределенное поведение», поэтому может случиться что угодно, включая segfault.
Есть два способа исправить это:
malloc
и выделять память в куче.sprintf
).
Что вы подразумеваете под «связать что-то с символом *»? Где вы ожидаете, что результат будет сохранен?