义乌外贸网站建设行吗手机网站样式代码
目录
一、字符串就是字符数组
1.字符串的声明和初始化
2.字符串的输入和输出
2.1 字符串的输入
使用 scanf
使用 gets(不推荐,存在安全隐患)
使用 fgets
2.2 字符串的输出
使用 printf
使用 puts
方式2 和 方式3 的区别
方式2 是字符串变量,允许数据被修改
方式3 是字符串常量,不允许数据被修改
注意指针的操作
二、字符串、字符数组和整型数组在存储上的区别
代码解析
三、sizeof和strlen的区别(重点)
1.sizeof
3. 主要区别
4.代码示例
编辑
5. 常见误区
总结
四、动态开辟字符串
动态开辟字符串常用API
malloc 函数
realloc 函数
参数说明:
返回值:
检查返回值:
1. 失败原因分析
2. 正确处理流程
2. 检查返回值是否为 NULL
3. 失败后的操作选择
示例:保留原内存并报错
示例:释放原内存并退出
memset 函数
free 函数
1. 动态开辟字符串的核心步骤
2. 常用动态内存分配函数
3. 动态开辟字符串的示例
使用 malloc 动态开辟字符串
使用 memset 清空字符串
使用 free 释放内存空间
使用 realloc 扩容内存
五、字符串常用API
拷贝函数(strcpy 和 strncpy)
strcpy
功能
函数原型
参数
返回值
特点
编程示例
编程案例
strncpy
功能
函数原型
参数
返回值
特点
编程示例
编程案例
strcpy 和 strncpy 的区别
断言函数(assert)
核心作用
基本用法
使用场景
关键注意事项
编程示例
断言的值为真,程序正常执行
断言的值为假,终止程序,输出错误信息
拼接函数(strcat 和 strncat)
strcat
1. 函数原型
2. 关键要求
编程示例
编程案例
strncat
1. 函数原型
2. 核心特性
2.1. 安全机制
2.2. 参数说明
编程示例
编程案例
字符串比较函数(strcmp 和 strncmp)
strcmp
函数原型:
参数:
返回值:
工作原理:
编程示例
编程案例
strncmp
函数原型
参数
返回值
注意事项
与 strcmp 的区别
适用场景
编程示例
查找子字符(strchr)
函数原型
参数
返回值
注意事项
编程示例
查找子串(strstr)
函数原型
参数
返回值
注意事项
与 strchr 的区别
编程示例
字符串分割(strtok)
函数原型
参数
返回值
注意事项
编程示例
代码解释
一、字符串就是字符数组
在C语言中,字符串是由字符数组表示的,通常以空字符 \0 结尾。
1.字符串的声明和初始化
//字符数组、字符串变量
char str1[] = {'h', 'e', 'l', 'l', 'o'};//方式1:逐个字符初始化
char str2[] = "hello";//方式2:直接赋值
char str4[128] = {'\0'};//方式4:部分赋初值//字符串常量
char *pstr = "hello";//方式3
一般使用 方式2 和 方式3 的初始化方式
2.字符串的输入和输出
2.1 字符串的输入
使用 scanf
char str[100];
printf("Enter a string: ");
scanf("%s", str); // 输入字符串,遇到空格会停止读取
-
scanf会从输入中读取字符,直到遇到空格或换行符。 -
不会读取空格后的内容。
使用 gets(不推荐,存在安全隐患)
char str[100];
printf("Enter a string: ");
gets(str); // 读取一行输入,直到换行符
-
gets不会检查输入的长度,可能导致缓冲区溢出。
使用 fgets
char str[100];
printf("Enter a string: ");
fgets(str, sizeof(str), stdin); // 安全地读取一行输入
-
fgets会读取指定长度的字符,包括换行符。 -
更安全,推荐使用。
2.2 字符串的输出
使用 printf
printf("String: %s\n", str); // 输出字符串
使用 puts
puts(str); // 输出字符串并自动换行
方式2 和 方式3 的区别
方式2 是字符串变量,允许数据被修改
#include <stdio.h>int main()
{char str2[] = "hello";//方式2:直接赋值str2[3] = 'm';printf("%s\n",str2);return 0;
}
运行结果:

方式3 是字符串常量,不允许数据被修改
#include <stdio.h>int main()
{char *pstr = "hello";*pstr = 'a';//pstr表示 hello字符串常量首字母的地址(即h的地址)//*pstr表示 获取到字符 h printf("%s\n",pstr);return 0;
}
运行结果:

