张银峰的编程课堂

字符串函数

文本处理是很常见的任务,C语言通过库函数为此提供支持,为使用这些函数,需要包含 string.h 头文件。

#include <string.h>

这里是一些常用函数列表

  • strlen:返回字符串长度
  • strcpy:拷贝字符串
  • strcat:拼接字符串
  • strcmp:比较字符串

这里是一个综合示例

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

void test_strlen()
{
    const char *msg = "C tutorials";
    printf("======================= strlen\n");
    printf("glimix.com: %d\n", strlen("glimix.com"));
    printf("C tutorials: %d\n", strlen(msg));
}

void test_strcpy()
{
    char result[64];
    const char *message = "do it yourself";

    printf("======================= strcpy\n");

    // 把message中的内容拷贝到result中
    strcpy(result, message);
    printf("%s\n", result);

    // 直接拷贝常量字符串
    printf("%s\n", strcpy(result, "hello world!"));

    char *p;
    // 错误:p仅是一个指针,没有有效的指向,因此不能拷贝。
    // 这里有效的指向不仅是指向一个合法的地址,
    // 并且被指向的地址的存储空间要能够包含源字符串。
    //strcpy( p, "hello, world!" );
    p = result;
    printf("%s\n", strcpy(p, "glimix.com"));
}

void test_strcat()
{
    printf("======================= strcat\n");

    char website[64];
    strcpy(website, "website: ");
    strcat(website, "glimix.com");
    printf("%s\n", website);

    char homepage[64] = "";
    strcat(homepage, "website: ");
    strcat(homepage, "glimix.com");
    printf("%s\n", website);
}

void test_strcmp()
{
    printf("======================= strcmp\n");
    printf("(ab, ac): %d\n", strcmp("ab", "ac"));
    printf("(ab, AB): %d\n", strcmp("ab", "AB"));
    printf("(xy, ab): %d\n", strcmp("xy", "ac"));
    printf("(v9, v9): %d\n", strcmp("v9", "v9"));

    printf("======================= stricmp\n");
    printf("(ab, Ab): %d\n", stricmp("ab", "Ab"));
}

int main()
{
    test_strlen();
    test_strcpy();
    test_strcat();
    test_strcmp();
    return 0;
}

glimix.com

  • strcpy(目标字符串, 源字符串):负责把源字符串拷贝到目标字符串。作为程序员,我们要负责保证目标字符串的存储空间能包容源字符串及结束符'\0'。

  • strcat(目标字符串, 源字符串):用于将源字符串拼接到目标字符串之后。对于website的构建,我们先strcpy再strcat;而对于homepage则是使用两个strcat完成同等工作。区别这于homepage被初始化为空字符串,这样strcat追加时,可以找到初始字符串的结束符,而website是一个未初始化的变量,里面充斥了随机值。

  • strcmp(字符串1, 字符串2):按当前的编码规则比较两个字符串的内容,相同时返回0;小于时返回-1,大于返回1。字符编码是个复杂的概念,这些比较操作是按照系统使用的语言编码规则比较(比如GB2312)。在ASCII码规则下,'b'在'c'之前,即b的ASCII码小于c,因此第一个比较操作结果为-1。接下来两个字符串语义一样,但小写字母的ASCII码值大于大写字母,所以结果是1。

  • stricmp(字符串1, 字符串2):当我们需要忽略大小写比较字符串时,可以使用此函数。

may be unsafe?

当你编译程序时,可能会出现类似如下的错误:

glimix.com

使用VS创建的项目,默认会启用【安全开发生命周期(SDL)检查】选项,将其设置为否即可。

glimix.com

但此时编译程序时,【输出】或【错误列表】窗口会反馈很多警告信息。提示我们使用的这些C语言字符串操作函数是不安全的,并建议使用对应的安全版本。这些安全版本的字符串函数大多以 _s 结尾,在使用需要指出目标字符串的大小。

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

int main()
{
    char msg[6];

    strcpy_s(msg, 6, "hello");
    printf("%s\n", msg);

    strcpy_s(msg, 6, "glimix");
    printf("%s\n", msg);

    return 0;
}

glimix.com

msg的大小是容纳包含'\0'在内的6个字符的空间,第一个strcpy_s拷贝"hello"时空间刚刚好;但对于第二次拷贝,空间太小不足以容纳"glimix"字符串,C语言运行时库给出错误提示对话框,指示:缓冲区太小了。

Expression: (L"Buffer is to samll" && 0)

建议掌握安全字符串函数的使用,不过在之后的教程中,我们通常会关闭SDL检查。