高阶C语言|深入理解字符串函数和内存函数
文章目录
- 前言
- 1.求字符串长度
- 1.1 字符串长度函数:`strlen`
- 模拟实现
- 长度不受限制的字符串函数
- 1.2 字符串拷贝函数:`strcpy`
- 模拟实现
- 1.3 字符串连接函数:`strcat`
- 模拟实现
- 1.4 字符串比较函数:`strcmp`
- 模拟实现
- 长度受限制的字符串函数
- 2.1`strncpy`
- 2.2`strncat`
- 2.3`strncmp`
- 字符串查找
- 3.1字符串查找函数:`strstr`
- 模拟实现
- 3.2字符串分割函数:`strtok`
- 错误信息报告
- 错误信息报告函数:`strerror`
- 内存操作函数
- 4.1 内存操作函数:`memcpy` 和 `memmove`
- `memcpy`模拟实现
- `memmove`模拟实现
- 4.2 内存操作函数:`memset`
- 注意事项:
- 4.3 `memset` 的应用场景
- 4.4 内存比较函数:`memcmp`
- 使用示例:
- 输出:
- 注意事项:
- 应用场景:
前言
在C语言中,字符和字符串是常用的数据类型。然而,C语言并没有专门的字符串类型,所有字符串都是通过字符数组或字符串常量来表示。为了处理这些字符串,C语言提供了许多强大的库函数。本文将详细介绍这些常用的字符和字符串处理函数,以及它们的使用方法和注意事项。
1.求字符串长度
1.1 字符串长度函数:strlen
strlen
函数用来计算字符串的长度。它返回字符串中不包含终止字符 '\0'
的字符数量。
size_t strlen(const char *str);
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
const char *str = "Hello, world!";
printf("Length of the string: %zu\n", strlen(str)); // 输出:13
return 0;
}
模拟实现
#include<stdio.h>
#include<assert.h>
//#include<string.h>
int my_strlen(const char* str)
{
assert(str);
int count = 0;
while (*str++) {
count++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen(arr);
printf("%d", ret);
return 0;
}
长度不受限制的字符串函数
1.2 字符串拷贝函数:strcpy
strcpy
函数将源字符串拷贝到目标字符串,包括终止符 '\0'
。
char *strcpy(char *destination, const char *source);
注意事项:
- 源字符串必须以
'\0'
结束。 - 目标空间必须足够大,以容纳源字符串。
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char dest[20];
strcpy(dest, "Hello");
printf("Destination string: %s\n", dest); // 输出:Hello
return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>
void my_strcpy(char* dest,const char* source)
{
assert(dest && source);
while (*dest++=*source++) {
;
}
}
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "hijklmn";
my_strcpy(arr1, arr2);
printf("%s", arr1);
return 0;
}
1.3 字符串连接函数:strcat
strcat
函数将源字符串追加到目标字符串的末尾。它会覆盖目标字符串末尾的 '\0'
,并将新的字符串结束符 '\0'
放置在新字符串的末尾。
char *strcat(char *destination, const char *source);
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello, ";
strcat(dest, "world!");
printf("Concatenated string: %s\n", dest); // 输出:Hello, world!
return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* source)
{
char* ret = dest;
while (*dest) {
dest++;
}
while (*dest++ = *source++) {
;
}
return ret;
}
int main()
{
char arr1[20] = "abcdf";
char arr2[10] = "ghijk";
my_strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}
1.4 字符串比较函数:strcmp
strcmp
函数比较两个字符串的大小。它返回一个整数,根据两个字符串的字典顺序进行比较。
int strcmp(const char *str1, const char *str2);
- 如果
str1
大于str2
,返回大于 0 的值。 - 如果
str1
等于str2
,返回 0。 - 如果
str1
小于str2
,返回小于 0 的值。
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
const char *str1 = "abc";
const char *str2 = "abd";
int result = strcmp(str1, str2);
if (result < 0) {
printf("str1 is less than str2\n");
} else if (result > 0) {
printf("str1 is greater than str2\n");
} else {
printf("str1 is equal to str2\n");
}
return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1,const char* str2)
{
assert(str1 && str2);
while (*str1== *str2) {
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcf";
int ret = my_strcmp(arr1, arr2);
if (ret == 0)
{
printf("=\n");
}
else if (ret > 0) {
printf(">\n");
}
else
printf("<\n");
return 0;
}
长度受限制的字符串函数
2.1strncpy
char * strncpy ( char * destination, const char * source, size_t num );
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
2.2strncat
char * strncat ( char * destination, const char * source, size_t num );
在目标字符串结尾追加源字符串前num个字符
2.3strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
字符串查找
3.1字符串查找函数:strstr
strstr
函数查找一个字符串在另一个字符串中首次出现的位置。如果找到,则返回该位置的指针;如果未找到,则返回 NULL
。
char *strstr(const char *haystack, const char *needle);
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
const char *str = "This is a simple string";
const char *substr = "simple";
char *result = strstr(str, substr);
if (result != NULL) {
printf("Found substring: %s\n", result); // 输出:simple string
}
return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
if (*str2 == '\0') {
return (char*)str1;
}
const char* s1 = NULL;
const char* s2 = NULL;
const char* cp = str1;
while (cp) {
s1 = cp;
s2 = str2;
while (*s1 !='\0' && *s2 != '\0' && * s1 == *s2) {
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)cp;
cp++;
}
return NULL;
}
int main()
{
char* p1 = "abbbcdef";
char* p2 = "ba";
char* ret = my_strstr(p1, p2);
if (ret = NULL) {
printf("Ҳ\n");
}
else {
printf("%s", ret);
}
return 0;
}
3.2字符串分割函数:strtok
strtok
函数将字符串分割成多个标记,每次调用 strtok
返回下一个标记。当没有更多标记时,返回 NULL
。
char *strtok(char *str, const char *delim);
注意事项:
strtok
会修改原始字符串,因此一般会拷贝字符串后再进行分割。- 每次调用
strtok
返回字符串中的下一个标记,后续调用需要将第一个参数设为NULL
。
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello, world! C programming";
char *token = strtok(str, " ,!");
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok(NULL, " ,!");
}
return 0;
}
错误信息报告
错误信息报告函数:strerror
strerror
函数根据错误码返回相应的错误信息。
char *strerror(int errnum);
使用示例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("Error: %s\n", strerror(errno));
}
return 0;
}
内存操作函数
4.1 内存操作函数:memcpy
和 memmove
memcpy
用于从源内存区域复制指定字节的数据到目标内存区域。memmove
与memcpy
类似,但它处理内存重叠的情况。
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "This is a test.";
char dest[20];
memcpy(dest, src, strlen(src) + 1);
printf("Copied string: %s\n", dest);
return 0;
}
memcpy
模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{
assert(dest && source);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)source;
dest = (char*)dest + 1;
source = (char*)source + 1;
}
return ret;
}
void test1()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr1, arr2, 20);
}
int main()
{
test1();
return 0;
}
memmove
模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memmove(void* dest, const void* source, size_t num)
{
assert(dest && source);
void* ret = dest;
if (dest < source) {
while (num--) {
*(char*)dest = *(char*)source;
dest = (char*)dest + 1;
source = (char*)source + 1;
}
}
else {
while (num--) {
*((char*)dest + num) = *((char*)source + num);
}
}
return ret;
}
void test1()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
my_memmove(arr+3, arr, 20);
}
int main()
{
test1();
//test2();
return 0;
}
4.2 内存操作函数:memset
memset
函数用于将一块内存区域的所有字节设置为指定的值。通常用于初始化内存或将内存区域清零。
void *memset(void *s, int c, size_t n);
s
:指向内存区域的指针。c
:要设置的值(以无符号字符形式)。n
:要设置的字节数。
注意事项:
memset
函数将指定的字节值填充到内存区域中,可以用于初始化数组或结构体的值。- 由于是按字节处理,因此它适用于任何类型的内存块。
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char str[20];
memset(str, 'A', sizeof(str) - 1); // 将前19个字节设置为'A'
str[19] = '\0'; // 确保字符串以'\0'结束
printf("The string is: %s\n", str); // 输出:AAAAAAAAAAAAAAAAAAA
return 0;
}
在上面的例子中,memset
将数组 str
的前 19 个字节设置为字符 'A'
,并在最后设置为字符串的终止符 '\0'
,确保它成为一个有效的字符串。
4.3 memset
的应用场景
-
初始化数组或结构体:在动态分配内存或创建数据结构时,使用
memset
可以方便地初始化内存,将内存区域填充为特定的值。例如,常见的做法是使用memset
来清零结构体或数组中的内容。 -
清空敏感数据:当处理涉及敏感信息(如密码、密钥)的程序时,使用
memset
可以确保这些数据在使用后被立即清除,以减少安全隐患。 -
内存清零:在程序需要清除缓存或重置数据时,
memset
可以帮助快速设置内存区域为零。
4.4 内存比较函数:memcmp
memcmp
函数用于比较两个内存区域的内容。它按字节逐一比较两个内存块,并根据比较结果返回一个整数值。
int memcmp(const void *ptr1, const void *ptr2, size_t num);
ptr1
:指向第一个内存区域的指针。ptr2
:指向第二个内存区域的指针。num
:要比较的字节数。
返回值:
- 如果两个内存区域的内容完全相同,则返回
0
。 - 如果
ptr1
指向的内存块大于ptr2
,则返回大于 0 的值。 - 如果
ptr1
指向的内存块小于ptr2
,则返回小于 0 的值。
使用示例:
#include <stdio.h>
#include <string.h>
int main() {
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int result = memcmp(buffer1, buffer2, sizeof(buffer1));
if (result > 0) {
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
} else if (result < 0) {
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
} else {
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
}
return 0;
}
输出:
'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.
在这个示例中,memcmp
比较了两个字符数组 buffer1
和 buffer2
的内容。由于在比较过程中,DWgaOtP12df0
的字节值大于 DWGAOTP12DF0
,所以返回的值大于 0。
注意事项:
- 按字节比较:
memcmp
是按字节逐一比较内存区域的内容,因此它不会考虑数据的类型。如果要比较更复杂的数据类型(例如结构体),需要保证结构体成员的字节表示没有问题。 - 内存重叠问题:与
memcpy
不同,memcmp
不会处理内存重叠的情况,它只比较内存的内容,而不考虑是否存在重叠。因此在使用memcmp
时要确保比较的内存区域不会重叠。
应用场景:
- 内存区域比较:
memcmp
适用于需要比较两个内存区域是否相等的场景,例如在数据校验或加密算法中经常使用它来比较数据块的内容。 - 查找差异:当需要查找两个内存块中不同的位置时,可以使用
memcmp
来快速发现差异。