Как я могу реализовать функцию для конкатенации с char *, а не с массивом char?

Как я могу реализовать функцию, которая будет объединять что-то с 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;
}

Что вы подразумеваете под «связать что-то с символом *»? Где вы ожидаете, что результат будет сохранен?

Brian61354270 12.12.2020 01:21

Результат будет сохранен в char*

Dock 12.12.2020 01:22

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

Brian61354270 12.12.2020 01:26

Тогда как работает char* a = "Hello, world!"?

Dock 12.12.2020 01:27

@Dock: в примере вашего предыдущего комментария строковый литерал"Hello, world!" хранится в доступной только для чтения области памяти, которая загружалась из вашего исполняемого файла. Ваша строка кода заставит a указывать на это место в памяти только для чтения. Однако вам нужна память, в которую можно записать то, что вы хотите сделать.

Andreas Wenzel 12.12.2020 01:34

ХОРОШО. Я просто не хотел, чтобы произошло переполнение буфера, поэтому я избегал использования массива.

Dock 12.12.2020 01:37

В char* a = "Hello, world!"; строка «Привет, мир!» будет где-то в памяти, и технически a будет содержать указатель на «H» в начале. Когда кто-то говорит, что char * содержит строку, обычно они имеют в виду именно это.

Dmitri 12.12.2020 01:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
74
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Из первого кода, который вы опубликовали, кажется, что вы хотите объединить 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 вместо второго цикла?

Andreas Wenzel 12.12.2020 01:48

Это не будет работать так, как вы ожидаете, если l1, l2 или их сумма плюс один больше, чем INT_MAX. Возможно, вы хотите использовать size_t?

Brian61354270 12.12.2020 01:50

Оба очень хорошие моменты. Я рассматривал strcpy, не рассматривал strcat.

Evan Hendler 12.12.2020 02:56
stackoverflow.com/questions/21880730/…
Evan Hendler 12.12.2020 03:05

Нашел интересный пример, когда охотился за причиной не использовать strcat.

Evan Hendler 12.12.2020 03:06

Хорошо, только что закончил бенчмаркинг всего. Для 10 000 000 итераций использование двух циклов заняло 0,51 с, использование strcpy и одного цикла — 0,44 с, а использование strcpy и strcat — 0,38 с. Это имеет смысл — strcpy обеспечит наибольший выигрыш в эффективности, потому что ему не нужно находить терминатор null, а strcat находит, поэтому он дает предельный выигрыш.

Evan Hendler 12.12.2020 03:35

Да, вы правы, ваш предыдущий код был, в принципе, эффективнее, потому что strcat приходится заново проходить первую строку, чтобы найти конец. Кроме того, ваш предыдущий код записывал только один нулевой завершающий символ, так что это также было более эффективно. Так что на самом деле у вас была веская причина не использовать функции strcpy и strcat, даже если в данном случае, вероятно, лучше использовать эти две функции, потому что простота, вероятно, важнее эффективности для вопроса. Однако в вашем текущем решении строка ret[l1+l2]='\0'; больше не нужна.

Andreas Wenzel 12.12.2020 16:03

Причина, по которой strcpy и strcat более эффективны, чем ваше первое решение, несмотря на то, что ваше решение в принципе более эффективно, вероятно, заключается в том, что эти две функции сильно оптимизированы на уровне языка ассемблера. Вы запускали тест с полной активной оптимизацией компилятора (-O3 на большинстве компиляторов)?

Andreas Wenzel 12.12.2020 16:08

Нет - я использовал оптимизацию по умолчанию. Очень, очень интересный материал — мне нравится изобретать велосипед, поэтому в следующий раз я должен сравнить его с -03 и посмотреть, что у меня получится!

Evan Hendler 12.12.2020 16:55

@EvanHendler: Обратите внимание, что флаг не -03, а -O3, то есть буква О, а не цифра. В своем последнем комментарии вы написали номер. Это не сработает, если вы воспользуетесь номером.

Andreas Wenzel 12.12.2020 17:16

@AndreasWenzel методом проб и ошибок - я бы туда попал;)

Evan Hendler 12.12.2020 17:50

Давайте продолжим обсуждение в чате.

Evan Hendler 12.12.2020 17:54

У вас есть 3 проблемы:

  1. Вы вообще не выделяете память для current_line!
  2. Вы не выделили достаточно памяти для temp.
  3. Вы возвращаете указатель на локальную переменную из charpointercat.

Первое должно быть очевидным, и было объяснено в комментариях:
char *current_line содержит только указатель на некоторые байты, но вам нужно выделить фактические байты, если вы хотите сохранить что-то с помощью такой функции, как stracat.

Для второго обратите внимание, что sprintf(temp, "%c", 'A'); требуется как минимум char temp[2], поскольку он будет использовать один байт для «A» и один байт для завершающего нулевого символа.

Поскольку sprintf не знает, насколько велико temp, оно пишет за его пределами, и именно так вы получаете segfault.

Что касается вашего charpointercat, после выхода из функции ret больше не существует.

Если быть точнее:
Массив в C представлен указателем (адресом памяти) его первого элемента (ячейки).

Итак, строка return ret; возвращает не копию всех байтов в ret, а только указатель на первый байт.

Но этот адрес памяти действителен только внутри функции charpointercat.
Как только вы попытаетесь использовать его снаружи, это «неопределенное поведение», поэтому может случиться что угодно, включая segfault.

Есть два способа исправить это:

  1. Узнайте, как использовать malloc и выделять память в куче.
  2. Передайте третий массив функции, чтобы она могла сохранить там результат (так же, как вы делаете с sprintf).

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