
往期《C语言基础系列》回顾:链接: C语言基础之【C语言概述】 C语言基础之【数据类型】(上) C语言基础之【数据类型】(下) C语言基础之【运算符与表达式】 C语言基础之【程序流程结构】 C语言基础之【数组和字符串】(上) C语言基础之【数组和字符串】(下) C语言基础之【函数】 C语言基础之【指针】(上) C语言基础之【指针】(中)
在C语言中,字符串是以字符数组的形式存储的,而字符指针(char *)可以用于操作字符串。
字符指针定义的语法:char *指针变量名;
字符指针初始化:
char *str1 = "Hello"; // 指向字符串字面量
char str2[] = "World"; // 字符数组
char *str3 = str2; // 指向字符数组字符指针的使用:
访问字符串
char *str = "Hello";
//通过字符指针可以访问字符串中的字符。
printf("%c\n", *str); // 输出:H
printf("%c\n", *(str + 1)); // 输出:e遍历字符串
char* str = "Hello";
//通过字符指针可以遍历字符串中的所有字符。
for (int i = 0; str[i] != '\0'; i++)
{
printf("%c", str[i]); // 输出:Hello
}修改字符串
char str[] = "Hello";
char *p = str;
p[0] = 'h'; // 修改字符串内容
printf("%s\n", str); // 输出:hello字符数组,可以通过指针修改字符串的内容
字符串字面量,则不能修改字符串的内容(行为未定义)
代码示例:字符数组和字符指针的区别
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char str1[] = "hello"; // {'h', 'e', 'l', 'l', 'o', '\0'}
char m[] = "hello";
char* str2 = "hello"; // "hello" 是一个字符串常量, 不能修改。
char* n = "hello";
//char* str3 = {'h','e','l','l','o','\0'} error:初始化语法错误
//str1[0] = 'R';
//str2[0] = 'R'; error
printf("str1 = %p\n", str1);
printf("m = %p\n", m);
printf("str2 = %p\n", str2);
printf("n = %p\n", n);
system("pause");
return EXIT_SUCCESS;
}输出:
str1 = 000000B9A54FF534
m = 000000B9A54FF554
str2 = 00007FF788E7ACF0
n = 00007FF788E7ACF0分析:
1.字符数组 str1 和 m
char str1[] = "hello";
char m[] = "hello";存储方式:
str1 和 m 是字符数组,存储在栈上。每个数组都有独立的内存空间"hello",包括结尾的 \0 字符。可修改性:
数组的内容可以修改。
str1[0] = 'R'; // 合法,str1 变为 "Rello" 因为str1是字符数组,内容可以修改。地址:
str1 和 m 的地址不同,因为它们是独立的数组。2.字符指针 str2 和 n
char* str2 = "hello";
char* n = "hello";存储方式:
str2 和 n 是指向字符串常量的指针,字符串常量 "hello" 存储在只读内存区域(通常是 .rodata 段)编译器可能会将相同的字符串常量优化为同一内存地址可修改性:
字符串常量的内容不能修改。
str2[0] = 'R'; // 非法,会导致未定义行为(通常是程序崩溃)
//因为str2指向的是字符串常量,内容不能修改。地址:
str2 和 n 的地址可能相同,因为它们指向相同的字符串常量。// char* str3 = {'h','e','l','l','o','\0'} error:初始化语法错误注意:
{} 包含的字符列表来初始化。字符指针与字符数组的区别:
特性 | 字符指针 | 字符数组 |
|---|---|---|
定义 | char *str | char str[] |
内存分配 | 指向字符串字面量(只读)或 动态分配的内存 | 静态分配内存,大小固定 |
可修改性 | 如果指向字符数组,可以修改如果指向字符串字面量,不能修改 | 可以修改 |
大小 | sizeof(str) 返回指针的大小(通常为4或8字节) | sizeof(str) 返回整个数组的大小 |
灵活性 | 可以指向不同的字符串 | 不能指向其他字符串 |
字符指针的常见应用:
示例代码:字符指针的使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 示例1:字符指针指向字符串字面量
char* str1 = "Hello";
printf("str1: %s\n", str1); // 输出:Hello
// 示例2:字符指针指向字符数组
char str2[] = "Hello";
char* str3 = str2;
str3[0] = 'h'; // 修改字符串内容
printf("str2: %s\n", str2); // 输出:hello
// 示例3:动态分配字符串
char* str4 = (char*)malloc(20 * sizeof(char));
strcpy(str4, "Hello, World!");
printf("str4: %s\n", str4); // 输出:Hello, World!
free(str4); // 释放内存
// 示例4:字符串数组
char* strArr[] = { "Apple", "Banana", "Cherry" };
for (int i = 0; i < 3; i++)
{
printf("strArr[%d]: %s\n", i, strArr[i]); // 输出每个字符串
}
return 0;
}输出:
str1: Hello
str2: hello
str4: Hello, World!
strArr[0]: Apple
strArr[1]: Banana
strArr[2]: Cherry在C语言中,字符指针(char *)作为函数参数时,通常用于传递字符串或字符数组的地址。
字符指针作为函数参数的注意事项:
1.字符串的长度
字符指针作为函数参数时,字符串的长度信息会丢失
\0 来判断字符串的结束。#include <stdio.h>
//需要额外传递字符串的长度
void printString1(char* str, int n)
{
for (int i = 0; i < n; i++)
{
printf("%c", str[i]); // 输出字符串的前n个字符
}
printf("\n");
}
//依赖 \0 来判断字符串的结束
void printString2(char* str)
{
for (int i = 0; str[i] != '\0'; i++)
{
printf("%c", str[i]); // 输出字符串
}
printf("\n");
}
int main()
{
char str[] = "Hello, World!";
int n = sizeof(str) / sizeof(str[0]);
printString1(str, n); // 输出:Hello, World!
printString2(str); // 输出:Hello, World!
return 0;
}2.字符串的可修改性
如果字符指针指向的是字符串字面量
char *str = "Hello";,则不能通过指针修改字符串的内容(行为未定义)如果字符指针指向的是字符数组
char str[] = "Hello";,则可以通过指针修改字符串的内容#include <stdio.h>
void modifyString(char* str)
{
str[0] = 'h';
}
int main()
{
char* str1 = "Hello";
modifyString(str1); // 未定义行为
char str2[] = "Hello";
modifyString(str2); // 修改字符串内容
printf("%s\n", str2); // 输出:hello
return 0;
}示例代码:字符指针作为函数参数的使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 打印字符串
void printString(char *str)
{
printf("%s\n", str);
}
// 将字符串转换为大写
void toUpperCase(char *str)
{
for (int i = 0; str[i] != '\0'; i++)
{
if (str[i] >= 'a' && str[i] <= 'z')
{
str[i] = str[i] - 32;
}
}
}
// 动态分配字符串
char *createString(const char *src)
{
char *str = (char *)malloc((strlen(src) + 1) * sizeof(char));
strcpy(str, src);
return str;
}
// 打印字符串数组
void printStrings(char *strArr[], int n)
{
for (int i = 0; i < n; i++)
{
printf("%s\n", strArr[i]);
}
}
int main()
{
// 示例1:打印字符串
char str1[] = "Hello, World!";
printString(str1);
// 示例2:修改字符串
toUpperCase(str1);
printString(str1);
// 示例3:动态分配字符串
char *str2 = createString("动态分配字符串");
printString(str2);
free(str2);
// 示例4:字符串数组
char *strArr[] = {"Apple", "Banana", "Cherry"};
int n = sizeof(strArr) / sizeof(strArr[0]);
printStrings(strArr, n);
return 0;
}输出:
Hello, World!
HELLO, WORLD!
Dynamic String
Apple
Banana
Cherry注意:
当 字符串数组 做函数参数时,我们通常在函数定义中,封装2个参数。
一个表示字符串数组首地址,一个表示数组元素个数const 修饰指针的常见用法:
使用 const 修饰指针参数,防止函数内部修改数据。
void printArray(const int *arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}使用 const 修饰指针,定义常量字符串。
const char *str = "Hello, World!";
// str[0] = 'h'; // 错误:不能修改常量字符串在C语言中,main 函数可以接受两个参数:argc 和 argv
理解 main 函数的参数及其用法对于编写支持命令行输入的程序非常重要。
main 函数原型的语法:
int main(int argc, char *argv[]);argc(参数计数):是一个整形,用于表示命令行参数的数量(包括程序名本身)
argc 的最小值为 1,因为 argv[0] 总是程序名。argv(参数向量):是一个指针数组,用于存储命令行参数的字符串
指针数组argv的每个元素是一个 char * 类型的指针,指向一个命令行参数字符串。
argv[0] :是程序名,argv[1] 到 argv[argc-1] 是实际的命令行参数。argv[argc]: 是一个空指针(NULL),表示参数列表的结束。main 函数参数的使用:
1.打印所有命令行参数
通过遍历 argv 数组,可以打印所有命令行参数。
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("程序名: %s\n", argv[0]); // 打印程序名
for (int i = 1; i < argc; i++)
{
printf("参数 %d: %s\n", i, argv[i]); // 打印每个参数
}
return 0;
}2.处理命令行参数
通过 argc 和 argv 可以处理命令行参数。
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("用法: %s <参数>\n", argv[0]); // 提示用法
return 1;
}
printf("第一个参数: %s\n", argv[1]); // 打印第一个参数
return 0;
}示例代码:运行带参数的 main 函数
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("程序名: %s\n", argv[0]); // 打印程序名
printf("参数总数: %d\n", argc - 1); // 打印参数总数
// 打印所有命令行参数
for (int i = 1; i < argc; i++)
{
printf("参数 %d: %s\n", i, argv[i]);
}
return 0;
}借助VS编辑工具运行 main 函数C程序 —>右击该程序所在的项目名 —> 属性—>调试 —> 命令参数 —> 添加命令参数 —> 确定