注意指针的操作
- 合法操作:保存地址、修改指向、指向字符串常量的地址空间
- 非法操作:对野指针的内存空间操作不行
二、字符串、字符数组和整型数组在存储上的区别
#include <stdio.h>int main()
{int length;int data[] = {1,2,3,4,5};//整型数组char cdata[] = {'h','e','l','l','o'}; //表示是一个字符数组char cdata2[] = "hello"; //表示是一个字符串,编译器会自动加上'\0'char cdata3[] = {'h','e','l','l','o','\0'};//字符串的结束标志 '\0'length = sizeof(data) / sizeof(data[0]);printf("data的长度:%d\n",length);length = sizeof(cdata) / sizeof(cdata[0]);printf("cdata的长度:%d\n",length);length = sizeof(cdata2) / sizeof(cdata2[0]);printf("cdata2的长度:%d\n",length);length = sizeof(cdata3) / sizeof(cdata3[0]);printf("cdata3的长度:%d\n",length);return 0;
}
代码解析
int data[] = {1, 2, 3, 4, 5};
这是一个整型数组,包含5个元素。
sizeof(data)返回整个数组占用的字节数。
sizeof(data[0])返回单个整型元素占用的字节数。数组长度计算公式:
sizeof(data) / sizeof(data[0])。
char cdata[] = {'h', 'e', 'l', 'l', 'o'};
这是一个字符数组,包含5个字符。
它不是字符串,因为没有以
'\0'结尾。
sizeof(cdata)返回整个字符数组占用的字节数。
char cdata2[] = "hello";
这是一个字符串,编译器会自动在末尾添加
'\0'。因此,
cdata2的长度是6(5个字符 + 1个'\0')。
char cdata3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
这是一个显式以
'\0'结尾的字符数组,等价于字符串。因此,
cdata3的长度也是6。
运行结果:

三、sizeof和strlen的区别(重点)
1.sizeof
作用:
sizeof用于计算数据类型或变量占用的内存大小(以字节为单位)。适用范围:
数据类型(如
int、char等)。变量(如数组、结构体等)。
对于字符串:
sizeof返回整个字符数组占用的字节数,包括字符串末尾的'\0'。特点:
在编译时计算,不会实际运行代码。
对于数组,返回整个数组占用的字节数。
对于指针,返回指针本身的大小(通常是4或8字节,取决于系统架构)。
#include <stdio.h>int main()
{char cdata[] = "hello";printf("sizeof:%d\n",sizeof(cdata));// 输出 6(5个字符 + 1个'\0')return 0;
}
2.strlen(定义在 <string.h> 头文件中)
作用:
strlen是一个运行时函数,用于计算字符串的长度(即字符的数量,不包括末尾的'\0')。适用范围:
仅适用于以
'\0'结尾的字符串。返回值:返回一个
size_t类型的值,表示字符串的长度。特点:
在运行时计算,会遍历字符串直到遇到
'\0'。不计算
'\0'的长度。如果字符串没有以
'\0'结尾,strlen的行为是未定义的(可能导致内存越界访问)。
#include <stdio.h>
#include <string.h>int main()
{char cdata[] = "hello";printf("strlen:%d\n",strlen(cdata));// 输出 5(不包括'\0')return 0;
}
3. 主要区别

