关于C字符串的问题

时间:2010-06-30 09:43:25

标签: c string

我是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如何处理空终止字符?

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

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

任何帮助都会很棒!

7 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:5)

  1. O(n)是你能做的最好的,因为C字符串的工作方式。
  2. char delimiter[] = ",";。这使得分隔符包含逗号和NUL的字符数组。此外,文本需要长度为7. hello为5,然后你有逗号和NUL。
  3. 如果你正确定义分隔符,那很好(因为你将一个字符分配给一个指针,这是错误的)。输出的内容以后不依赖于分隔符。
  4. 它将覆盖第一个NUL。
  5. 你走在正确的轨道上。我强烈建议您阅读K&R C第2版。它将帮助您使用字符串,指针等。不要忘记手册页和文档。他们会非常清楚地回答strcat上的问题。两个不错的网站是The Open Group和cplusplus.com。

答案 2 :(得分:3)

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

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

现在问你的问题:

从字符串

中查找最后一个字符

你的方式是正确的。没有更快的方法来知道字符串结束的位置,而不是通过扫描来找到最终的零。

将char转换为char *

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

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

或者简单地说:

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

将strcat用于具有本地范围的变量

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

strcat如何处理null终止字符?

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

答案 3 :(得分:1)

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

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

char转换为char*

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

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

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

strcat用于具有本地范围的变量

变量本身不在范围之外引用。当函数返回时, 已经评估了局部变量并且其内容已经存在 复制。

strcat如何处理空终止字符?

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

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

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

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

答案 4 :(得分:1)

  

我怎样才能找到最后一个角色   从字符串?

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

  我不知何故认为,这不是   正确的方法,因为strlen必须   迭代字符以获得   长度。所以这个操作会有一个   O(n)复杂性。

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

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

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

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

  

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

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

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

在上面的代码中,分隔符是本地的   之后被破坏的变量   foo回来了。可以追加它   变量输出?

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

  

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

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

答案 5 :(得分:1)

  

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

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

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

  

我有一个字符串,需要为它添加一个字符。我怎么能这样做?

你的做法是错误的。 C字符串只是一个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会将两个空终止字符附加到结果字符串吗?

没有

答案 6 :(得分:1)

  

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

你在why C-strings suck读了Joel Spolsky。围绕它的方法很少。方法包括不使用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的数组足够长以容纳更长的字符串,编译器不会帮助你。它既不优雅又不安全。

  

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

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

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

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

  

在上面的代码中,分隔符是本地的   之后被破坏的变量   foo回来了。可以追加它   变量输出?

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