复制C字符串的正确方法

Proper way to copy C strings

本文关键字:方法 字符串 复制      更新时间:2023-10-16

有没有一种简单的方法可以复制C字符串?

我有const char *stringA,我希望char *stringB取该值(注意,stringB不是const)。我尝试了stringB=(char*) stringA,但这使得stringB仍然指向相同的内存位置,所以当stringA后来更改时,stringB也会更改。

我也尝试过strcpy(stringB,stringA),但似乎如果stringB没有初始化为足够大的数组,就会出现segfault。不过,我对C字符串不是很有经验,是不是遗漏了一些显而易见的东西?

如果我只是将stringB初始化为char *stringB[23],因为我知道我永远不会有超过22个字符的字符串(并且允许空终止符),这是正确的方法吗?如果检查stringB是否与其他C字符串相等,那么额外的空间会影响什么吗?

(在这里,仅仅使用字符串并不是一个解决方案,因为我需要最小的开销和对单个字符的轻松访问。)

您可以使用strdup()返回C字符串的副本,如:

#include <string.h>
const char *stringA = "foo";
char *stringB = NULL;
stringB = strdup(stringA);
/* ... */
free(stringB);
stringB = NULL; 

您也可以使用strcpy(),但您需要首先分配空间,这并不难做到,但如果操作不正确,可能会导致溢出错误:

#include <string.h>
const char *stringA = "foo";
char *stringB = NULL;
/* you must add one to cover the byte needed for the terminating null character */
stringB = (char *) malloc( strlen(stringA) + 1 ); 
strcpy( stringB, stringA );
/* ... */
free(stringB);
stringB = NULL;

如果您不能使用strdup(),我建议使用strncpy()而不是strcpy()strncpy()函数最多可复制--最多只能复制--n字节,这有助于避免溢出错误。但是,如果是strlen(stringA) + 1 > n,则需要自己终止stringB。但是,一般来说,你会知道你需要什么尺寸的东西:

#include <string.h>
const char *stringA = "foo";
char *stringB = NULL;
/* you must add one to cover the byte needed for the terminating null character */
stringB = (char *) malloc( strlen(stringA) + 1 ); 
strncpy( stringB, stringA, strlen(stringA) + 1 );
/* ... */
free(stringB);
stringB = NULL;

我觉得strdup()更干净,所以我试着在专门处理字符串的地方使用它。我不知道POSIX/非POSIX方法在性能方面是否有严重的缺点,但我不是C或C++专家。

请注意,我将malloc()的结果强制转换为char *。这是因为您的问题被标记为c++问题。在C++中,需要强制转换malloc()的结果。然而,在C中,您将而不是强制执行此操作。

编辑

有一个复杂的问题:strdup()不在C或C++中。因此,将strcpy()strncp()与预先确定大小的数组或malloc指针一起使用。使用strncp()而不是strcpy()是一个好习惯,无论您在哪里使用该函数。这将有助于减少出错的可能性。

如果我只是将stringB初始化为char*stringB[23],因为我知道我的字符串永远不会超过22个字符(并且允许使用null终止符),这是正确的方法吗?

差不多。在C中,如果你确信字符串永远不会太长:

char stringB[MAX+1];
assert(strlen(stringA) <= MAX));
strcpy(stringB, stringA);

或者,如果字符串可能太长:

char stringB[MAX+1];
strncpy(stringB, stringA, MAX+1);
if (stringB[MAX] != '') {
    // ERROR: stringA was too long.
    stringB[MAX] = ''; // if you want to use the truncated string
}

在C++中,您应该使用std::string,除非您已经证明开销过高。许多实现都有"短字符串优化",这将避免短字符串的动态分配;在这种情况下,使用C样式数组将几乎没有开销。访问单个字符与使用C样式数组一样方便;在这两种情况下,s[i]都将位置i处的字符作为左值。复制成为stringB = stringA;,没有未定义行为的危险。

如果您确实发现std::string不可用,请考虑std::array<char,MAX+1>:一个包含固定大小数组的可复制类。

如果检查字符串B是否与其他C字符串相等,那么额外的空间会影响什么吗?

如果使用strcmp,则它将停止在最短字符串的末尾,并且不会受到额外空间的影响。

如果你想用纯C风格来做,那么:

char* new_string = strdup(old_string);
free(new_string);

如果你想用(某种)C++风格:

char* new_string = new char[strlen(old_string) + 1];
strcpy(new_string, old_string);
delete[] new_string;

您可能正在寻找strncpy,它允许您从字符串中复制第一个n字符。只需确保在复制到的字符串的位置n添加null终止符。