4.代码示例
5. 常见误区
混淆 sizeof 和 strlen:
-
sizeof计算的是内存大小,strlen计算的是字符串的有效长度。
char str[100] = "hello";
printf("%d\n", sizeof(str)); // 输出 100
printf("%d\n", strlen(str)); // 输出 5
未以 '\0' 结尾的字符数组:
-
如果字符数组没有以
'\0'结尾,strlen的行为是未定义的。
char str[] = {'h', 'e', 'l', 'l', 'o'}; // 不是字符串
printf("%d\n", strlen(str)); // 未定义行为
指针 vs 数组:
-
对于指针,
sizeof返回指针本身的大小,而不是字符串的大小。
char *str = "hello";
printf("%d\n", sizeof(str)); // 输出 8(str是一个指针“地址”,在系统中通常以8个字节表示一个地址)
printf("%d\n", strlen(str)); // 输出 5
总结
-
sizeof:用于计算变量或类型占用的内存大小,适用于所有数据类型,包括字符串。 -
strlen:用于计算字符串的长度,仅适用于以'\0'结尾的字符串。
四、动态开辟字符串
在C语言中,动态开辟字符串是指通过动态内存分配函数(如 malloc、calloc、realloc 等)在堆上分配内存来存储字符串。这种方式可以灵活地根据字符串的实际长度分配内存,避免了静态数组固定大小的限制。
动态开辟字符串常用API
malloc 函数
malloc函数用于动态分配内存。它的原型在 stdlib.h 头文件中定义:
void* malloc(size_t size);
- 参数:
size表示要分配的字节数。 - 返回值:返回一个指向分配的内存块的指针,如果分配失败则返回
NULL。
注意事项:
-
返回
void*指针,需强制类型转换。 -
分配失败时返回
NULL,必须检查:
if (str == NULL) {// 处理错误(如退出程序)
}
realloc 函数
realloc函数是C标准库中的一个重要函数,用于重新分配之前通过malloc、calloc或realloc分配的内存块的大小。
void* realloc(void* ptr, size_t size);
参数说明:
ptr:指向要重新分配内存的内存块的指针。这个内存块必须是之前通过malloc、calloc或realloc分配的。如果ptr是空指针(NULL),则realloc的行为相当于malloc(size),即分配一块新的内存块。size:新内存块的大小,以字节为单位。
例如:通过malloc(10)申请了 10 字节。发现 10 字节不够用,需要扩容 30 字节,总容量需要变为 10 + 30 = 40 字节。size参数应设置为 新的总大小(40 字节)
返回值:
- 如果重新分配成功,
realloc返回指向新内存块的指针。这个指针可能与原来的ptr相同,也可能不同(如果内存块被移动到了新的位置)。 - 如果重新分配失败,
realloc返回NULL,并且原来的内存块保持不变(并没有释放)。此时,原来的指针ptr仍然有效,但不应该继续使用它,因为内存分配失败可能意味着系统内存不足。
检查返回值:
每次调用 realloc 函数,都需要检查内存是否分配失败。
1. 失败原因分析
-
内存不足:系统堆空间不足以满足新的内存请求。
-
内存碎片:剩余内存不连续,无法满足扩展需求。
-
无效指针:传入的指针不是动态分配的或已被释放。
2. 正确处理流程
-
使用临时变量接收返回值
避免直接覆盖原指针,防止丢失原有内存块的引用。
void *temp = realloc(ptr, new_size);
2. 检查返回值是否为 NULL
-
若
temp == NULL,表示扩展失败,原内存块仍然有效。 -
若
temp != NULL,表示扩展成功,更新原指针。
if (temp == NULL) {// 处理失败:原内存块仍可用
} else {ptr = temp; // 更新指针
}
3. 失败后的操作选择
-
保留原内存:继续使用原有内存块,但需调整业务逻辑(如限制数据量)。
-
优雅降级:释放原内存并报错,避免程序继续运行导致未定义行为。
示例:保留原内存并报错
if (temp == NULL) {printf("内存扩展失败,保留原内存!\n");// 继续使用 ptr 指向的原有内存// 可能需要限制后续操作(如停止添加数据)
}
示例:释放原内存并退出
if (temp == NULL) {printf("内存扩展失败,释放资源并退出!\n");free(ptr); // 释放原内存ptr = NULL;return ERROR_CODE;
}
memset 函数
memset 函数用于将内存块的内容设置为指定的值。它的原型在 string.h 头文件中定义:
void* memset(void* s, int c, size_t n);
- 参数:
s是指向要填充的内存块的指针,c是要设置的值(以无符号字符形式传递,但会被转换为unsigned char),n是要设置的字节数。 - 返回值:返回指向内存块的指针。
-
典型用途:
-
初始化动态分配的内存 。
-
清零敏感数据(如密码)。
-
free 函数
free 函数用于释放之前通过 malloc、calloc 或 realloc 分配的内存。它的原型在 stdlib.h 头文件中定义:
void free(void* ptr);
- 参数:
ptr是指向要释放的内存块的指针。
注意:
- 释放内存后,应将指针设置为
NULL,以避免悬空指针(dangling pointer)问题。 - 不能对未分配的内存或已经释放的内存调用
free。
1. 动态开辟字符串的核心步骤
-
分配内存:使用
malloc或calloc分配足够的内存(包括字符串末尾的'\0')。 -
清空内存:使用 memset 清空内存区域
-
复制字符串:将字符串内容复制到分配的内存中。
-
使用字符串:像普通字符串一样使用动态分配的字符串。
-
释放内存:使用完毕后,调用
free释放内存,避免内存泄漏。
2. 常用动态内存分配函数

