Как объединить константные / литеральные строки в C?
Я работаю в C, и мне нужно объединить несколько вещей.
Прямо сейчас у меня есть это:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Теперь, если у вас есть опыт работы с Си, я уверен, что вы понимаете, что это вызывает ошибку сегментации, когда вы пытаетесь запустить ее. Так как мне обойти это?
17 ответов
В C "строки" просто char
массивы. Следовательно, вы не можете напрямую объединять их с другими "строками".
Вы можете использовать strcat
функция, которая добавляет строку, указанную src
до конца строки, на которую указывает dest
:
char *strcat(char *dest, const char *src);
char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");
Для первого параметра вам нужно указать сам буфер назначения. Буфер назначения должен быть буфером массива символов. Например: char buffer[1024];
Убедитесь, что первый параметр имеет достаточно места для хранения того, что вы пытаетесь скопировать в него. Если вам доступно, безопаснее использовать такие функции, как: strcpy_s
а также strcat_s
где вы явно должны указать размер буфера назначения.
Примечание. Строковый литерал нельзя использовать в качестве буфера, поскольку он является константой. Таким образом, вы всегда должны выделять массив символов для буфера.
Возвращаемое значение strcat
может просто игнорироваться, он просто возвращает тот же указатель, который был передан в качестве первого аргумента. Это сделано для удобства и позволяет вам объединять вызовы в одну строку кода:
strcat(strcat(str, foo), bar);
Таким образом, ваша проблема может быть решена следующим образом:
char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
Избегать использования strcat
в C-код. Самый чистый и, самое главное, самый безопасный способ - это использовать snprintf
:
char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);
Некоторые комментаторы поднимали вопрос о том, что число аргументов может не соответствовать строке формата, и код все равно будет компилироваться, но большинство компиляторов уже выдают предупреждение, если это так.
Строки также могут быть объединены во время компиляции.
#define SCHEMA "test"
#define TABLE "data"
const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry = // include comments in a string
" SELECT * " // get all fields
" FROM " SCHEMA "." TABLE /* the table */
" WHERE x = 1 " /* the filter */
;
Ребята, используйте str cpy (), str n cat () или s n printf ().
Превышение вашего буферного пространства приведет к удалению всего, что следует в памяти!
(И не забудьте оставить место для завершающего нулевого символа '\0'!)
Также malloc и realloc полезны, если вы не знаете заранее, сколько строк объединяются.
#include <stdio.h>
#include <string.h>
void example(const char *header, const char **words, size_t num_words)
{
size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
char *message = (char*) malloc(message_len);
strncat(message, header, message_len);
for(int i = 0; i < num_words; ++i)
{
message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
message = (char*) realloc(message, message_len);
strncat(strncat(message, ";", message_len), words[i], message_len);
}
puts(message);
free(message);
}
Если у вас есть опыт работы с Си, вы заметите, что строки - это только массивы символов, где последний символ является нулевым символом.
Теперь это довольно неудобно, так как вам нужно найти последний символ, чтобы добавить что-то. strcat
сделаю это для вас.
Таким образом, strcat ищет в первом аргументе нулевой символ. Затем он заменит это на содержимое второго аргумента (до тех пор, пока оно не закончится нулевым значением).
Теперь давайте пройдемся по вашему коду:
message = strcat("TEXT " + var);
Здесь вы добавляете что-то к указателю на текст "TEXT" (тип "TEXT" - const char*. A pointer.).
Это обычно не работает. Также изменение массива "TEXT" не будет работать, так как он обычно помещается в постоянный сегмент.
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
Это может работать лучше, за исключением того, что вы снова пытаетесь изменить статические тексты. strcat не выделяет новую память для результата.
Я бы предложил сделать что-то вроде этого:
sprintf(message2, "TEXT %s TEXT %s", foo, bar);
Прочитайте документацию sprintf
чтобы проверить его варианты.
А теперь важный момент:
Убедитесь, что в буфере достаточно места для текста и нулевого символа. Есть пара функций, которые могут вам помочь, например, strncat и специальные версии printf, которые выделяют буфер для вас. Несоблюдение размера буфера приведет к повреждению памяти и удаленному использованию ошибок.
Лучший способ сделать это без ограничения размера буфера - использовать asprintf()
char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}
Не забудьте инициализировать выходной буфер. Первый аргумент strcat должен быть строкой с нулевым символом в конце и достаточным дополнительным пространством, выделенным для результирующей строки:
char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
Как отмечали люди, обработка строк значительно улучшилась. Поэтому вы можете узнать, как использовать библиотеку строк C++ вместо строк в стиле C. Однако вот решение в чистом C
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void appendToHello(const char *s) {
const char *const hello = "hello ";
const size_t sLength = strlen(s);
const size_t helloLength = strlen(hello);
const size_t totalLength = sLength + helloLength;
char *const strBuf = malloc(totalLength + 1);
if (strBuf == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(strBuf, hello);
strcpy(strBuf + helloLength, s);
puts(strBuf);
free(strBuf);
}
int main (void) {
appendToHello("blah blah");
return 0;
}
Я не уверен, что это правильно / безопасно, но сейчас я не смог найти лучший способ сделать это в ANSI C.
Неопределенное поведение - пытаться модифицировать строковые литералы, что-то вроде:
strcat ("Hello, ", name);
попытаюсь сделать. Он будет пытаться придерживаться name
строка до конца строкового литерала "Hello, "
, который не очень хорошо определен.
Попробуйте что-нибудь такое. Он достигает того, что вы пытаетесь сделать:
char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);
Это создает область буфера, которую можно изменять, а затем копирует в нее как строковый литерал, так и другой текст. Просто будьте осторожны с переполнением буфера. Если вы управляете входными данными (или проверяете их заранее), можно использовать буферы фиксированной длины, как у меня.
В противном случае вы должны использовать стратегии смягчения, такие как выделение достаточного количества памяти из кучи, чтобы гарантировать, что вы можете справиться с этим. Другими словами, что-то вроде:
const static char TEXT[] = "TEXT ";
// Make *sure* you have enough space.
char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);
// Need to free message at some point after you're done with it.
Первый аргумент strcat() должен содержать достаточно места для объединенной строки. Поэтому выделите буфер с достаточным пространством для получения результата.
char bigEnough[64] = "";
strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);
/* and so on */
strcat() объединит второй аргумент с первым аргументом и сохранит результат в первом аргументе, возвращенный символ * - это просто первый аргумент, и только для вашего удобства.
Вы не получите вновь выделенную строку с объединенным первым и вторым аргументом, что, как я полагаю, вы ожидаете, основываясь на вашем коде.
Вы пытаетесь скопировать строку в адрес, который статически выделен. Вам нужно кошку в буфер.
В частности:
... чик...
место назначения
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
... чик...
http://www.cplusplus.com/reference/clibrary/cstring/strcat.html
Здесь также есть пример.
Вы можете написать свою собственную функцию, которая делает то же самое, что и strcat()
но это ничего не меняет:
#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
static char buffer[MAX_STRING_LENGTH];
strncpy(buffer,str1,MAX_STRING_LENGTH);
if(strlen(str1) < MAX_STRING_LENGTH){
strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
}
buffer[MAX_STRING_LENGTH - 1] = '\0';
return buffer;
}
int main(int argc,char *argv[]){
printf("%s",strcat_const("Hello ","world")); //Prints "Hello world"
return 0;
}
Если длина обеих строк превышает 1000 символов, строка будет обрезана до 1000 символов. Вы можете изменить значение MAX_STRING_LENGTH
чтобы удовлетворить ваши потребности.
Предполагая, что у вас есть char[fixed_size], а не char*, вы можете использовать один творческий макрос, чтобы сделать все это одновременно с <<cout<<like
упорядочение ("скорее%s несвязанный%s\n", "чем", "формат стиля printf"). Если вы работаете со встроенными системами, этот метод также позволит вам исключить malloc и большие *printf
семейство функций, таких как snprintf()
(Это удерживает dietlibc от жалоб на *printf тоже)
#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
const char *s, \
*a[] = { __VA_ARGS__,NULL}, \
**ss=a; \
while((s=*ss++)) \
while((*s)&&(++offset<(int)sizeof(buf))) \
*bp++=*s++; \
if (offset!=sizeof(buf))*bp=0; \
}while(0)
char buf[256];
int len=0;
strcpyALL(buf,len,
"The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
write(1,buf,len); //outputs our message to stdout
else
write(2,"error\n",6);
//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
write(1,buf,len); //outputs both messages
else
write(2,"error\n",6);
- Примечание 1, вы, как правило, не используете argv[0] вот так - просто пример
- Примечание 2, вы можете использовать любую функцию, которая выводит символ *, включая нестандартные функции, такие как itoa(), для преобразования целых чисел в строковые типы.
- Примечание 3, если вы уже используете printf где-либо в своей программе, нет причин не использовать snprintf(), так как скомпилированный код будет больше (но встроен и значительно быстрее)
int main()
{
char input[100];
gets(input);
char str[101];
strcpy(str, " ");
strcat(str, input);
char *p = str;
while(*p) {
if(*p == ' ' && isalpha(*(p+1)) != 0)
printf("%c",*(p+1));
p++;
}
return 0;
}
Это было мое решение
#include <stdlib.h>
#include <stdarg.h>
char *strconcat(int num_args, ...) {
int strsize = 0;
va_list ap;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++)
strsize += strlen(va_arg(ap, char*));
char *res = malloc(strsize+1);
strsize = 0;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++) {
char *s = va_arg(ap, char*);
strcpy(res+strsize, s);
strsize += strlen(s);
}
va_end(ap);
res[strsize] = '\0';
return res;
}
но вам нужно указать, сколько строк вы собираетесь объединить
char *str = strconcat(3, "testing ", "this ", "thing");
Попробуйте что-то похожее на это:
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[])
{
// Insert code here...
char firstname[100], secondname[100];
printf("Enter First Name: ");
fgets(firstname, 100, stdin);
printf("Enter Second Name: ");
fgets(secondname,100,stdin);
firstname[strlen(firstname)-1]= '\0';
printf("fullname is %s %s", firstname, secondname);
return 0;
}