数组
- 声明数组必须要去指定数组长度
#include <stdio.h>
int main() {
// 主动声明长度
int arr1[3] = {1, 3, 5};
// 长度由内容确定(编译器自动计算长度)
int arr2[] = {2, 4, 6, 8};
printf("arr1[0] = %d \n", arr1[0]); // 1
printf("arr2[3] = %d \n", arr2[3]); // 8
}遍历数组
同其他编程语言一样, 但是在 c 语言中, 无法直接获取数组的长度, 比如 js 的 arr.length 属性, 需要手动声明需要遍历多少次, 所以我推荐, 在使用数组的时候, 先声明数组的长度
#include <stdio.h>
int main() {
int arr[] = {2, 4, 6, 8};
for (int i = 0; i < 4; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
}#include <stdio.h>
int main() {
// 注意: 必须加 const, 否则 len 是变长数组(VLA), 不能带初始化器
const int len = 4;
int arr[len] = {2, 4, 6, 8};
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
}推荐先声明数组长度, 这样需要遍历多少次一目了然, 也不容易写错
二维数组
数组的元素也可以是数组, 这就是二维数组, 可以理解为"表格"
#include <stdio.h>
int main() {
// 3 行 4 列的二维数组
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 遍历二维数组(双重循环)
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
}字符串
字符串的本质
字符串的本质就是 char 数组, 编译器会在最后自动增加一个 '\0' 元素作为结束标志
#include <stdio.h>
int main() {
// 直接声明字符串(编译器自动加上 '\0')
char str1[] = "hello";
printf("str1 %s\n", str1);
// 使用数组声明字符串, 注意最后一个元素 '\0' 不能少
char str2[] = {'h', 'e', 'l', 'l', 'o', '\0'};
printf("str2 %s\n", str2);
// 如果少了 '\0', printf 会一直往后读到内存中的 '\0' 为止
char str3[] = {'h', 'i'};
printf("str3 %s\n", str3);
}为什么需要
'\0'?因为字符串没有"长度"属性, 程序只能靠
'\0'来判断字符串在哪里结束, 所以所有字符串处理函数都是遇到'\0'才停下来的
获取字符串长度
注意需要导入头文件 <string.h>
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello";
int len = strlen(str);
printf("length is %d \n", len); // 5
}
sizeof和strlen的区别
strlen: 字符串的长度(不包含'\0')sizeof: 整个数组占用的字节数(包含'\0', 也叫数组容量)
#include <stdio.h>
#include <string.h>
int main() {
char str[64] = "hello";
printf("strlen = %zu \n", strlen(str)); // 5 (内容长度, 不含 '\0')
printf("sizeof = %zu \n", sizeof(str)); // 64 (整个数组占用的空间)
}字符串常用函数
| 函数 | 作用 |
|---|---|
| strcpy | 复制字符串(覆盖) |
| strcat | 拼接字符串 |
| strcmp | 比较字符串(相等返回 0) |
| strstr | 查找子串, 返回指针 |
#include <stdio.h>
#include <string.h>
int main() {
char str[64] = "hello";
// strcat: 拼接(在末尾追加)
strcat(str, " world");
printf("%s\n", str); // hello world
// strcpy: 复制(会覆盖目标数组原来的内容)
char copy[64];
strcpy(copy, str);
printf("%s\n", copy); // hello world
// strcmp: 比较, 相等返回 0, 常用于判断两个字符串是否相同
if (strcmp(str, copy) == 0) {
printf("两个字符串相等\n"); // 两个字符串相等
}
// strstr: 查找子串, 找到返回子串位置的指针, 没找到返回 NULL
char *found = strstr(str, "world");
if (found != NULL) {
printf("找到了: %s\n", found); // 找到了: world
}
}正则表达式
正则表达式(Regular Expression)是用来匹配字符串的规则
比如要校验用户输入的手机号: 需要判断是否是 11 位、是否全是数字、是否以 1 开头, 用 if 一个个判断非常麻烦, 而正则表达式用一行规则就能搞定
C 语言标准本身没有正则, 但是 POSIX 标准提供了 <regex.h> 头文件, Linux/macOS 自带
基本语法
POSIX 扩展正则(配合 REG_EXTENDED 标志)常用语法:
| 语法 | 作用 | 示例 |
|---|---|---|
| 普通字符 | 匹配字符本身 | a 匹配 "a" |
. | 匹配任意单个字符 | a.c 匹配 "abc" |
[...] | 字符集合 | [0-9] 匹配数字 |
[^...] | 取反字符集合 | [^0-9] 匹配非数字 |
* | 前一个字符出现 0 次或多次 | ab*c 匹配 "ac"/"abbc" |
+ | 前一个字符出现 1 次或多次 | ab+c 匹配 "abc"/"abbc" |
? | 前一个字符出现 0 次或 1 次 | ab?c 匹配 "ac"/"abc" |
{n,m} | 前一个字符出现 n 到 m 次 | a{2,3} 匹配 "aa"/"aaa" |
^ | 匹配行首 | ^hello 以 hello 开头 |
$ | 匹配行尾 | world$ 以 world 结尾 |
(...) | 分组(可以提取内容) | (ab)+ 匹配 "ab"/"abab" |
| | 或 | cat|dog 匹配 cat 或 dog |
注意: POSIX 正则和 JavaScript/Python 的正则不太一样
- 不支持
\d\w\s这种简写, 数字要写[0-9]或[[:digit:]]- 默认是"基础正则"(BRE),
+?|()这些语法需要加REG_EXTENDED标志才能用
匹配流程
使用 POSIX 正则分三步: regcomp 编译 → regexec 匹配 → regfree 释放
| 函数 | 作用 |
|---|---|
| regcomp | 编译正则表达式 |
| regexec | 执行匹配, 返回 0 表示匹配成功 |
| regfree | 释放编译后的正则(必须调用) |
| regerror | 获取错误信息 |
#include <stdio.h>
#include <regex.h>
int main() {
regex_t regex;
// 1. 编译正则表达式
// REG_EXTENDED: 使用扩展正则(支持 + ? | 分组等)
// 返回 0 表示编译成功
int ret = regcomp(®ex, "^h.*o$", REG_EXTENDED);
if (ret != 0) {
printf("正则编译失败\n");
return 1;
}
// 2. 执行匹配, regexec 返回 0 表示匹配成功
ret = regexec(®ex, "hello", 0, NULL, 0);
if (ret == 0) {
printf("匹配成功\n"); // 匹配成功
} else {
printf("匹配失败\n");
}
// 3. 释放资源(不调用会造成内存泄漏)
regfree(®ex);
}提取匹配的内容
regexec 的第四个参数是 regmatch_t 数组, 用来保存匹配到的位置
| 字段 | 作用 |
|---|---|
| rm_so | 匹配内容开始的位置(下标) |
| rm_eo | 匹配内容结束的位置(下标) |
#include <stdio.h>
#include <regex.h>
#include <string.h>
int main() {
regex_t regex;
regmatch_t match[1]; // 只需要第 0 组(整个匹配)
regcomp(®ex, "[0-9]+", REG_EXTENDED);
const char *text = "订单号: 10086, 金额: 99";
int ret = regexec(®ex, text, 1, match, 0);
if (ret == 0) {
// 根据匹配到的位置, 从原字符串中截取内容
int start = match[0].rm_so;
int end = match[0].rm_eo;
char result[32] = {0};
strncpy(result, text + start, end - start);
printf("匹配到的内容: %s\n", result); // 匹配到的内容: 10086
} else {
printf("没有匹配到\n");
}
regfree(®ex);
}分组捕获
用 (...) 分组之后, regmatch_t 数组的每一项就对应一个分组(第 0 项是整个匹配)
#include <stdio.h>
#include <regex.h>
#include <string.h>
int main() {
regex_t regex;
regmatch_t match[3]; // 第 0 组是整个匹配, 第 1/2 组是括号里的内容
// 两个分组: (4位数字)-(2位数字)
regcomp(®ex, "([0-9]{4})-([0-9]{2})", REG_EXTENDED);
const char *text = "日期: 2026-06";
int ret = regexec(®ex, text, 3, match, 0);
if (ret == 0) {
for (int i = 0; i < 3; i++) {
char buf[32] = {0};
strncpy(buf, text + match[i].rm_so, match[i].rm_eo - match[i].rm_so);
printf("第%d组: %s\n", i, buf);
}
// 第0组: 2026-06
// 第1组: 2026
// 第2组: 06
}
regfree(®ex);
}匹配所有结果
使用循环, 每次把游标移动到上次匹配结束的位置, 继续找下一个
#include <stdio.h>
#include <regex.h>
#include <string.h>
int main() {
regex_t regex;
regmatch_t match[1];
regcomp(®ex, "[0-9]+", REG_EXTENDED);
const char *text = "今年是2026年, 有12个月, 365天";
const char *cursor = text; // 游标: 从哪个位置开始匹配
while (regexec(®ex, cursor, 1, match, 0) == 0) {
char buf[32] = {0};
strncpy(buf, cursor + match[0].rm_so, match[0].rm_eo - match[0].rm_so);
printf("数字: %s\n", buf); // 数字: 2026 / 数字: 12 / 数字: 365
// 移动游标到本次匹配结束的位置, 继续找下一个
cursor += match[0].rm_eo;
}
regfree(®ex);
}注意点
regfree一定要调用, 否则会内存泄漏- Windows 的 MSVC 没有
<regex.h>, 需要使用第三方库(如 PCRE)- POSIX 正则不支持
\d\w简写, 数字用[0-9]或[[:digit:]]
获取用户输入
#include <stdio.h>
int main() {
// 获取用户输入(数字)
int num;
scanf("%d", &num);
printf("your input is: %d \n", num);
// 获取用户输入(浮点数)
float fnum;
scanf("%f", &fnum);
printf("your input is: %f \n", fnum);
}注意:
scanf读取字符串时, 遇到空格会停下来, 所以只能读取"单词"
#include <stdio.h>
int main() {
char name[32];
scanf("%s", name); // 输入: tom jack
printf("name is: %s \n", name); // 只会输出: tom
}猜数字游戏
结合前面学过的内容, 写一个完整的程序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 可以先不管这些是什么, 只需要知道
// 这个方法生成指定范围内的随机数字
int range(int min, int max) {
srand(time(NULL));
return rand() % (max - min + 1) + min;
}
int main() {
printf("===== 猜数字游戏 =====\n");
// 数字范围
int min = 1;
int max = 9;
// 谜底数字
int secret_num = range(min, max);
// 输入次数控制
int guess_changes = 3;
printf("请输入一个数字(%d-%d), 你有%d次机会\n", min, max, guess_changes);
for (int i = 0; i < guess_changes; i++) {
// 获取用户输入(数字)
int input_num;
scanf("%d", &input_num);
if (input_num > secret_num) {
printf("你猜的数字太大了\n");
} else if (input_num < secret_num) {
printf("你猜的数字太小了\n");
} else {
printf("恭喜你猜对了, 你赢了\n");
break;
}
if (i == guess_changes - 1) {
printf("很遗憾, 你的机会用完了\n");
}
}
}