3. 动态开辟字符串的示例
使用 malloc 动态开辟字符串
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char *p; //定义一个字符指针p = (char *)malloc(sizeof(char) * 6); //让指针变量p指向一个新开辟的内存地址//判断新开辟的内存空间是否开辟成功if(p == NULL){printf("malloc error\n");exit(-1);}//复制字符串内容strcpy(p,"hello");//打印字符串 printf("%s",p);return 0;
}
使用 memset 清空字符串
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{char *p;//使用malloc函数创建一段内存空间p = (char *)malloc(6 * sizeof(char));// 判断是否正确开辟内存if( p == NULL ){printf("malloc error\n");exit(-1);}// 使用memset清空这一段开辟的内存空间的数据memset(p,'\0',6);//对p指向的地址空间写入数据strcpy(p,"hello");printf("%s",p);return 0;
}
使用 free 释放内存空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char *p;p = (char *)malloc(1);if( p == NULL ){printf("malloc error\n");exit(-1);}memset(p,'\0',1);*p = 'a';printf("%c\n",*p);free(p);//释放内存p = NULL;//释放内存后,如p不指向NULL,会变成悬挂指针p = (char *)malloc(6);if( p == NULL ){printf("malloc error\n");exit(-1);}memset(p,'\0',6);strcpy(p,"hello"); printf("%s\n",p);return 0;
}
使用 realloc 扩容内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char *p;p = (char *)malloc(1 * sizeof(char)); //动态开辟内存给p// 检查是否开辟内存成功if( p == NULL ){printf("malloc error\n");exit(-1);}int length = strlen("hello world 666666");int newLength = 1 + length + 1;char *p2 = realloc(p,newLength); //创建一个新的指针变量,指向realloc扩展后的内存空间// 检查是否拓展内存成功if( p2 == NULL ){free(p);p = NULL;exit(-2);} else{p = p2;//更新指针指向,使p指向扩展后的内存地址空间} strcpy(p,"hello world 666666");printf("%s\n",p);return 0;
}
五、字符串常用API
拷贝函数(strcpy 和 strncpy)
-
strcpy
功能
strcpy 用于将一个字符串(包括终止符 '\0')从源地址复制到目标地址。
函数原型
char* strcpy(char *dest, const char *src);
参数
-
dest:目标字符串的起始地址,用于存储复制后的字符串。 -
src:源字符串的起始地址,需要复制的字符串。
返回值
-
返回目标字符串的起始地址
dest。
特点
-
复制整个字符串:
-
strcpy会复制src中的所有字符,直到遇到'\0'(包括'\0')。 -
例如,如果
src是"hello",strcpy会复制'h','e','l','l','o','\0'到dest。
-
-
不检查目标缓冲区大小:
-
strcpy不会检查dest是否有足够的空间来存储src的内容。 -
如果
src的长度大于dest的容量,会导致 缓冲区溢出,这是一种严重的安全漏洞。
-
-
适合已知安全的场景:
-
只有在确保
src的长度不超过dest的容量时,才适合使用strcpy。
-
编程示例
#include <stdio.h>
#include <string.h>int main() {char dest[20];char *src = "Hello, World!";strcpy(dest, src); // 复制 src 到 destprintf("dest: %s\n", dest); // 输出: dest: Hello, World!return 0;
}
编程案例
手写,手撕 strcpy 函数
#include <stdio.h>char* myStrcpy(char *dest, const char *src)
{//判断 dest 和 src 是否 == NULL,如果是return NULL,防止解引用空指针if( dest == NULL || src == NULL ){return NULL;}char *bak = dest; //备份地址头//复制字符while ( *src != '\0' ){*dest = *src;dest++;src++;}*dest = '\0';//手动添加结束标志:'\0'return bak;
}int main()
{char str[128] = {'\0'};char *pstr = "xiaolin handsome";myStrcpy(str, pstr);printf("%s\n",str);return 0;
}
-
strncpy
功能
strncpy 用于从源地址复制最多 n 个字符到目标地址。如果 src 的长度小于 n,则用 '\0' 填充剩余部分。
函数原型
char* strncpy(char *dest, const char *src, size_t n);
参数
-
dest:目标字符串的起始地址,用于存储复制后的字符串。 -
src:源字符串的起始地址,需要复制的字符串。 -
n:最多复制的字符数(包括'\0')。
返回值
-
返回目标字符串的起始地址
dest。
特点
-
限制复制的字符数:
-
strncpy会复制最多n个字符。 -
如果
src的长度小于n,则复制src的所有字符,并用'\0'填充剩余部分。 -
如果
src的长度大于或等于n,则只复制前n个字符,不会自动添加'\0'。
-
-
避免缓冲区溢出:
-
通过限制复制的字符数,
strncpy可以避免目标缓冲区溢出的问题。 -
但需要注意,如果
n大于dest的容量,仍然可能导致溢出。
-
-
适合需要控制复制长度的场景:
-
当需要限制复制的字符数时,
strncpy是一个更安全的选择。
-
编程示例
#include <stdio.h>
#include <string.h>int main() {char dest[20];char *src = "Hello, World!";strncpy(dest, src, 5); // 复制 src 的前 5 个字符到 destdest[5] = '\0'; // 手动添加终止符printf("dest: %s\n", dest); // 输出: dest: Helloreturn 0;
}
编程案例
手写,手撕 strncpy函数
#include <stdio.h>char* myStrncpy(char *dest, const char *src, int num)
{//判断 dest 和 src 是否 == NULL,如果是return NULL,防止解引用空指针if( dest == NULL || src == NULL ){return NULL;}char *bak = dest;int numChar = 1; //numChar是一个计数器,用于记录已经复制的字符数量,从1开始记录。// 最多复制num个字符while ( *src != '\0' && numChar <= num ){*dest = *src;dest++;src++;numChar++;}//这个判断的作用是检查是否已经复制了足够的字符(即是否已经复制了num个字符)。if( numChar <= num ){// src的长度小于num,则在dest中填充'\0',直到达到num个字符while ( numChar <= num ) {*dest = '\0';dest++;numChar++;}return bak;}*dest = '\0';//手动添加字符串结束标志:'\0'return bak;
}
int main()
{char str[128] = {'\0'};char *pstr = "xiaolin handsome";myStrncpy(str, pstr, 7);printf("%s\n",str);return 0;
}
strcpy 和 strncpy 的区别

