复制C字符串的正确方法

Posted

技术标签:

【中文标题】复制C字符串的正确方法【英文标题】:Proper way to copy C strings 【发布时间】:2012-03-06 23:55:59 【问题描述】:

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

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

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

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

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

【问题讨论】:

回复:“在这里仅使用字符串不是解决方案,因为我需要最小的开销和轻松访问单个字符”:我强烈怀疑您高估了开销;而且您肯定高估了访问单个字符的难度:c = s[10]s[10] = c 如果sstd::string,就好像它是char* 一样,都可以正常工作。 请格式化您的问题。您可以使用反引号表示代码内联和缩进四个空格以获得代码块。 使用std::string,除非你有更好的理由不这样做。 std::string 是解决方案。你是在错误地假设它会更贵的情况下工作。 抱歉格式化;我不知道反引号是什么意思(撇号似乎没有任何作用)。 【参考方案1】:

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

#include <string.h>

const char *stringA = "foo";
char *stringB = NULL;

stringB = strdup(stringA);
/* ... */
free(stringB);    

您也可以使用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);

如果您不能使用strdup(),我建议使用strncpy() 而不是strcpy()strncpy() 函数最多复制 - 且仅最多 - n 字节,这有助于避免溢出错误。但是,如果是 strlen(stringA) + 1 &gt; 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);

我认为strdup() 我自己更干净,所以我尝试在专门处理字符串的情况下使用它。我不知道 POSIX/非 POSIX 方法在性能方面是否存在严重的缺点,但我不是 C 或 C++ 专家。

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

编辑

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

【讨论】:

strdup 在 POSIX 中,而不是 C 或 C++。 strdup() 只不过是strlen()mallocmemset 的封装。我说,如果您将此作为学习练习,请使用strlen()memset,这样您就可以了解在直接编辑内存的可怕而强大的世界中事情是如何运作的:) 不,不要使用strncpy 代替strcpystrncpy 是为将字符串插入其他字符串而创建的。因此,它不会终止字符串。那就是用一个问题换取另一个同等规模的问题。 @R.MartinhoFernandes 好吧,似乎不再是:en.cppreference.com/w/c/experimental/dynamic/strdup【参考方案2】:

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

几乎。在 C 中,如果您确定字符串永远不会太长:

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

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

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

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

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

如果检查 stringB 与其他 C 字符串是否相等,多余的空间会影响什么吗?

如果你使用strcmp,那么它将停在最短字符串的末尾,不受额外空格的影响。

【讨论】:

【参考方案3】:

如果你想用纯 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;

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于复制C字符串的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

从连接字符串中获取用户名和密码的正确方法? [复制]

前端复制长字符串卡死

如何正确排序带有数字的字符串? [复制]

检查 False 的正确方法是啥? [复制]

在 HTML 属性中调用带有字符串参数的函数的正确语法是啥? [复制]

清空 C 字符串的正确方法