我如何连接const/文字字符串在C?

我在用C语言工作,我必须把一些东西连接起来。

现在我有这个:

message = strcat("TEXT ", var);


message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

现在,如果你有C语言的经验,我相信你会意识到,当你试图运行它时,这会给你一个分割错误。我该怎么做呢?

1268660 次浏览

在C语言中,“字符串”只是普通的char数组。因此,您不能直接将它们与其他“字符串”连接。

你可以使用strcat函数,它将由src指向的字符串追加到由dest指向的字符串的末尾:

char *strcat(char *dest, const char *src);

下面是一个示例来自cplusplus.com:

char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");

对于第一个参数,需要提供目标缓冲区本身。目标缓冲区必须是字符数组缓冲区。例如:char buffer[1024];

确保的第一个参数有足够的空间来存储你试图复制到它。如果可用,使用类似strcpy_sstrcat_s这样的函数更安全,其中必须显式指定目标缓冲区的大小。

请注意:字符串字面值不能用作缓冲区,因为它是常量。因此,您总是必须为缓冲区分配一个char数组。

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 ("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()将第二个参数与第一个参数连接起来,并将结果存储在第一个参数中,返回的char*就是第一个参数,只是为了方便。

您不会得到一个连接第一个和第二个参数的新分配字符串,我猜这是您根据代码所期望的。

您正在尝试将字符串复制到静态分配的地址中。你需要进入缓冲地带。

具体地说:

剪断…

目的地

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

这里也有一个例子。

避免在C代码中使用strcat。最干净,也是最重要的,最安全的方法是使用snprintf:

char buf[256];
snprintf(buf, sizeof(buf), "%s%s%s%s", str1, str2, str3, str4);

一些评论者提出了一个问题,即参数的数量可能与格式字符串不匹配,代码仍然会被编译,但如果出现这种情况,大多数编译器已经发出警告。

如果你有C语言的经验,你会注意到字符串只是字符数组,其中最后一个字符是空字符。

现在这是相当不方便的,因为你必须找到最后一个字符,以追加一些东西。strcat将为你做这件事。

所以strcat通过第一个参数搜索空字符。然后它将用第二个参数的内容替换它(直到它以null结束)。

现在让我们看一下你的代码:

message = strcat("TEXT " + var);

在这里,您向指向文本“text”的指针添加了一些内容(“text”的类型是const char*。一个指针)。

这通常是行不通的。此外,修改“TEXT”数组也将不起作用,因为它通常被放置在常量段中。

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

这可能会工作得更好,除非您再次尝试修改静态文本。Strcat没有为结果分配新的内存。

我建议这样做:

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

阅读sprintf的文档,检查它的选项。

现在有一点很重要:

确保缓冲区有足够的空间容纳文本和空字符。有几个函数可以帮助您,例如strncat和printf的特殊版本,它们为您分配缓冲区。 不确保缓冲区大小将导致内存损坏和远程可利用的漏洞

各位,请使用strncpy(), strncat()或snprintf()。 超过你的缓冲空间将会破坏内存中的任何东西!
(并且记住要为尾随的空“\0”字符留出空间!)

不要忘记初始化输出缓冲区。strcat的第一个参数必须是一个空结束的字符串,并为结果字符串分配足够的额外空间:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars

另外,如果您不知道要连接多少字符串,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);
}

正如人们指出的,字符串处理改进了很多。所以你可能想学习如何使用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。

不限制缓冲区大小的最佳方法是使用asprintf()

char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}

试试类似的方法:

#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;
}

假设你有一个char[fixed_size]而不是char*,你可以使用一个单独的、创造性的宏以<<cout<<like顺序一次性完成(“而不是%s不连接的%s\n”,”而不是”,“printf样式格式”)。如果你使用的是嵌入式系统,这种方法还允许你省略malloc和snprintf()等庞大的*printf系列函数(这也可以让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,您可以使用输出char*的任何函数,包括用于将整数转换为字符串类型的itoa()等非标准函数。
  • 注意3,如果你已经在程序的任何地方使用了printf,那么没有理由不使用snprintf(),因为编译后的代码会更大(但内联且明显更快)。

字符串也可以在编译时进行连接。

#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 */
;

你可以编写自己的函数,做与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的值来满足你的需要。

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");