断言函数(assert)
在 C 语言中,断言函数 assert 是一个调试工具,用于在代码中插入“检查点”,验证程序运行时的某些条件是否满足。如果条件不成立(结果为假),assert 会立即终止程序,并输出详细的错误信息(如文件名、行号、失败的条件),帮助开发者快速定位问题。
它像一个“守门员”,验证某个条件是否必须成立。如果条件不成立(为假),程序会立即终止,并输出错误信息(文件名、行号、失败的条件)。
核心作用
-
调试辅助:用于捕捉程序中隐藏的逻辑错误(如变量值异常、假设不成立)。
-
防御性编程:确保代码的前提条件或中间状态符合预期,防止错误扩散。
基本用法
-
头文件:需包含
<assert.h>。 -
语法:
assert(条件表达式);-
若表达式为真(非零):程序继续执行。
-
若表达式为假(0):终止程序,输出错误信息。
-
使用场景
1. 验证函数参数:
void process(int* ptr) {assert(ptr != NULL); // 确保指针非空// 后续操作...
}
2. 检查中间状态:
int result = complex_calculation();
assert(result >= 0 && result <= 100); // 确保结果在合理范围内
3.测试假设条件:
// 假设某个循环最多执行 100 次
for (int i = 0; i < n; i++) {assert(i < 100); // 防止意外死循环
}
关键注意事项
仅用于调试:
断言默认只在调试阶段生效。若在发布版本中禁用断言,需在包含 <assert.h> 前定义 NDEBUG 宏:
#define NDEBUG // 禁用所有断言
#include <assert.h>
编程示例
以下是一个手写字符串拷贝函数
断言的值为真,程序正常执行
#include <stdio.h>
#include <assert.h>char* myStrncpy(char *dest, const char *src, int num)
{assert( dest != NULL && src != NULL ); //断言char *bak = dest;int numChar = 1; //numChar是一个计数器,用于记录已经复制的字符数量,从1开始记录。// 最多复制num个字符while ( *src != '\0' && numChar <= num ){*dest = *src;dest++;src++;numChar++;}//这个判断的作用是检查是否已经复制了足够的字符(即是否已经复制了num个字符)。if( numChar <= num ){// src的长度小于num,则在dest中填充'\0',直到达到num个字符while ( numChar <= num ) {*dest = '\0';dest++;numChar++;}return bak;}*dest = '\0';//手动添加字符串结束标志:'\0'return bak;
}
int main()
{char str[128] = {'\0'};char *pstr = "xiaolin handsome";myStrncpy(str, pstr, 7);printf("%s\n",str);return 0;
}

