如何在C语言中遍历字符串?

79

我现在正在尝试这个:

#include <stdio.h>

int main(int argc, char *argv[]) {

    if (argc != 3) {

        printf("Usage: %s %s sourcecode input", argv[0], argv[1]);
    }
    else {
        char source[] = "This is an example.";
        int i;

        for (i = 0; i < sizeof(source); i++) {

            printf("%c", source[i]);
        }
    }

    getchar();

    return 0;
}

这个同样不起作用:

char *source = "This is an example.";
int i;

for (i = 0; i < strlen(source); i++){

    printf("%c", source[i]);
}

我遇到了这个错误:

在Test.exe中的0x5bf714cf地址处(msvcr100d.dll),未处理异常:0xC0000005:访问位置0x00000054时发生访问冲突。

我的代码有什么问题?


7
请不要编辑你所问的代码。那样会使你的问题发生很大改变,导致很多答案都失去了意义。相反,请列出你尝试过的所有方法,并说明哪些是针对解答而进行的。 - Michael Myers
你添加的针对 argc 的新测试是错误的。 - anon
14个回答

88

您想要:

for (i = 0; i < strlen(source); i++) {

sizeof 给出的是指针的大小,而不是字符串的大小。但是,如果您将指针声明为数组,则可以使用它:

char source[] = "This is an example.";

但是如果你将数组传递给函数,它也会衰变为指针。对于字符串,最好始终使用strlen。请注意其他人关于将printf更改为使用%c的建议。另外,考虑到mmyers对效率的评论,最好将调用strlen的操作移出循环:

int len = strlen(source);
for (i = 0; i < len; i++) {

或者重写这个循环:

for (i = 0; source[i] != 0; i++) {

2
嗯,我有点怀疑他想要使用 "strlen" 作为边界。(尽管在一个10行的程序中并没有太大的区别。) - Michael Myers
24
据我所知,strlen函数需要遍历所有字符才能确定字符串的长度。由于在循环过程中char*指针可能会发生改变,因此编译器无法将strlen函数提取出边界检查。这使得循环的时间复杂度从O(n)变为O(n^2)。如果我有错误,请纠正我。 - Michael Myers
1
在这个例子中,一个好的编译器可以确定该字符串不会改变,并省略对strlen的重复调用。但是最好的做法可能是在循环之前将长度存储在一个变量中。 - R.. GitHub STOP HELPING ICE
3
@Neil Butterworth:for (i=0, max=strlen(str); i<max; i++) ... 和 for (i=0; i < strlen(str); i++) ... 的区别在于时间复杂度,前者是O(n),而后者是O(n^2)(除非你已经将其声明为const char *str),因为循环的上限需要在每轮循环中重新计算。请注意保持原意并尽量简洁易懂。 - Vatine
@Valine 但这有关系吗?无论如何,我已经修改了答案来解决这个问题。 - anon
1
@anon 当编写一个打印21个字符的字符串的程序时,这并不重要,但当它确实重要时,O(n^2)和O(n)之间的差异将变得非常明显。 - Elfen Dew

62
一个常见的习语是:
char* c = source;
while (*c) putchar(*c++);

一些注意事项:

  • 在 C 中,字符串是以 null-terminated 结尾的。你需要在读取到空字符之前进行迭代。
  • *c++ 会将 c 增加并返回 c 值。
  • printf("%s") 打印的是以 null 结尾的字符串,而不是单个字符。这就是你访问冲突的原因。

5
我可以使用 while (*c++) putc(*c); 吗? - Incerteza
1
在C语言中,字符串是以零结尾的。这没有意义。也许你想说的是c-字符串应该以null结尾。 - Celeritas
8
@Celeritas: 我觉得这个负评有点严厉——C语言支持以零结尾的字符串,而提问者正在使用这种方式。虽然可以使用计数字符串(例如Windows世界中的“BSTR”),但它们不太常见,并且问题并没有让人产生困惑,因为我们知道在使用哪种字符串类型。 - Alexandre C.
9
哦,你只是在谈论它们的称呼?“Zero-terminated(零结尾)”是Win32 API世界的一个名称(其中字符串变量愚蠢地以“sz”为前缀),而“null-terminated(空结尾)”是更传统(和常见)的名称。不管怎样,它们的意思都是一样的。 - Alexandre C.
2
这个putc是来自<stdio.h>吗?因为这个函数似乎需要第二个参数,即流。putchar可以只使用一个参数,就像这个例子所做的那样。 - Dave Yarwood
显示剩余7条评论

7

一种优化的方法:

for (char character = *string; character != '\0'; character = *++string)
{
    putchar(character); // Do something with character.
}

大多数C字符串都是以空字符'\0'结束的,这意味着一旦字符变成空字符时,循环就应该停止。 *++string将指针移动一个字节,然后对其进行解引用,循环重复。

strlen()相比,这种方法更有效率,因为strlen()已经通过循环来查找字符串的长度,所以使用strlen()会导致循环两次(比实际需要的多一次)。


5

与上面建议使用strlen不同,您可以直接检查空字符:

#include <stdio.h>

int main(int argc, char *argv[])
{
    const char *const pszSource = "This is an example.";
    const char *pszChar = pszSource;

    while (pszChar != NULL && *pszChar != '\0')
    {
        printf("%s", *pszChar);
        ++pszChar;
    }

    getchar();

    return 0;
}

2
它会运行,但我仍建议检查pszChar != '\0'来更加明确。你不是与NULL(通常用于指针)进行比较,而是与空字符进行比较,这用于终止C字符串。(或者,这两者都等于零,所以如果你喜欢的话,pszChar && *pszChar也是一个很好的条件判断)。 - Matt Enright

2

这应该可以工作

 #include <stdio.h>
 #include <string.h>

 int main(int argc, char *argv[]){

    char *source = "This is an example.";
    int length = (int)strlen(source); //sizeof(source)=sizeof(char *) = 4 on a 32 bit implementation
    for (int i = 0; i < length; i++) 
    {

       printf("%c", source[i]);

    }


 }

2

C 字符串的最后一个索引始终是整数值 0,因此有短语“null terminated string”。由于在 C 中,整数 0 等同于布尔值 false,因此您可以使用它来为 for 循环创建简单的 while 子句。当它到达最后一个索引时,它会找到零并将其等同于 false,从而结束 for 循环。

for(int i = 0; string[i]; i++) { printf("Char at position %d is %c\n", i, string[i]); }

2

sizeof(source)返回指针char*所需的字节数。您应该将其替换为strlen(source),这将是您要显示的字符串的长度。

另外,您应该将printf("%s",source[i])替换为printf("%c",source[i]),因为您正在显示一个字符。


2
  1. sizeof() 包括终止空字符。你应该使用 strlen()(但将其放在循环外并保存在变量中),但这可能不是导致异常的原因。
  2. 在 printf 中应该使用 "%c" 而不是 "%s" - 你正在打印一个字符,而不是一个字符串。

1

这篇文章虽然11年前写的,但对于正在学习C语言的人仍然很有参考价值。我不明白为什么我们会就如此基础的问题展开讨论和争议。在C语言中,字符串字面量(例如"引号之间的文本")在最后一个字符之后隐含了一个空字符终止符。不要让名称让你困惑。空字符终止符等于数字0。它的目的正是OP所需要的:

char source[] = "This is an example.";

for (int i = 0; source[i]; i++)
  printf("%c", source[i]);

C语言中的char是一个8位整数,其对应字符的ASCII值为正整数。这意味着source[i]在char[19]之前是一个正整数,而char[19]是最后一个'.'后面的空终止符。空字符的ASCII值为0。这就是循环终止的地方。该循环遍历每个字符,而不考虑数组的长度。


1

只需将 sizeof 改为 strlen。

像这样:

char *source = "This is an example.";
int i;

for (i = 0; i < strlen(source); i++){

    printf("%c", source[i]);

}

1
这段代码仍然会因为在 printf 中传递的 %s 和 char 而引发异常。 - ULysses

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接