高阶C语言|深入理解字符串函数和内存函数

文章目录

前言

在C语言中,字符和字符串是常用的数据类型。然而,C语言并没有专门的字符串类型,所有字符串都是通过字符数组或字符串常量来表示。为了处理这些字符串,C语言提供了许多强大的库函数。本文将详细介绍这些常用的字符和字符串处理函数,以及它们的使用方法和注意事项。

1.求字符串长度

1.1 字符串长度函数:strlen

strlen 函数用来计算字符串的长度。它返回字符串中不包含终止字符 '\0' 的字符数量。

c 复制代码
size_t strlen(const char *str);

使用示例:

c 复制代码
#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;
}
模拟实现
c 复制代码
#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'

c 复制代码
char *strcpy(char *destination, const char *source);

注意事项:

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须足够大,以容纳源字符串。

使用示例:

c 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char dest[20];
    strcpy(dest, "Hello");
    printf("Destination string: %s\n", dest); // 输出:Hello
    return 0;
}
模拟实现
c 复制代码
#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' 放置在新字符串的末尾。

c 复制代码
char *strcat(char *destination, const char *source);

使用示例:

c 复制代码
#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;
}
模拟实现
c 复制代码
#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 函数比较两个字符串的大小。它返回一个整数,根据两个字符串的字典顺序进行比较。

c 复制代码
int strcmp(const char *str1, const char *str2);
  • 如果 str1 大于 str2,返回大于 0 的值。
  • 如果 str1 等于 str2,返回 0。
  • 如果 str1 小于 str2,返回小于 0 的值。

使用示例:

c 复制代码
#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;
}
模拟实现
c 复制代码
#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

c 复制代码
char * strncpy ( char * destination, const char * source, size_t num );
  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

2.2strncat

c 复制代码
char * strncat ( char * destination, const char * source, size_t num );

在目标字符串结尾追加源字符串前num个字符


2.3strncmp

c 复制代码
int strncmp ( const char * str1, const char * str2, size_t num );

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。


字符串查找

3.1字符串查找函数:strstr

strstr 函数查找一个字符串在另一个字符串中首次出现的位置。如果找到,则返回该位置的指针;如果未找到,则返回 NULL

c 复制代码
char *strstr(const char *haystack, const char *needle);

使用示例:

c 复制代码
#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;
}
模拟实现
c 复制代码
#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

c 复制代码
char *strtok(char *str, const char *delim);

注意事项:

  • strtok 会修改原始字符串,因此一般会拷贝字符串后再进行分割。
  • 每次调用 strtok 返回字符串中的下一个标记,后续调用需要将第一个参数设为 NULL

使用示例:

c 复制代码
#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 函数根据错误码返回相应的错误信息。

c 复制代码
char *strerror(int errnum);

使用示例:

c 复制代码
#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 内存操作函数:memcpymemmove

  • memcpy 用于从源内存区域复制指定字节的数据到目标内存区域。
  • memmovememcpy 类似,但它处理内存重叠的情况。
c 复制代码
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);

使用示例:

c 复制代码
#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模拟实现
c 复制代码
#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模拟实现
c 复制代码
#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 函数用于将一块内存区域的所有字节设置为指定的值。通常用于初始化内存或将内存区域清零。

c 复制代码
void *memset(void *s, int c, size_t n);
  • s:指向内存区域的指针。
  • c:要设置的值(以无符号字符形式)。
  • n:要设置的字节数。

注意事项:

  • memset 函数将指定的字节值填充到内存区域中,可以用于初始化数组或结构体的值。
  • 由于是按字节处理,因此它适用于任何类型的内存块。

使用示例:

c 复制代码
#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 的应用场景

  1. 初始化数组或结构体 :在动态分配内存或创建数据结构时,使用 memset 可以方便地初始化内存,将内存区域填充为特定的值。例如,常见的做法是使用 memset 来清零结构体或数组中的内容。

  2. 清空敏感数据 :当处理涉及敏感信息(如密码、密钥)的程序时,使用 memset 可以确保这些数据在使用后被立即清除,以减少安全隐患。

  3. 内存清零 :在程序需要清除缓存或重置数据时,memset 可以帮助快速设置内存区域为零。


4.4 内存比较函数:memcmp

memcmp 函数用于比较两个内存区域的内容。它按字节逐一比较两个内存块,并根据比较结果返回一个整数值。

c 复制代码
int memcmp(const void *ptr1, const void *ptr2, size_t num);
  • ptr1:指向第一个内存区域的指针。
  • ptr2:指向第二个内存区域的指针。
  • num:要比较的字节数。

返回值

  • 如果两个内存区域的内容完全相同,则返回 0
  • 如果 ptr1 指向的内存块大于 ptr2,则返回大于 0 的值。
  • 如果 ptr1 指向的内存块小于 ptr2,则返回小于 0 的值。

使用示例:

c 复制代码
#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 比较了两个字符数组 buffer1buffer2 的内容。由于在比较过程中,DWgaOtP12df0 的字节值大于 DWGAOTP12DF0,所以返回的值大于 0。

注意事项:
  1. 按字节比较memcmp 是按字节逐一比较内存区域的内容,因此它不会考虑数据的类型。如果要比较更复杂的数据类型(例如结构体),需要保证结构体成员的字节表示没有问题。
  2. 内存重叠问题 :与 memcpy 不同,memcmp 不会处理内存重叠的情况,它只比较内存的内容,而不考虑是否存在重叠。因此在使用 memcmp 时要确保比较的内存区域不会重叠。
应用场景:
  1. 内存区域比较memcmp 适用于需要比较两个内存区域是否相等的场景,例如在数据校验或加密算法中经常使用它来比较数据块的内容。
  2. 查找差异 :当需要查找两个内存块中不同的位置时,可以使用 memcmp 来快速发现差异。

相关推荐
SmartRadio几秒前
MK8000(UWB射频芯片)与DW1000的协议适配
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·dw1000
guygg882 分钟前
基于捷联惯导与多普勒计程仪组合导航的MATLAB算法实现
开发语言·算法·matlab
fengfuyao9853 分钟前
遗传算法与粒子群算法求解非线性函数最大值问题
算法
LeetCode天天刷17 分钟前
【软件认证】比特翻转【滑动窗口】
算法
froginwe1117 分钟前
Rust 文件与 IO
开发语言
源代码•宸19 分钟前
Leetcode—1123. 最深叶节点的最近公共祖先【中等】
经验分享·算法·leetcode·职场和发展·golang·dfs
liulilittle19 分钟前
LIBTCPIP 技术探秘(tun2sys-socket)
开发语言·网络·c++·信息与通信·通信·tun
yyy(十一月限定版)20 分钟前
c++(3)类和对象(中)
java·开发语言·c++
s砚山s22 分钟前
代码随想录刷题——二叉树篇(十三)
数据结构·算法
落羽凉笙22 分钟前
Python基础(4)| 玩转循环结构:for、while与嵌套循环全解析(附源码)
android·开发语言·python