断言的值为假,终止程序,输出错误信息
#include <stdio.h>
#include <assert.h>char* myStrncpy(char *dest, const char *src, int num)
{assert( dest != NULL && src != NULL ); //断言char *bak = dest;int numChar = 1; //numChar是一个计数器,用于记录已经复制的字符数量,从1开始记录。// 最多复制num个字符while ( *src != '\0' && numChar <= num ){*dest = *src;dest++;src++;numChar++;}//这个判断的作用是检查是否已经复制了足够的字符(即是否已经复制了num个字符)。if( numChar <= num ){// src的长度小于num,则在dest中填充'\0',直到达到num个字符while ( numChar <= num ) {*dest = '\0';dest++;numChar++;}return bak;}*dest = '\0';//手动添加字符串结束标志:'\0'return bak;
}
int main()
{char str[128] = {'\0'};char *pstr = "xiaolin handsome";char *p2 = NULL;myStrncpy(p2, pstr, 7);printf("%s\n",str);return 0;
}

在这里,故意将p2设置为NULL,并且传递给 myStrncpy 函数。因 myStrncpy 函数的 dest 参数不允许为 NULL,所以断言 assert(dest != NULL && src != NULL) 的作用是:当调用者错误地传递 NULL 给 dest 或 src 时,立即终止程序并明确提示错误。这是典型的“防御性编程”实践,用于在调试阶段快速暴露代码逻辑错误。
拼接函数(strcat 和 strncat)
strcat 是 字符串拼接函数,用于将一个字符串(源字符串)追加到另一个字符串(目标字符串)的末尾。
strcat
1. 函数原型
#include <string.h>
char *strcat(char *dest, const char *src);
-
功能:将
src字符串(包括结尾的'\0')追加到dest字符串的末尾。 -
返回值:返回目标字符串的指针
dest。
2. 关键要求
-
dest必须指向一个足够大的缓冲区,能够容纳dest和src的总长度(包括末尾的'\0')。 -
dest和src必须是以'\0'结尾的字符串。
编程示例
#include <stdio.h>
#include <string.h>int main() {char dest[20] = "Hello, ";const char *src = "World!";strcat(dest, src); // 将 src 追加到 dest 末尾printf("%s\n", dest); // 输出:Hello, World!return 0;
}
编程案例
手写 strcat 函数
#include <stdio.h>
#include <assert.h>char* myStrcat(char *dest, const char *src)
{//使用 assert 检查 dest 和 src 是否为非空指针;如为空指针,退出程序并提示开发者assert( dest != NULL && src != NULL );char *bak = dest;//通过 while(*dest != '\0') 找到目标字符串 dest 的末尾(即 \0 的位置)。while( *dest != '\0' ){dest++;//地址偏移}//将 src 的字符逐个复制到 dest 末尾,直到 src 遇到 \0。while( *src != '\0' ){*dest = *src;dest++;src++;}//手动在拼接后的字符串末尾添加 \0。*dest = '\0';return bak;
}int main()
{char str[128] = "xiaolin";char *pstr = " handsome";myStrcat(str, pstr);printf("%s\n",str);return 0;
}

strncat
strncat用于将源字符串的指定数量字符追加到目标字符串末尾,避免缓冲区溢出。
1. 函数原型
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
-
功能:从
src字符串中复制最多n个字符到dest末尾(自动添加\0)。 -
返回值:返回目标字符串的指针
dest。
2. 核心特性
2.1. 安全机制
-
限制最大复制长度:通过
n参数控制追加字符数量,防止目标缓冲区溢出。 -
自动添加终止符:无论是否复制完
n个字符,strncat都会在dest末尾添加\0。
2.2. 参数说明

编程示例
#include <stdio.h>
#include <string.h>int main() {char dest[20] = "Hello";const char *src = ", World!";// 安全追加最多 7 个字符strncat(dest, src, 7); printf("%s\n", dest); // 输出:Hello, Worldreturn 0;
}
编程案例
手写 strncat 函数
#include <stdio.h>
#include <string.h>
#include <assert.h>char* myStrncat(char *dest, const char *src, int num_limit, int array_total)
{assert( dest != NULL && src != NULL );char *bak = dest;// 计算剩余可用空间大小:数组总大小 - 数组有效字节 - 1('\0')== 剩余可用空间int remaining = array_total - strlen(dest) - 1;// 移动到 dest 的末尾while ( *dest != '\0'){dest++;}//如果剩余字节大小 大于或者等于 需要复制的最大字节数if( remaining >= num_limit ){//复制最大的字节数while( *src != '\0' && num_limit > 0 ){*dest = *src;dest++;src++;num_limit--;}*dest = '\0';}//如果剩余字节大小 小于 需要复制的最大字节数else if( remaining < num_limit ){//复制剩余字节大小while( *src != '\0' && remaining > 0 ){*dest = *src;dest++;src++;remaining--;}*dest = '\0';}return bak;
}int main()
{char str[14] = "hello"; // 6个字节char *pstr = " xiaoLin"; // 9个字节(8个有效字节 + 1个'\0')myStrncat(str, pstr, 8, sizeof(str));printf("%s\n",str);return 0;
}

