关于C字符串的问题

我是C的新手,我对C字符串非常困惑。 以下是我的问题。

从字符串中查找最后一个字符

如何找出字符串中的最后一个字符? 我带来了类似的东西,

char *str = "hello"; printf("%c", str[strlen(str) - 1]); return 0; 

这是要走的路吗? 我不知何故认为,这不是正确的方法,因为strlen必须遍历字符以获得长度。 因此该操作将具有O(n)复杂度。

char转换为char*

我有一个字符串,需要附加一个字符。 我怎样才能做到这一点? strcat只接受char* 。 我试过以下,

 char delimiter = ','; char text[6]; strcpy(text, "hello"); strcat(text, delimiter); 

strcat与具有局部范围的变量一起使用

请考虑以下代码,

 void foo(char *output) { char *delimiter = ','; strcpy(output, "hello"); strcat(output, delimiter); } 

在上面的代码中, delimiter是一个局部变量,它在foo返回后被销毁。 可以将它附加到变量output吗?

strcat如何处理null终止字符?

如果我连接两个空终止字符串, strcat会将两个空终止字符附加到结果字符串吗?

是否有一篇很好的初级水平的文章解释了字符串如何在C中工作以及如何执行常用的字符串操作?

任何帮助都会很棒!

  1. 最后一个字符:你的方法是正确的。 如果您需要在大字符串上执行此操作,则包含字符串的数据结构应与它们存储长度。 如果不是,那就是O(n)并不重要。

  2. 附加一个字符:你有几个错误。 首先,你的缓冲区太小而无法容纳另一个角色。 至于如何调用strcat,你可以将字符放在一个字符串中(一个包含2个条目的数组,第二个为0),或者你可以手动使用长度将字符写入结尾。

  3. 你对2个终止符的担心是没有根据的。 虽然它占用与字符串相邻的内存并且是必要的,但是在长度意义上,末尾的nul字节不是“字符串的一部分”,等等。它纯粹是结束的标记。 strcat将覆盖旧的nul并在连接字符串之后在最后添加一个新的nul。 同样,在调用strcat之前,您需要确保缓冲区足够大!

  1. 由于C字符串的工作方式,O(n)是你能做的最好的事情。
  2. char delimiter[] = ","; 。 这使分隔符成为一个包含逗号和NUL的字符数组。此外,文本需要长度为7. hello为5,然后你有逗号和NUL。
  3. 如果你正确定义分隔符,那很好(因为你将一个字符分配给指针,这是错误的)。 输出的内容以后不依赖于分隔符。
  4. 它会覆盖第一个NUL。

你走在正确的轨道上。 我强烈建议你阅读K&R C第二版。 它将帮助您使用字符串,指针等。 不要忘记手册页和文档。 他们会很清楚地回答像strcat这样的问题。 两个不错的网站是The Open Group和cplusplus.com。

“C string”实际上是一个简单的char数组, str[0]包含第一个字符, str[1]包含第二个字符,依此类推。 在最后一个字符之后,该数组包含一个元素,该元素保持为零。 按惯例,此零表示字符串的结尾。 例如,这两行是等价的:

 char str[] = "foo"; //str is 4 bytes char str[] = {'f', 'o', 'o', 0}; 

现在问你的问题:

从字符串中查找最后一个字符

你的方式是正确的。 没有更快的方法来知道字符串结束的位置比扫描它以找到最终的零。

将char转换为char *

如前所述,“字符串”只是一个char的数组,在末尾添加了一个零终止符。 因此,如果你想要一个字符串,你声明一个包含两个 char的数组 – 你的角色和最后的零,如下所示:

 char str[2]; str[0] = ','; str[1] = 0; 

或者干脆:

 char str[2] = {',', 0}; 

将strcat与具有局部范围的变量一起使用

strcat()只是将源数组的内容复制到目标数组,位于目标数组中空字符的偏移处。 因此,操作后源会发生什么变得无关紧要。 但是你需要担心目标数组是否足以保存数据 – 否则strcat()将覆盖数组后面的内存中的任何数据! 所需的大小是strlen(str1) + strlen(str2) + 1

strcat如何处理null终止字符?

预期最后的零将终止两个输入字符串,并附加到输出字符串。

从字符串中查找最后一个字符

我提出了一个思想实验:如果通常可以在O(n)时间内找到字符串的最后一个字符,那么你是否还能在O(n)时间内实现strlen

char转换为char*

您暂时可以将char存储在char -array数组中,并且会衰减为指向char的指针:

 char delimiterBuf[2] = ""; delimiterBuf[0] = delimiter; ... strcat(text, delimiterBuf); 

但是,如果您只是使用字符文字,则可以简单地使用字符串文字。

strcat与具有局部范围的变量一起使用

变量本身不在范围之外引用。 函数返回时,该局部变量已被评估,其内容已被复制。

strcat如何处理null终止字符?

C中的“字符串”是NUL终止的字符序列。 strcat两个输入必须以NUL终止,结果将以NUL终止。 如果不需要, strcat向结果写一个额外的NUL字节是没有用的。

(如果你想知道如果输入字符串已经有多个尾随的NUL字节,我提出另一个思想实验: strcat如何知道字符串中有多少尾随NUL字节?)

顺便说一句,既然您用“最佳实践”标记了这一点,我还建议您注意不要写入目标缓冲区的末尾。 通常这意味着避免strcatstrcpy (除非你已经检查过输入字符串不会溢出目标)并使用更安全的版本(例如strncat 。注意strncpy有自己的缺陷,所以这是一个很差的替代品。还有更安全的非标准版本,例如strlcpy / strlcatstrcpy_s / strcat_s 。)

类似地,像foo函数这样的函数总是应该使用一个额外的参数来指定目标缓冲区的大小(并且文档应该明确说明该大小是否考虑了NUL终结符)。

如何找出字符串中的最后一个字符?

你用str[strlen(str) - 1]技术很好。 正如所指出的,你应该避免重复,不必要的strlen调用并存储结果。

我不知何故认为,这不是正确的方法,因为strlen必须遍历字符以获得长度。 因此该操作将具有O(n)复杂度。

重复调用strlen可能是C程序的祸根。 但是,您应该避免过早优化。 如果一个探查器实际上演示了strlen价格昂贵的热点,那么你可以为你的文字字符串案例做这样的事情:

 const char test[] = "foo"; sizeof test // 4 

当然,如果你在堆栈上创建’test’,它会产生一些开销(递增/递减堆栈指针),但不涉及线性时间操作。

文字字符串通常不会如此巨大。 对于从文件中读取大字符串的其他情况,您可以提前存储字符串的长度,但这是一个示例,以避免重新计算字符串的长度。 这也很有用,因为它会事先告诉你为字符缓冲区分配多少内存。

我有一个字符串,需要附加一个字符。 我怎样才能做到这一点? strcat只接受char *。

如果你有一个char并且不能用它来创建一个字符串(char * c =“a”),那么我相信你可以使用strncat(需要validation):

 char ch = 'a'; strncat(str, &ch, 1); 

在上面的代码中,分隔符是一个局部变量,它在foo返回后被销毁。 可以将它附加到变量输出吗?

是的:像strcat和strcpy这样的函数会生成源字符串的深层副本 。 它们不会留下浅指针,因此在执行这些操作后,本地数据被销毁是很好的。

如果我连接两个空终止字符串,strcat会将两个空终止字符附加到结果字符串吗?

不,strcat基本上会覆盖dest字符串上的null终止符并写入它,然后在它结束时附加一个新的null终止符。

如何找出字符串中的最后一个字符?

你的方法几乎是正确的。 找到C字符串结尾的唯一方法是遍历字符,寻找nul。

你的答案中有一个错误(在一般情况下)。 如果strlen(str)为零,则在字符串开头之前访问该字符。

我有一个字符串,需要附加一个字符。 我怎样才能做到这一点?

你的方法是错的。 AC字符串只是一个C字符数组,最后一个是'\0' 。 所以从理论上讲,你可以追加一个这样的角色:

 char delimiter = ','; char text[7]; strcpy(text, "hello"); int textSize = strlen(text); text[textSize] = delimiter; text[textSize + 1] = '\0'; 

但是,如果我这样离开,我会得到数以万计的投票,因为有三个地方我有潜在的缓冲区溢出(如果我不知道我的初始字符串是“你好”)。 在进行复制之前,您需要检查文本是否足以包含字符串中的所有字符加上一个用于分隔符加一个用于终止nul。

… delimiter是一个局部变量,在foo返回后会被销毁。 可以将它附加到变量输出吗?

是的,没关系。 strcat复制字符。 但是你的代码示例没有检查输出是否足以容纳你所投入的所有东西。

如果我连接两个空终止字符串,strcat会将两个空终止字符附加到结果字符串吗?

没有。

我不知何故认为,这不是正确的方法,因为strlen必须遍历字符以获得长度。 因此该操作将具有O(n)复杂度。

你正确地读了Joel Spolsky 为什么C字符串很糟糕 。 围绕它的方法很少。 方法包括不使用C字符串(例如使用Pascal字符串并创建自己的库来处理它们),或者不使用C(使用C ++,它有一个字符串类 – 由于不同的原因,它很慢,但你也可以写你自己处理Pascal字符串比在C中更容易处理,例如)

关于向C字符串添加char; C字符串只是一个带有nul终结符的char数组,只要你保留终结符就是一个字符串,就没有魔法。

 char* straddch( char* str, char ch ) { char* end = &str[strlen(str)] ; *end = ch ; end++ ; *end = 0 ; return str ; } 

就像strcat()一样,你必须知道创建str的数组足够长以容纳更长的字符串,编译器也无法帮助你。 它既不优雅又不安全。

如果我连接两个空终止字符串,strcat会将两个空终止字符附加到结果字符串吗?

不,只是一个,但随之而来的可能只是偶然,或者在记忆中发生的事情。 考虑以下等价物:

 char* my_strcat( char* s1, const char* s2 ) { strcpy( &str[strlen(str)], s2 ) ; } 

s2的第一个字符覆盖s1中的终结符。

在上面的代码中,分隔符是一个局部变量,它在foo返回后被销毁。 可以将它附加到变量输出吗?

在您的示例中, delimiter不是字符串,并且使用char初始化指针是没有意义的。 但是如果它是一个字符串,代码就可以了,strcat()从第二个字符串复制数据,所以第二个参数的生命周期是无关紧要的。 当然,您可以在您的示例中使用char(不是char *)和上面建议的straddch()函数。