借助记事本、gcc编译工具运行

切记:粘贴完代码一定要记得按Ctrl + S进行保存



在处理字符串时,经常需要统计某个特定子字符串在主字符串中出现的次数。
p,其内容为 "11abcd111122abcd333abcd3322abcd3333322qqq""abcd" 在主字符串中出现的次数//while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
const char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;
int len = strlen("abcd");
while (p != NULL)
{
n++;
p = p + len;
p = strstr(p, "abcd");
}
printf("n = %d\n", n);
return 0;
}输出: n = 4
//do-while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
int n = 0;
int len = strlen("abcd");
do
{
p = strstr(p, "abcd");
if (p != NULL)
{
n++; //累计个数
p = p + len; //重新设置查找的起点
}
else //如果没有匹配的字符串,跳出循环
{
break;
}
} while (*p != 0); //如果没有到结尾
printf("n = %d\n", n);
return 0;
}输出: n = 4
在字符串处理过程中,常常需要统计去除首尾空格后字符串中非空字符的数量。
fun 的函数,该函数的功能是统计一个字符串去除首尾空格后非空字符的数量。#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int fun(char *p, int *n)
{
// 检查指针是否为空
if (p == NULL || n == NULL)
{
return -1;
}
int begin = 0;
int end = strlen(p) - 1;
// 从左边开始,跳过空格
while (p[begin] == ' ' && p[begin] != '\0')
{
begin++;
}
// 从右边开始,跳过空格
while (p[end] == ' ' && end >= begin)
{
end--;
}
// 如果字符串全是空格,返回错误
if (end < begin)
{
return -2;
}
// 计算非空字符的个数
*n = end - begin + 1;
return 0;
}
int main()
{
char *p = " abcddsgadsgefg ";
int ret = 0;
int n = 0;
// 调用函数处理字符串
ret = fun(p, &n);
if (ret != 0)
{
printf("函数执行失败,错误码:%d\n", ret);
return ret;
}
// 输出结果
printf("非空字符串元素个数:%d\n", n);
return 0;
}输出: 非空字符串元素个数:14
编写一个C语言程序,判断一个字符串是否是回文字符串。
#include <stdio.h>
#include <stdlib.h>
// 判断回文字符串 abcdpba
int str_abcbb(char* str)
{
char* start = str; // 记录首元素地址
char* end = str + strlen(str) - 1; // 记录最后一个元素地址。
while (start < end)
{
if (*start != *end)
{
return 0; // 0 表示 非回文
}
start++;
end--;
}
return 1; // 1 表示 回文
}
int main(void)
{
char s2[] = "abccba";
int ret = str_abcbb(s2);
if (ret == 0)
{
printf("不是回文\n");
}
else if (ret == 1)
{
printf("是回文\n");
}
system("pause");
return EXIT_SUCCESS;
}输出: 是回文
在字符串处理的诸多场景中,字符串反转是一个常见的需求。
inverse 的函数,该函数接收一个字符串作为参数,并将该字符串的字符顺序进行反转。#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int inverse(char* str)
{
// 检查指针是否为空
if (str == NULL)
{
return -1;
}
int begin = 0;
int end = strlen(str) - 1;
char tmp;
// 交换字符,直到begin和end相遇
while (begin < end)
{
// 交换元素
tmp = str[begin];
str[begin] = str[end];
str[end] = tmp;
begin++; // 往右移动位置
end--; // 往左移动位置
}
return 0;
}
int main()
{
//char *str = "abcdef";
// 使用字符数组,而不是字符指针,因为字符指针指向的是常量区,内容不允许修改
char str[] = "abcdef";
// 调用反转函数
int ret = inverse(str);
if (ret != 0)
{
printf("函数执行失败,错误码:%d\n", ret);
return ret;
}
// 输出结果
printf("str = %s\n", str);
return 0;
}输出: str = fedcba
函数的介绍:strchr():用于在一个字符串中查找指定字符首次出现的位置。
strchr() 函数会从字符串 str 的开头开始,逐个字符进行比较,查找字符 c 首次出现的位置。
NULL
函数的原型:
char *strchr(const char *str, int c);str:是指向要进行查找的字符串的指针。c:要查找的字符。 int,但实际上是一个字符的 ASCII 值c 会被转换为 char 类型
函数的返回值:
str 中找到字符 c:则返回指向该字符首次出现位置的指针str 中未找到字符 c:则返回 NULL函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello, World!";
char target = 'o';
char* result = strchr(str, target);
if (result != NULL)
{
printf("字符 '%c' 首次出现在位置: %ld\n", target, result - str);
printf("从该位置开始的子字符串: %s\n", result);
}
else
{
printf("未找到字符 '%c'\n", target);
}
return 0;
}输出:
字符 ‘o’ 首次出现在位置: 4 从该位置开始的子字符串: o, World!
函数的注意事项:
字符查找的范围:strchr 函数会查找字符首次出现的位置,并且会查找包括字符串结束符 '\0' 之前的所有字符。
'\0',函数会返回指向字符串结束符的指针。#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello";
char *result = strchr(str, '\0');
if (result != NULL)
{
printf("找到字符串结束符,位置: %ld\n", result - str);
}
return 0;
}输出:
找到字符串结束符,位置: 5
函数的介绍:strrchr():用于查找字符串中某个字符最后一次出现的位置。(即:用于在字符串中从后往前查找指定字符的第一次出现位置)
函数的原型:
char *strrchr(const char *str, int c);str:是指向要被搜索的字符串的指针。c:是要查找的字符。 int,但实际上是一个字符的 ASCII 值c 会被转换为 char 类型
函数的返回值:
str 中找到了字符 c 的最后一次出现位置,则返回一个指向该位置的指针str 中未找到字符 c,则返回 NULL函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
const char *str = "Hello, World!";
char target = 'o';
char *result = strrchr(str, target);//error:const char *类型的值不能用于初始化 char *类型的实体
if (result != NULL)
{
printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);
printf("从该位置开始的子字符串是: %s\n", result);
} else
{
printf("未找到字符 '%c'\n", target);
}
return 0;
}分析错误原因:
strrchr 的参数str :是 const char* 类型,表示它指向的字符串是常量不能修改。strrchr 的返回值:是 char* 类型,表示它返回的指针可以用于修改字符串。注意:将 char* 赋值给 const char* 是允许的,但反过来不行,因为这会破坏 const 的语义
问题解决方法:
将 str 的类型改为 char*:(如果 str 指向的字符串不需要保护,即:可以修改)
char str[] = "Hello, World!";str 就是一个可修改的字符数组,strrchr 的返回值可以直接赋值给 result。使用类型转换:(如果你需要 str 是 const char* 类型)
const char* str = "Hello, World!";
const char* result = strrchr(str, target);result 也是 const char* 类型,与 str 的类型一致。修改后的代码:
方法 1:将 str 改为 char*
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello, World!"; // 改为 char[]
char target = 'o';
char* result = strrchr(str, target);
if (result != NULL)
{
printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);
printf("从该位置开始的子字符串是: %s\n", result);
}
else
{
printf("未找到字符 '%c'\n", target);
}
return 0;
}方法 2:使用类型转换
#include <stdio.h>
#include <string.h>
int main()
{
const char* str = "Hello, World!"; // 保持 const char*
char target = 'o';
const char* result = strrchr(str, target); // 将返回值转换为 const char*
if (result != NULL)
{
printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);
printf("从该位置开始的子字符串是: %s\n", result);
}
else
{
printf("未找到字符 '%c'\n", target);
}
return 0;
}strchr() 和 strrchr() 的区别:
函数 | 搜索方向 | 返回的字符位置 |
|---|---|---|
strchr | 从字符串开头向后搜索 | 字符第一次出现的位置 |
strrchr | 从字符串末尾向前搜索 | 字符最后一次出现的位置 |
函数的介绍:strstr():用于在一个字符串中查找另一个子字符串首次出现的位置。
strstr() 函数会从主字符串 haystack 的起始位置开始,逐个字符地进行比对,尝试找到与子字符串 needle 完全匹配的部分。
haystack 字符串都没有找到匹配的 needle 子字符串,就返回 NULL
函数的原型:
char *strstr(const char *haystack, const char *needle);haystack:是指向要进行搜索的主字符串的指针。 needle:是指向要查找的子字符串的指针。
函数的返回值:
haystack 字符串中找到了 needle 子字符串:函数返回指向该子字符串首次出现位置的指针。haystack 字符串中未找到 needle 子字符串:函数返回 NULL函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char haystack[] = "Hello, World! This is a test.";
char needle[] = "World";
char* 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;
}输出:
子字符串 ‘World’ 首次出现在位置: 7 从该位置开始的子字符串: World! This is a test.
函数的注意事项:
空字符串的处理:如果 needle 是一个空字符串(即 ""),strstr() 函数会直接返回 haystack 本身。
空字符串可以被认为在任何字符串的开头都存在#include <stdio.h>
#include <string.h>
int main()
{
char haystack[] = "Hello";
char needle[] = "";
char* result = strstr(haystack, needle);
if (result != NULL)
{
printf("找到空字符串,返回主字符串: %s\n", result);
}
return 0;
}输出:
找到空字符串,返回主字符串: Hello
strchr() 和 strstr() 的区别:
特性 | strchr() | strstr() |
|---|---|---|
搜索对象 | 单个字符 | 子字符串 |
返回值 | 指向字符的指针 | 指向子字符串的指针 |
区分大小写 | 是 | 是 |
适用场景 | 查找单个字符 | 查找子字符串 |
函数的介绍:strtok():用于将一个字符串按照指定的分隔符分割成多个子字符串。
strtok() 函数会在字符串 str 中查找由分隔符 delim 分隔的子字符串。
它会将分隔符替换为字符串结束符 '\0',并返回指向分割出的子字符串的指针。
函数会记录当前分割的位置,以便后续调用时继续从该位置进行分割。
strstrtok() 会找到第一个标记,并在标记末尾添加 \0,返回指向该标记的指针。NULL 作为 str 参数。strtok() 会从上一次保存的位置继续查找下一个标记。NULL函数的原型:
char *strtok(char *str, const char *delim);str:是指向要进行分割的字符串的指针。 strtok() 时,需要传入待分割的字符串NULL,以继续从上次的位置分割,因为函数会记录上一次分割的位置delim:是指向包含分隔符字符的字符串的指针。
函数的返回值:
NULL函数的使用:
1.处理连续分隔符
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello,World,How,Are,You";
const char delim[] = ",";
// 第一次调用 strtok(),传入待分割的字符串
char* token = strtok(str, delim);
while (token != NULL)
{
printf("分割出的子字符串: %s\n", token);
// 后续调用,传入 NULL
token = strtok(NULL, delim);
}
return 0;
}输出:
分割出的子字符串: Hello 分割出的子字符串: World 分割出的子字符串: How 分割出的子字符串: Are 分割出的子字符串: You
2.处理多分隔符
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "Hello;World|This-is/C";
const char delim[] = ";|-/";
char* token = strtok(str, delim);
while (token != NULL)
{
printf("分割出的子字符串: %s\n", token);
token = strtok(NULL, delim);
}
return 0;
}输出:
分割出的子字符串: Hello 分割出的子字符串: World 分割出的子字符串: This 分割出的子字符串: is 分割出的子字符串: C
函数的注意事项:
原字符串会被修改:strtok() 函数会将分隔符替换为字符串结束符 '\0',因此原字符串会被修改。
#include <stdio.h>
#include <string.h>
int main()
{
char original[] = "Apple,Banana,Cherry";
char copy[50];
strcpy(copy, original);
const char delim[] = ",";
char *token = strtok(copy, delim);
while (token != NULL)
{
printf("分割出的子字符串: %s\n", token);
token = strtok(NULL, delim);
}
printf("原字符串: %s\n", original);
return 0;
}输出:
分割出的子字符串: Apple 分割出的子字符串: Banana 分割出的子字符串: Cherry 原字符串: Apple,Banana,Cherry
传入的参数必须可读可写:strtok函数拆分字符串是直接在原串上进行的操作,所以要求传入的参数必须,可读可写。
char *str = "www. baidu.com" 不行!!!分隔符的处理:连续的分隔符会被视为一个分隔符。
"Hello,,World" 中连续的两个逗号会被当作一个分隔符处理,只替换第一个为\0,分割结果为 "Hello" 和 "World"空字符串的处理:如果字符串以分隔符开头,第一个返回的子字符串将是空字符串。
",Hello,World" 分割时,第一个返回的子字符串为空
函数的介绍:strcpy():用于将一个字符串复制到另一个字符串中。
\0)复制到目标缓冲区。src 中的内容复制到 dest 中,直到遇到 src 的结束符 '\0',并将这个结束符也复制到 dest 中。函数的原型:
char *strcpy(char *dest, const char *src);dest:目标字符串的起始地址,即要将源字符串复制到的内存位置。 \0)src:源字符串的起始地址,即要复制的字符串。 \0 结尾
函数的返回值:strcpy():返回目标字符串dest的起始地址,即复制后的字符串的地址。
函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char src[] = "Hello, World!";
char dest[20]; // 确保足够大
char* p = strcpy(dest, src);
printf("p: %s\n", p); // 输出:Hello, World!
printf("dest: %s\n", dest); // 输出:Hello, World!
return 0;
}函数的注意事项:
目标字符串的空间足够:目标字符串 dest 必须有足够的空间来容纳源字符串及其终止符 '\0'
#include <stdio.h>
#include <string.h>
int main()
{
char source[] = "Hello";
char destination[5]; // 目标字符串空间不足
char* p = strcpy(destination, source);
// 溢出!destination 容量不足(需要6字节,包含\0)
printf("%s\n", p);
return 0;
}避免自我复制:不要将源字符串和目标字符串设置为同一个字符串,否则可能会导致不可预期的结果。
char str[] = "Example";
strcpy(str, str); // 不建议这样做
函数的介绍:strncpy():用于将一个字符串的部分或全部内容复制到另一个字符串中。
strncpy() 函数会尝试将源字符串 src 中的最多 n 个字符复制到目标字符串 dest 中,具体复制规则如下:
src 的长度小于 n: strncpy() 会将 src 中的所有字符(包括字符串结束符 '\0')复制到 dest 中,然后用 '\0' 填充 dest 中剩余的空间,直到复制满 n 个字符src 的长度大于等于 n: strncpy() 只会复制 src 的前 n 个字符到 dest 中,并且不会自动在 dest 的末尾添加字符串结束符 '\0'函数的原型:
char *strncpy(char *dest, const char *src, size_t n);dest:目标字符串的起始地址,即要将源字符串复制到的内存位置。src:源字符串的起始地址,即要复制的字符串。n:目标字符串的最大长度,即最多复制的字符数。
函数的返回值:strncpy():返回目标字符串dest的起始地址,即复制后的字符串的地址。
函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20];
char src[] = "hello world";
char* p = strncpy(dest, src, 5);
printf("%s\n", p);
dest[5] = '\0';
printf("%s\n", p);
return 0;
}输出:
hello烫烫烫烫烫烫烫烫烫?N┖濑? hello
代码示例:更好的使用strncpy()函数的方法
#include <stdio.h>
#include <string.h>
int main()
{
char src[] = "Hello, World!";
char dest[10];
char* p = strncpy(dest, src, sizeof(dest) - 1); // 留1字节给\0
dest[sizeof(dest) - 1] = '\0'; // 手动添加终止符
printf("p: %s\n", p); // 输出:Hello, Wo
printf("dest: %s\n", dest); // 输出:Hello, Wo
return 0;
}函数的注意事项:
手动添加字符串结束符:当 src 的长度大于等于 n 时,strncpy() 不会在 dest 的末尾添加 '\0'
strncpy() 后,需要根据实际情况手动添加字符串结束符,否则在将 dest 作为字符串处理时可能会出现问题char dest[5];
strncpy(dest, "Hello", 5); // 复制5个字符,但未添加\0
printf("%s\n", dest); // 可能输出乱码(dest未以\0结尾)代码示例 :更好的理解 strcpy() 与 strncpy() 函数的区别
#include <stdio.h>
#include <string.h>
int main()
{
char src[] = "hello world";
char dest[10] = { 0 };
char* p = strncpy(dest, src, 10); // 字符串src 拷贝给dest
for (size_t i = 0; i < 10; i++)
{
printf("%c", p[i]);
}
printf("\n");
printf("p= %s\n", p);
printf("dest = %s\n", dest);
system("pause");
return 0;
}输出:
hello worl
p= hello worl烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫橔/戈
dest = hello worl烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫橔/戈strcpy() 与 strncpy() 的对比:
特性 | strcpy() | strncpy() |
|---|---|---|
复制长度 | 复制整个字符串(包括 \0) | 最多复制 n 个字符 |
安全性 | 不安全(不检查缓冲区大小) | 相对安全(限制最大长度) |
自动添加 \0 | 是 | 仅在 src 长度 < n 时添加 |
性能 | 高(直接复制) | 低(可能需要填充 \0) |
适用场景 | 已知 src 长度且目标缓冲区足够大时 | 需要控制复制长度,避免溢出 |
总结:
strcpy() 容易导致缓冲区溢出,因此在处理不可信输入时应避免使用。strncpy() 更安全,但需要手动处理字符串的终止符 \0,否则可能导致未定义行为。
函数的介绍:strcat():用于将一个字符串追加到另一个字符串的末尾。
dest 的结束符 '\0',然后从这个位置开始将源字符串 src 的内容复制过去,包括源字符串的结束符 '\0','\0'函数的原型:
char *strcat(char *dest, const char *src);dest:目标字符串的起始地址,即要将源字符串追加到的字符串。 \0 结尾的有效 C 字符串。src:源字符串的起始地址,即要追加的字符串。
函数的返回值:strcat():返回目标字符串dest的起始地址,即连接后的字符串的地址。
函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20] = "Hello, ";
char src[] = "World!";
// 使用 strcat() 函数将 src 字符串追加到 dest 字符串末尾
strcat(dest, src);
printf("追加后的字符串: %s\n", dest);
return 0;
}函数的注意事项:
目标字符串的空间足够:目标字符串 dest 必须有足够的空间来容纳源字符串追加后的内容,包括源字符串和结束符 '\0'
#include <stdio.h>
#include <string.h>
int main()
{
char dest[10] = "Hello";
char src[] = " World!";
// 目标字符串空间不足
strcat(dest, src);
printf("%s\n", dest);
return 0;
}目标字符串必须以 '\0'结尾:
strcat() 函数通过查找目标字符串的结束符 '\0' 来确定追加的起始位置。
'\0' 结尾,strcat() 函数会继续在内存中查找,直到找到 '\0' 为止,这可能会导致不可预期的结果。
自我追加问题:不能将目标字符串和源字符串设置为同一个字符串。
char str[20] = "Example";
strcat(str, str); // 不建议
函数的介绍:strncat():用于将源字符串中的最多n个字符追加到目标字符串的末尾。
dest 的结束符 '\0'
src 中的字符逐个复制到目标字符串 dest 中,最多复制 n 个字符。
src 的长度小于 n,则会将整个源字符串(包括结束符 '\0')追加到目标字符串 dest 中
src 的长度大于或等于 n,则只会追加n 个字符,并在目标字符串 dest 的末尾添加一个字符串结束符 '\0'
函数的原型:
char *strncat(char *dest, const char *src, size_t n);dest:目标字符串的起始地址,即要将源字符串追加到的字符串。 '\0'\0 结尾的有效 C 字符串src:源字符串的起始地址,即要追加的字符串。n:最多从源字符串中追加到目标字符串的字符数量。
函数的返回值:strcat():返回目标字符串dest的起始地址,即连接后的字符串的地址。
函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20] = "Hello, ";
char src[] = "World!";
// 使用 strncat() 函数将 src 字符串的前 3 个字符追加到 dest 字符串末尾
strncat(dest, src, 3);
printf("追加后的字符串: %s\n", dest); //输出:Hello, Wor
return 0;
}函数的注意事项:
目标字符串的空间:目标字符串 dest 必须有足够的空间来容纳追加的字符和最终的字符串结束符 '\0'
#include <stdio.h>
#include <string.h>
int main()
{
char dest[10] = "Hello";
char src[] = " World!";
// 目标字符串空间不足
strncat(dest, src, 5); //dest初始存储"Hello"后剩余空间不足以容纳追加的5个字符及结束符
printf("%s\n", dest);
return 0;
}目标字符串需以'\0'结尾:
strncat() 函数通过查找目标字符串的结束符 '\0' 来确定追加的起始位置。'\0' 结尾,函数会继续在内存中查找,直到找到 '\0' 为止,这可能会导致不可预期的结果。strcat() 和 strncat() 的区别:
特性 | strcat() | strncat() |
|---|---|---|
追加长度 | 追加整个 src 字符串(包括 \0) | 最多追加 n 个字符 |
自动添加 \0 | 是 | 是 |
缓冲区溢出风险 | 高(如果 dest 不够大) | 较低(可以控制追加的长度) |
适用场景 | 已知 dest 足够大 | 需要控制追加长度,避免溢出 |
函数的介绍:strcmp():用于完全比较两个字符串。
strcmp() 函数会逐字符比较两个字符串,比较是基于字符的 ASCII 值进行的遇到不同的字符或者到达字符串的结束符 '\0'遇到不同的字符或者到达字符串末尾 函数的原型:
int strcmp(const char *str1, const char *str2);str1:指向第一个要比较的字符串的指针。str2:指向第二个要比较的字符串的指针。
函数的返回值:
相等,返回 0小于 s2,返回一个负整数大于 s2,返回一个正整数函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "apple";
char str2[] = "banana";
int result = strcmp(str1, str2);
if (result < 0)
{
printf("%s按字典序小于%s\n", str1, str2);
}
else if (result > 0)
{
printf("%s按字典序大于%s\n", str1, str2);
}
else
{
printf("%s等于%s\n", str1, str2);
}
return 0;
}输出:
apple按字典序小于banana
函数的介绍:strncmp():用于比较两个字符串str1和str2的前n个字符。
strncmp() 函数会它会逐字符比较这两个字符串,基于字符的 ASCII 值进行判断,直到出现以下情况之一:
n 个字符'\0'函数的原型:
int strncmp(const char *str1, const char *str2, size_t n);s1:指向第一个要比较的字符串的指针。s2:指向第二个要比较的字符串的指针。n:表示最多比较的字符数量。
函数的返回值:
相等,返回 0小于 s2 的前 n 个字符,返回一个负整数大于 s2 的前 n 个字符,返回一个正整数函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello, World!";
char str2[] = "Hello, C!";
char str3[] = "Goodbye";
int result1 = strncmp(str1, str2, 5); // 比较前 5 个字符
int result2 = strncmp(str1, str3, 5); // 比较前 5 个字符
printf("strncmp(str1, str2, 5): %d\n", result1); // 输出:0
printf("strncmp(str1, str3, 5): %d\n", result2); // 输出:正整数(具体值取决于实现)
return 0;
}strcmp() 和 strncmp() 的区别:
特性 | strcmp() | strncmp() |
|---|---|---|
比较长度 | 比较整个字符串(直到 \0) | 只比较前 n 个字符 |
比较范围 | 无限制 | 受 n 限制 |
适用场景 | 需要完全匹配时使用 | 需要部分匹配或限制比较长度时使用 |
函数的介绍:sprintf():用于将格式化后的内容存储到一个字符数组中。
<stdio.h> 头文件中。
函数的原型:
int sprintf(char *str, const char *format, ...);str:是指向一个字符数组的指针,用于存储格式化后的字符串。 '\0'format:是格式化字符串,它指定了后续参数应该如何被格式化。 普通字符和格式说明符 % 开头,用于指定不同类型数据的输出格式。%d 用于整数,%f 用于浮点数,%s 用于字符串等。...:是可变参数列表,根据 format 字符串中的格式说明符,提供相应数量和类型的参数。
函数的返回值:成功时:返回值是一个整数,表示成功写入目标字符串的字符数(不包括末尾的\0) 失败时:返回一个负数
函数的使用:
#include <stdio.h>
int main()
{
char buffer[50];
int num = 123;
float f = 3.14;
char str[] = "Hello";
// 使用 sprintf() 函数将不同类型的数据格式化到 buffer 数组中
int result = sprintf(buffer, "整数: %d, 浮点数: %.2f, 字符串: %s", num, f, str);
printf("格式化后的字符串: %s\n", buffer);
printf("存储的字符数量: %d\n", result);
return 0;
}输出:
格式化后的字符串: 整数: 123, 浮点数: 3.14, 字符串: Hello 存储的字符数量: 38
函数的介绍:sscanf():用于从一个字符串中按照指定的格式读取数据,并将读取的数据存储到相应的变量中。
<stdio.h> 头文件中。 sscanf() 函数会根据 format 字符串的规则,从 str 字符串中提取数据,并将这些数据存储到可变参数列表指定的变量中。
它会按照格式说明符依次读取字符串中的数据,直到遇到不匹配的字符或者字符串结束。
函数的原型:
int sscanf(const char *str, const char *format, ...);str:是指向要进行解析的字符串的指针,该字符串包含了需要提取的数据。format:是格式化字符串,它规定了如何从 str 中读取数据。...:是可变参数列表,一系列用于存储读取数据的变量的地址,这些变量的类型要与 format 中的格式说明符相匹配。
函数的返回值:成功时:返回成功匹配并赋值的输入项的数量 失败时:返回EOF
函数的使用:
#include <stdio.h>
int main()
{
char input[] = "123 3.14";
int num;
float f;
// 使用 sscanf() 从字符串中读取整数和浮点数
int result = sscanf(input, "%d %f", &num, &f);
if (result == 2)
{
printf("成功读取整数: %d, 浮点数: %.2f\n", num, f);
}
else
{
printf("读取数据失败\n");
}
return 0;
}输出:
成功读取整数: 123, 浮点数: 3.14
函数的注意事项:
变量地址传递:在传递用于存储读取数据的变量时,必须传递它们的地址(使用 & 运算符)否则函数无法将数据正确存储到变量中。
int num,应该传递 &num缓冲区溢出风险:当使用 %s 格式说明符读取字符串时,如果没有限制读取的字符数量,可能会导致缓冲区溢出。
%ns(n 为正整数)的形式来限制读取的最大字符数 %19s 表示最多读取 19 个字符,会预留一个位置给字符串结束符 '\0'#include <stdio.h>
int main()
{
char input[] = "This is a very long string";
char str[10];
// 限制读取的字符数,避免缓冲区溢出
sscanf(input, "%9s", str); //最多读取9个字符,留一个位置给字符串结尾的空字符 \0
printf("读取的字符串: %s\n", str);
return 0;
}输出:
读取的字符串: This
分析:
"This"? sscanf 使用 %9s 格式说明符,表示最多读取 9 个字符。%s 在读取时会遇到空白字符(如空格)停止。input 中,第一个单词是 "This",后面有一个空格,因此 sscanf 读取到 "This" 后停止。代码示例:使用正则表达[^\n]式让 sscanf 函数可以读取包括空格的字符串
#include <stdio.h>
int main()
{
char input[] = "This is a very long string";
char str[20]; // 增大缓冲区大小
// 读取直到换行符(不包括换行符)
sscanf(input, "%19[^\n]", str);
printf("读取的字符串: %s\n", str);
return 0;
}格式匹配:format 字符串中的普通字符必须与 str 中的对应字符完全匹配。
format 为 "x = %d",那么 str 必须以 "x = " 开头,后面接着一个整数,否则读取会失败。sprintf() 和 sscanf() 的区别:
特性 | sprintf() | sscanf() |
|---|---|---|
功能 | 将格式化数据写入字符串 | 从字符串中读取格式化数据 |
目标 | 字符串 str | 字符串 str |
返回值 | 写入的字符数 | 成功读取的输入项数量 |
适用场景 | 生成格式化字符串 | 解析格式化字符串 |
函数的介绍:atoi():用于将字符串转换为整数。
atoi()是 C 语言标准库 <stdlib.h> 中的一个函数。 atoi() 函数会扫描输入的字符串,跳过前面的空白字符(如:空格、制表符 \t、换行符 \n等),然后尝试将后续的字符序列转换为一个整数。
转换过程会在遇到非数字字符(除了开头的正负号)时停止。
符号处理:字符串开头可以有一个可选的正负号(+ 或 -),用于表示整数的正负性。数字识别:函数会识别字符串中的数字字符(0 - 9),并将其转换为对应的整数值。终止条件:当遇到非数字字符(除开头的正负号)或字符串末尾时,转换停止。函数的原型:
int atoi(const char *nptr);nptr:是指向要转换为整数的字符串的指针。
函数的返回值:
函数的使用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str1[] = "1234";
char str2[] = " -567";
char str3[] = "12abc";
char str4[] = "abc123";
int num1 = atoi(str1);
int num2 = atoi(str2);
int num3 = atoi(str3);
int num4 = atoi(str4);
printf("str1 转换后的整数: %d\n", num1);
printf("str2 转换后的整数: %d\n", num2);
printf("str3 转换后的整数: %d\n", num3);
printf("str4 转换后的整数: %d\n", num4);
return 0;
}1.处理数字字符
#include <stdio.h>
#include <stdlib.h>
int main()
{
const char* str1 = "12345";
const char* str2 = "-6789";
const char* str3 = " 42";
int num1 = atoi(str1);
int num2 = atoi(str2);
int num3 = atoi(str3);
printf("str1 转换后的整数: %d\n", num1);
printf("str2 转换后的整数: %d\n", num2);
printf("str3 转换后的整数: %d\n", num3);
return 0;
}输出:
str1 转换后的整数: 12345 str2 转换后的整数: -6789 str3 转换后的整数: 42
2.处理非数字字符
#include <stdio.h>
#include <stdlib.h>
int main()
{
const char* str1 = "123abc";
const char* str2 = "abc123"; //开头就是非数字字符,无法转换为有效的整数
const char* str3 = "12.34";
int num1 = atoi(str1);
int num2 = atoi(str2);
int num3 = atoi(str3);
printf("str1 转换后的整数: %d\n", num1);
printf("str2 转换后的整数: %d\n", num2);
printf("str3 转换后的整数: %d\n", num3);
return 0;
}输出:
str1 转换后的整数: 123 str2 转换后的整数: 0 str3 转换后的整数: 12
3.处理溢出
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{
const char* str1 = "2147483647"; // INT_MAX
const char* str2 = "2147483648"; // 超出 INT_MAX
int num1 = atoi(str1);
int num2 = atoi(str2);
printf("str1 转换后的整数: %d\n", num1);
printf("str2 转换后的整数: %d\n", num2);
return 0;
}输出:
str1 转换后的整数: 2147483647 str2 转换后的整数: 2147483647
函数的注意事项:
错误处理有限:atoi 函数在遇到无法转换的情况时,只是简单地返回 0,没有提供更详细的错误信息。
溢出问题:atoi 函数不会检查转换结果是否会导致整数溢出。
int 类型的表示范围,可能会得到错误的结果。 int 时,可能会发生溢出。空白字符:atoi 函数会跳过开头的空白字符,但不会跳过字符串中间的空白字符。
👨💻 博主正在持续更新C语言基础系列中。 ❤️ 如果你觉得内容还不错,请多多点赞。 ⭐️ 如果你觉得对你有帮助,请多多收藏。(防止以后找不到了) 👨👩👧👦
C语言基础系列持续更新中~,后续分享内容主要涉及C++全栈开发的知识,如果你感兴趣请多多关注博主。