字符串比较函数(strcmp 和 strncmp)
strcmp
strcmp 是 C 语言中用于比较两个字符串(以 \0 结尾的字符数组)的标准库函数。它定义在 <string.h> 头文件中。
函数原型:
int strcmp(const char *str1, const char *str2);
参数:
-
str1:要比较的第一个字符串(以\0结尾)。 -
str2:要比较的第二个字符串(以\0结尾)。
返回值:
-
0:如果两个字符串相等(内容完全相同)。
-
负整数:如果
str1小于str2(按字典序比较)。 -
正整数:如果
str1大于str2(按字典序比较)。
工作原理:
strcmp 会逐个字符比较两个字符串,直到遇到不同的字符或到达字符串的结束符 \0。比较是基于字符的 ASCII 值。
编程示例
#include <stdio.h>
#include <string.h>int main() {char str1[] = "apple";char str2[] = "banana";char str3[] = "apple";// 比较 str1 和 str2int result1 = strcmp(str1, str2);if (result1 == 0) {printf("str1 和 str2 相等\n");} else if (result1 < 0) {printf("str1 小于 str2\n"); // 输出:str1 小于 str2} else {printf("str1 大于 str2\n");}// 比较 str1 和 str3int result2 = strcmp(str1, str3);if (result2 == 0) {printf("str1 和 str3 相等\n"); // 输出:str1 和 str3 相等} else {printf("str1 和 str3 不相等\n");}return 0;
}
运行结果:

编程案例
手写 strcmp 函数
#include <stdio.h>
#include <assert.h>int myStrcmp(const char *str1, const char *str2)
{assert(str1 != NULL && str2 != NULL);// 逐个字符比较while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2) {str1++;str2++;}// 返回字符差值return (*str1 - *str2);
}int main()
{char *p = "xiaoLin hello";char *p2 = "handsome";int ret = myStrcmp(p, p2);printf("myStrcmp return = %d\n", ret);return 0;
}
strncmp
strncmp 是 C 语言标准库中的一个字符串比较函数,用于比较两个字符串的前 n 个字符。它的声明在 <string.h> 头文件中。
函数原型
int strncmp(const char *str1, const char *str2, size_t n);
参数
-
str1:第一个要比较的字符串。 -
str2:第二个要比较的字符串。 -
n:要比较的最大字符数。
返回值
-
如果
str1和str2的前n个字符相等,返回0。 -
如果
str1的前n个字符小于str2的前n个字符,返回一个负整数。 -
如果
str1的前n个字符大于str2的前n个字符,返回一个正整数。
注意事项
-
strncmp只比较前n个字符,即使字符串长度超过n,也不会比较后面的字符。 -
如果
n大于两个字符串的长度,strncmp会在遇到字符串的结束符\0时停止比较。
与 strcmp 的区别
-
strcmp比较整个字符串,直到遇到\0结束符。 -
strncmp只比较前n个字符,即使字符串长度超过n。
适用场景
-
当你只需要比较字符串的前几个字符时,可以使用
strncmp。 -
例如,比较文件扩展名、协议头等场景。
编程示例
#include <stdio.h>
#include <string.h>int main() {char str1[] = "Hello, World!";char str2[] = "Hello, C!";int result;// 比较前 5 个字符result = strncmp(str1, str2, 5);if (result == 0) {printf("前 5 个字符相等\n");} else if (result < 0) {printf("str1 的前 5 个字符小于 str2 的前 5 个字符\n");} else {printf("str1 的前 5 个字符大于 str2 的前 5 个字符\n");}return 0;
}
查找子字符(strchr)
strchr 是 C 语言标准库中的一个字符串查找函数,用于在字符串中查找指定字符的第一次出现位置。它的声明在 <string.h> 头文件中。
函数原型
char *strchr(const char *str, int c);
参数
-
str:要搜索的字符串。 -
c:要查找的字符(以int形式传递,但实际会被转换为char)。
返回值
-
如果找到字符
c,返回指向该字符的指针。 -
如果未找到字符
c,返回NULL。
注意事项
-
查找字符的范围:
-
strchr会从字符串的开头开始查找,直到遇到字符串的结束符\0。 -
如果字符
c是\0,strchr也会返回指向\0的指针。
-
-
区分大小写:
-
strchr是区分大小写的。例如,查找'w'和'W'是不同的。
-
-
返回值:
-
返回的指针指向的是字符串中第一次出现字符
c的位置。 -
如果未找到字符,返回
NULL。
-
编程示例
#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, World!";char *result;// 查找字符 'W'result = strchr(str, 'W');if (result != NULL) {printf("找到字符 'W',位置在:%ld\n", result - str);printf("从该字符开始的子字符串为:%s\n", result);} else {printf("未找到字符 'W'\n");}return 0;
}
运行结果:
找到字符 'W',位置在:7
从该字符开始的子字符串为:World!
查找子串(strstr)
strstr 是 C 语言标准库中的一个字符串查找函数,用于在一个字符串中查找另一个子串的第一次出现位置。它的声明在 <string.h> 头文件中。
函数原型
char *strstr(const char *haystack, const char *needle);
参数
-
haystack:被搜索的主字符串。 -
needle:要查找的子串。
返回值
-
如果找到子串
needle,返回指向主字符串中第一次出现子串的指针。 -
如果未找到子串
needle,返回NULL。
注意事项
-
查找范围:
-
strstr会从主字符串的开头开始查找,直到遇到主字符串的结束符\0。 -
如果子串
needle是空字符串(""),strstr会返回指向主字符串开头的指针。
-
-
区分大小写:
-
strstr是区分大小写的。例如,查找"world"和"World"是不同的。
-
-
返回值:
-
返回的指针指向的是主字符串中第一次出现子串的位置。
-
如果未找到子串,返回
NULL。
-
与 strchr 的区别
-
strchr查找单个字符的第一次出现位置。 -
strstr查找子串的第一次出现位置。
编程示例
#include <stdio.h>
#include <string.h>int main() {char haystack[] = "Hello, World! This is a test string.";char needle[] = "World";char *result;// 查找子串 "World"result = strstr(haystack, needle);if (result != NULL) {printf("找到子串 \"%s\",位置在:%ld\n", needle, result - haystack);printf("从该子串开始的字符串为:%s\n", result);} else {printf("未找到子串 \"%s\"\n", needle);}return 0;
}
运行结果:

字符串分割(strtok)
strtok 是 C 语言标准库中的一个字符串分割函数,用于将一个字符串按照指定的分隔符拆分成多个子串(标记)。它的声明在 <string.h> 头文件中。
函数原型
char *strtok(char *str, const char *delim);
参数
-
str:要分割的字符串(不能是常量字符串)。在第一次调用时传入待分割的字符串,后续调用传入NULL。 -
delim:分隔符字符串。strtok会将字符串中任何属于delim的字符作为分隔符。
返回值
-
返回指向当前分割出的子串的指针。
-
如果没有更多的子串可分割,返回
NULL。
注意事项
-
修改原字符串:
strtok函数会修改原始字符串,它通过将分隔符替换为字符串结束符'\0'来实现分割。然而,你定义的字符串p是一个字符串常量,存储在只读内存区域,对其进行修改会导致未定义行为。因此,如果需要保留原字符串,应该先复制一份。
-
线程安全问题:
-
strtok是不可重入的,因为它使用静态缓冲区来保存状态。如果在多线程环境中使用,可能会导致问题。 -
可以使用
strtok_r(POSIX 标准)作为线程安全的替代品。
-
-
连续分隔符:
-
如果字符串中有连续的分隔符(例如
",,"),strtok会忽略它们,不会返回空字符串。
-
-
空字符串:
-
如果字符串中没有任何分隔符,
strtok会返回整个字符串作为唯一的子串。
-
编程示例
#include <stdio.h>
#include <string.h>int main()
{// 使用字符数组存储字符串,这样可以被修改char p[] = "hello my name is xiaoAi";char *p2 = " ";char *result = NULL;// 第一次调用strtok函数result = strtok(p, p2);while (result != NULL){//输出第一次分割的字符串,后续会继续输出分割的字符串,直至被全部分割完成printf("分割出的子字符串:%s\n", result);// 后续调用strtok函数,传入NULLresult = strtok(NULL, p2);}return 0;
}
代码解释
- 使用字符数组存储字符串:
char p[] = "hello my name is xiaoAi";,这样p是一个字符数组,存储在可写内存区域,strtok函数可以对其进行修改。 strtok函数的使用:- 第一次调用
strtok(p, p2)时,传入要分割的字符串p和分隔符字符串p2,函数会返回第一个分割出的子字符串的指针。 - 后续调用
strtok(NULL, p2)时,传入NULL表示继续对之前的字符串进行分割,函数会返回下一个分割出的子字符串的指针。
- 第一次调用
- 循环打印分割结果:使用
while循环,只要result不为NULL,就打印分割出的子字符串,并继续调用strtok函数进行分割。
运行结果:


