我正在使用C语言编程,需要将一些东西拼接起来。
目前我有以下代码:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
如果你有C语言的经验,你肯定知道当你运行这段代码时会产生分段错误。那么我该如何解决这个问题?
我正在使用C语言编程,需要将一些东西拼接起来。
目前我有以下代码:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
如果你有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 数组缓冲区。例如:char buffer[1024];
确保第一个参数有足够的空间来存储您要复制到其中的内容。如果可用,则更安全地使用像strcpy_s
和strcat_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
!strcat
必须检查你已经连接的所有前面的字节(搜索 '\0'
),这是无用的处理。 - dolmensnprintf()
进行串联是绝对不可取的。 - Leonardo Herrera字符串也可以在编译时连接。
#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 */
;
使用strncpy()、strncat()或snprintf()。
超出缓冲区空间将破坏后面的内存内容!
(记得为尾随的空字符'\0'留出空间!)
strncpy()
。它并不是strcpy()
的“更安全”的版本。目标字符数组可能会不必要地填充额外的'\0'
字符,或者更糟糕的是,可能会没有终止符(即,不是一个字符串)。(它是为一个现在很少用到的数据结构设计的,该数据结构是以零个或多个'\0'
字符填充到末尾的字符数组。) - Keith Thompson如果你事先不知道有多少字符串要连接,那么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);
}
num_words>INT_MAX
时,这将导致无限循环,也许你应该使用size_t
来代替i
。 - 12431234123412341234123如果不想限制缓冲区大小,最好使用asprintf()方法。
char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}
char *
,而不是 const char *
。返回值需要传递给 free
。 - Per Johanssonasprintf
只是 GNU 的扩展。 - Calmariusmessage = 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 可为您分配缓冲区。 不确保缓冲区大小将导致内存损坏和远程可利用的错误。
char[5]
,不是 const char*
。在大多数情况下,它会衰变为char*
。出于向后兼容性的原因,字符串字面值不是const
,但尝试修改它们会导致未定义的行为。(在C++中,字符串字面值是const
。) - Keith Thompsonchar out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
企图修改字符串字面量是未定义行为,而这正是类似以下代码的行为:
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.
正如人们所指出的,字符串处理已经得到了很大的改进。因此,您可能希望学习如何使用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中我找不到更好的方法。
<string.h>
是 C++ 风格。你需要 "string.h"
。你还计算了 strlen(s1)
两次,这是不必要的。s3
应该有 totalLenght+1
的长度。 - Mooing Duck"string.h"
是无意义的。 - sbi#include <string.h>
才是正确的 C 语言方式。使用尖括号来包含标准和系统库头文件(包括 <string.h>
),使用引号来包含你自己程序中的头文件。(如果你没有一个同名的头文件,#include "string.h"
也能工作,但总之还是要使用 <string.h>
。) - Keith Thompson