C: 字符串基础

C语言里的字符串就像一条以空字符结尾的项链——每颗珠子是一个字符,最后一颗是透明的终止符\0,告诉别人"项链到此结束"。

1. 字符数组与字符串

C语言没有专门的字符串类型,字符串本质是\0(空字符)结尾的字符数组\0的ASCII码为0,它标志着字符串的结束。

C
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

这个数组有6个元素:5个可见字符加1个\0。没有\0,就只是普通字符数组,不能当作字符串使用。

字符串字面量(双引号)自动在末尾添加\0

C
char str[] = "Hello";

数组长度为6(5个字符 + 1个\0),编译器自动计算。也可以指定更大长度,多余位置填\0

C
char str[10] = "Hello";

str[5]str[9]都是\0

💡 单引号'H'是字符(占1字节),双引号"Hello"是字符串(隐含\0),两者不同。


2. 字符串声明与初始化

(1) 字符数组方式

C
char s1[] = "abc";
char s2[10] = "abc";
char s3[] = {'a', 'b', 'c', '\0'};

三种方式等价于字符串"abc"。注意s3必须手动加\0

(2) 字符指针方式

C
const char *s4 = "abc";

s4指向字符串常量的首字符。字符串常量存储在只读区域,尝试修改会导致未定义行为:

C
s4[0] = 'A';

这是错误的。而字符数组是可修改的:

C
s1[0] = 'A';

合法,s1的内容变为"Abc"。

⚠️ 用指针指向字符串常量时加const修饰,编译器会在你尝试修改时给出警告。

(3) 指针与数组的区别

C
char arr[] = "hello";
const char *ptr = "hello";

3. 字符串输入输出

(1) printf和scanf

%s格式说明符输入输出字符串:

C
char name[20];
scanf("%s", name);
printf("你好, %s!\n", name);

scanf读字符串遇到空白符(空格、换行、制表符)就停止,所以无法读入包含空格的整行。

scanf不会检查缓冲区大小,输入超过数组长度会导致越界。可以限制读取长度:

C
scanf("%19s", name);

最多读19个字符,留1个位置给\0

(2) puts和gets

puts输出字符串并自动加换行:

C
char msg[] = "Hello";
puts(msg);

等价于printf("%s\n", msg);

gets读取一整行(包括空格),直到换行符:

C
char line[100];
gets(line);
🔥 永远不要用gets! 它不检查缓冲区大小,输入超过数组容量就直接越界写入,是最危险的C函数之一。C11标准已将其彻底移除。

(3) fgets

fgetsgets的安全替代:

C
char line[100];
fgets(line, sizeof(line), stdin);

参数依次为:存储缓冲区、最大读取字节数(含\0)、输入流。fgets会保留换行符在字符串末尾(如果读到了换行符且还有空间)。

去除末尾换行符的常用写法:

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

int main(void) {
    char line[100];
    fgets(line, sizeof(line), stdin);

    int len = strlen(line);
    if (len > 0 && line[len - 1] == '\n') {
        line[len - 1] = '\0';
    }

    printf("[%s]\n", line);
    return 0;
}

▶ 示例

读取用户姓名和一行自我介绍:

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

void trim_newline(char *s) {
    int len = strlen(s);
    if (len > 0 && s[len - 1] == '\n') {
        s[len - 1] = '\0';
    }
}

int main(void) {
    char name[30];
    char intro[200];

    printf("请输入姓名: ");
    fgets(name, sizeof(name), stdin);
    trim_newline(name);

    printf("请输入自我介绍: ");
    fgets(intro, sizeof(intro), stdin);
    trim_newline(intro);

    printf("\n--- 个人信息 ---\n");
    printf("姓名: %s\n", name);
    printf("介绍: %s\n", intro);

    return 0;
}
▶ 试一试
TEXT 📖 仅展示
请输入姓名: 张三
请输入自我介绍: 我是一名C语言初学者

--- 个人信息 ---
姓名: 张三
介绍: 我是一名C语言初学者

4. strlen函数

strlen声明在<string.h>中,返回字符串的长度(不含\0):

C
#include <string.h>

char s[] = "Hello";
printf("%zu\n", strlen(s));

输出5。注意sizeof(s)是6(含\0),strlen(s)是5(不含\0)。

strlen的工作原理是从首字符开始逐个计数,直到遇到\0

C
size_t my_strlen(const char *s) {
    size_t len = 0;
    while (s[len] != '\0') {
        len++;
    }
    return len;
}

如果字符数组没有\0结尾,strlen会一直读下去,直到偶然遇到0字节,这是很危险的。


5. 字符指针初识

字符指针可以指向字符数组或字符串常量:

C
char arr[] = "world";
const char *ptr = "world";

ptr = arr;
printf("%s\n", ptr);

指针可以被重新赋值指向不同的字符串,而数组名是常量不可修改:

C
ptr = "another string";

合法。但:

C
arr = "another string";

非法,数组名不能赋值。

遍历字符串的指针方式:

C
const char *p = "Hello";
while (*p != '\0') {
    printf("%c", *p);
    p++;
}
printf("\n");

*p解引用得到当前字符,p++移动到下一个字符的位置。指针的步长由指向类型决定,char *每步移动1字节。

▶ 示例

用字符指针实现字符串逆序输出:

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

void print_reverse(const char *s) {
    const char *p = s + strlen(s) - 1;
    while (p >= s) {
        putchar(*p);
        p--;
    }
    putchar('\n');
}

int main(void) {
    print_reverse("Hello");
    print_reverse("ABCDEF");
    return 0;
}
▶ 试一试
TEXT 📖 仅展示
olleH
FEDCBA

先让指针p跳到字符串最后一个字符(\0前面一位),然后从后向前逐个输出。

❓ 常见问题

Q 'a'"a"有什么区别?
A 'a'是字符常量,占1字节;"a"是字符串常量,占2字节('a'加'\0'),类型是char[]
Q sizeofstrlen对字符串结果为什么不同?
A sizeof返回数组占内存总字节数(含\0),strlen返回\0之前的字符个数。
Q fgets读到换行符怎么办?
A fgets会把换行符存入缓冲区。通常需要手动检查并替换为\0来去除。
Q 字符指针指向字符串常量后能修改内容吗?
A 不能。字符串常量存放在只读段,修改行为未定义。需要修改请用字符数组。

📖 小节

📝 作业

  1. 编写程序,用fgets读取一行文本,统计其中字母、数字和其他字符的个数。
  2. 编写函数void str_replace_char(char *s, char old, char new_ch),将字符串s中所有old字符替换为new_ch。
  3. 编写函数用字符指针方式计算字符串长度,不使用strlen
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