strlen、strcpy、strcat、strchr、strcmp、memcpy、memmove

本文深入解析了C语言中常见的字符串处理函数,如strlen、strcpy、strcat、strchr、strcmp、memcpy和memmove的实现原理及使用场景,通过代码示例详细阐述了这些函数如何在内存中操作字符串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'\0'为止,然后返回计数器值(长度不包含'\0')。

#include<stdio.h>
#include<assert.h>
int my_strlen(char *p)
{
	assert(p != NULL);
	int count = 0;
	while (*p)
	{
		count++;
		p++;
	}
	return count;
}
int my_strlen2(char *p)
{
	assert(p != NULL);
	if (*p == '\0')
		return 0;
	else
		return 1 + my_strlen2(p+1);
}
int main()
{
	char *p = "abcdef";//文本区、双引号后面默认加‘\0’{}后面得自己加'\0'
	char p2[] = "abcd";//栈
	printf("%d\n",my_strlen(p2));
	printf("%d\n",my_strlen2(p));
	system("pause");
}

strcpy是一种C语言的标准库函数,strcpy把从src地址开始且含有'\0'结束符的字符串复制到以dest开始的地址空间,返回值的类型为char*

#include<stdio.h>
#include<assert.h>
char* MY_strcpy(char* dest, const char* src)
{
	char * ret;
	assert(dest&&src);
	ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	/*while (*src)
	{
	*dest = *src;
	dest++;
	src++;
	}*/
	return ret;
}
int main()
{
	char arr[20] = "0";
	char avg[20] = "good job";
	MY_strcpy(arr, avg);
	printf("%s\n", avg);
	printf("%s\n", arr);
	system("pause");
	return 0;
}

strcat把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针

#include<stdio.h>
#include<assert.h>
char* my_strcat(char *dest, const char*src)
{
	char *ret = dest;
	assert(dest);
	assert(src);
	//1. 找目标的'\0'
	while (*dest)
	{
		dest++;
	}
	//2. 源拷贝到目标
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr[20] = "Good";
	char avg[10] = "game!!!";
	my_strcat(arr, avg);
	printf("%s\n", arr);
	system("pause");
	return 0;
} 

strchr是计算机编程语言的一个函数,原型为extern char *strchr(const char *s,char c),可以查找字符串s中首次出现字符c的位置。

#include<stdio.h>
char * my_strchr(char * arr, char ch)
{
	while (*arr != '\0')
	{
		if (*arr == ch)
		{
			return (char *)arr;
		}
		arr++;
	}
	return NULL;
}

int main()
{
	char *arr = "abcde";
	char *ret = my_strchr(arr, 'd');
	printf("%s\n", ret);
	system("pause");
	return 0;
}

matlab中函数,strcmp(s1,s2) 判断两个字符串s1和s2是否相同,相同返回true ,不同返回false。

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char *arr1, const char *arr2)
{
	char *s1 = arr1;
	char *s2 = arr2;
	assert(arr1&&arr2);
	while (*s1 && *s2)
	{
		if (*s1 == *s2)
		{
			s1++;
			s2++;
		}
		else if (*s1 > *s2)
			return 1;
		else
			return -1;
		
	}
	return 0;
}
int main()
{
	char *arr1 = "goodgame";
	char *arr2 = "goodgame";
	char *arr3 = "goodjob";
	int ret1 = my_strcmp(arr1, arr2);
	int ret2 = my_strcmp(arr1, arr3);
	printf("%d\n", ret1);
	printf("%d\n", ret2);
	system("pause");
	return 0;
}
///*字符串比较函数,一般形式为strcmp(字符串1,字符串2)。
//比较规则:
//对两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到‘\0’为止。
//如果全部字符相同,则认为相等;若出现不相同的字符,则以第一个不相同的字符的比较结果为准。
//如果两个字符串都由英文字母组成,则有一个简单的规律:在英文字典中位置在后面的为“大”,还要特别注意:小写字母比大写字母“大”。

memcpy指的是c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

//原型:extern void *memcpy(void *dest, void *src, unsigned int count);
//用法:#include <string.h>
//功能:由src所指内存区域复制count个字节到dest所指内存区域。
//说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针
//1、memcpy 函数用于 把资源内存(src所指向的内存区域) 拷贝到目标内存(dest所指向的内存区域);拷贝多少个?有一个size变量控制
//拷贝的字节数;
//函数原型:void *memcpy(void *dest, void *src, unsigned int count);
//用法:(1)可以拷贝任何类型的对象,因为函数的参数类型是void*(未定义类型指针),也就是说传进去的实参可以是int*,short*,char*等等,
//但是由于函数拷贝的过程是一个字节一个字节的拷贝的,所以实际操作的时候要把void*强制转化为char*,这样在指针加的时候才会保证每次加一个字节
#include<stdio.h>
#include<assert.h>
void *my_memcpy(void *dest, void *arr, size_t size)
{
	char *ret = dest;
	assert(dest != NULL);
	assert(arr != NULL);
	while (size-- )
	{
		*(char *)dest = *(char *)arr;
		++(char *)dest;
		++(char *)arr;
	}
	return ret;
}
int main()
{
	char arr[] = "goodjob!!!";
	char dest[20] = { 0 };
	char *ret =my_memcpy(dest, arr, strlen(arr));//第三个参数是需要拷贝的字节数
	printf("%s\n", ret);
	printf("%d", strlen(arr));
	system("pause");
	return 0;
}

memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

#include<stdio.h>
#include<assert.h>
void* my_memmove(void *dest, const void*src, size_t count)
 {
 	void *ret = dest;
 	assert(dest != NULL);
 	assert(src != NULL);
 	if(dest<src)
 	{
 		//前->后
 		while(count--)
 		{
 			*(char*)dest = *(char*)src;
 			dest = (char*)dest+1;
 			src = (char*)src+1;
 		}
 	}
 	else
 	{
		dest = (char *)dest + count - 1;
		src = (char *)src + count - 1;
 		//后->前
 		while(count--)
 		{
			*(char *)dest = *(char*)src;
			dest = (char *)dest - 1;
			src = (char*)src - 1;
 		}
 	}
 	return ret;
 }
  int main()
  {
	char arr2[] = "123456789";
  	char *ret = (char *)memmove(arr2+3, arr2+2, 4);
	printf("%s\n", ret);
	printf("%s\n", arr2);
	system("pause");
  	return 0;

 

<think>我们讨论的是针对ARM Cortex-M0内核的STM32F0系列微控制器。由于Cortex-M0是ARMv6-M架构,它没有像更高版本内核(如Cortex-M3/M4)那样丰富的字符串处理指令。但是,我们可以使用C语言标准库提供的字符串操作函数,这些函数在编译时会被优化为适当的机器指令。 在STM32F0开发中,我们通常使用标准C库(如ARM的CMSIS或newlib-nano)中的字符串和内存操作函数。这些函数包括: 1. `memcpy`:复制内存区域。 2. `memmove`:移动内存区域(处理重叠区域)。 3. `memset`:将内存区域设置为指定的值。 4. `memcmp`:比较两个内存区域。 5. `strcpy`:复制字符串。 6. `strncpy`:复制指定长度的字符串。 7. `strcat`:连接字符串。 8. `strncat`:连接指定长度的字符串。 9. `strcmp`:比较两个字符串。 10. `strncmp`:比较两个字符串的前n个字符。 11. `strlen`:计算字符串长度。 12. `strstr`:查找子字符串。 13. `strchr`:查找字符在字符串中第一次出现的位置。 此外,对于STM32F0,我们还可以使用CMSIS-DSP库(如果项目需要)中的一些优化函数,但CMSIS-DSP主要是为数字信号处理设计的,对于字符串操作,标准库通常已经足够。 在嵌入式系统中,我们通常需要注意这些函数的内存使用和性能,尤其是在没有硬件加速的情况下。对于Cortex-M0,这些函数通常是用汇编或C语言实现的,并针对该架构进行了优化。 下面给出一些使用示例: ```c #include <string.h> void example_memcpy(void) { char src[] = "Hello, World!"; char dest[20]; // 复制内存块 memcpy(dest, src, strlen(src) + 1); // +1 为了复制结束符'\0' } void example_memset(void) { char buffer[50]; // 将buffer的前10个字节设置为0 memset(buffer, 0, 10); } void example_strlen(void) { char str[] = "STM32"; int length = strlen(str); // 返回5 } void example_strcmp(void) { char str1[] = "Hello"; char str2[] = "World"; int result = strcmp(str1, str2); // 返回负值,因为Hello在World前面 } void example_strcat(void) { char dest[20] = "Hello"; char src[] = " World!"; strcat(dest, src); // dest变成"Hello World!" } ``` 注意:在嵌入式系统中,使用这些函数时要确保目标缓冲区足够大,以避免溢出。 另外,对于STM32F0,我们还可以使用硬件加速吗?实际上,Cortex-M0没有专门用于字符串操作的硬件加速,所以这些函数都是软件实现的。 如果我们想自己实现一些内存操作函数(例如,为了减少代码大小或提高特定情况下的性能),也可以自己编写。例如,一个简单的内存复制函数可以这样实现: ```c void my_memcpy(void *dest, const void *src, size_t n) { char *d = (char *)dest; const char *s = (const char *)src; for (size_t i = 0; i < n; i++) { d[i] = s[i]; } } ``` 但是,标准库的实现通常经过高度优化,可能使用了循环展开等技术,因此通常建议使用标准库函数。 最后,在STM32F0项目中,我们需要在链接时包含标准库(通常在IDE中已经配置好,比如使用STM32CubeIDE)。 §§相关问题§§: 1. 在STM32F0中,使用标准库的字符串函数是否安全(例如,是否有缓冲区溢出风险)?如何避免? 2. 在嵌入式系统中,如何优化字符串操作以减少内存占用? 3. 除了标准库,还有哪些针对嵌入式系统的字符串处理库? 4. 在STM32F0上,使用标准库的memcpy和自行实现的memcpy在性能上有何差异?</think>对于STM32F0系列(基于ARM Cortex-M0内核),以下是C语言中常用的字符串操作和内存操作函数及其实现原理。这些函数在标准C库(如ARM的`newlib-nano`)中已优化,可直接使用: ### 一、字符串操作函数 ```c #include <string.h> // 1. 字符串复制 char* strcpy(char* dest, const char* src); // 2. 带长度限制的字符串复制 char* strncpy(char* dest, const char* src, size_t n); // 3. 字符串连接 char* strcat(char* dest, const char* src); // 4. 带长度限制的字符串连接 char* strncat(char* dest, const char* src, size_t n); // 5. 字符串比较 int strcmp(const char* s1, const char* s2); // 6. 带长度限制的字符串比较 int strncmp(const char* s1, const char* s2, size_t n); // 7. 字符串长度 size_t strlen(const char* s); // 8. 查找字符 char* strchr(const char* s, int c); // 9. 查找子串 char* strstr(const char* haystack, const char* needle); ``` ### 二、内存操作函数 ```c #include <string.h> // 1. 内存复制(处理非重叠区域) void* memcpy(void* dest, const void* src, size_t n); // 2. 内存移动(处理重叠区域) void* memmove(void* dest, const void* src, size_t n); // 3. 内存设置 void* memset(void* s, int c, size_t n); // 4. 内存比较 int memcmp(const void* s1, const void* s2, size_t n); ``` ### 三、关键实现原理 1. **`memcpy`优化** STM32F0的`memcpy`通常使用字对齐复制(32位操作)加速: ```c void* memcpy(void* dest, const void* src, size_t n) { uint32_t* d32 = (uint32_t*)dest; const uint32_t* s32 = (const uint32_t*)src; // 按4字节复制主要部分 while (n >= 4) { *d32++ = *s32++; n -= 4; } // 剩余字节处理 uint8_t* d8 = (uint8_t*)d32; const uint8_t* s8 = (const uint8_t*)s32; while (n--) *d8++ = *s8++; return dest; } ``` 2. **`memset`优化** 利用32位写操作加速: ```c void* memset(void* s, int c, size_t n) { uint32_t c32 = (c << 24) | (c << 16) | (c << 8) | c; uint32_t* s32 = (uint32_t*)s; // 按4字节设置 while (n >= 4) { *s32++ = c32; n -= 4; } // 剩余字节处理 uint8_t* s8 = (uint8_t*)s32; while (n--) *s8++ = c; return s; } ``` 3. **`strlen`优化** 通过字对齐检查加速: ```c size_t strlen(const char* s) { const char* p = s; // 检查地址对齐 while ((uintptr_t)p & 3) { if (*p == '\0') return p - s; p++; } // 按4字节检查 const uint32_t* p32 = (const uint32_t*)p; while (1) { uint32_t v = *p32++; if ((v & 0xFF) == 0) return (const char*)p32 - s - 4; if ((v & 0xFF00) == 0) return (const char*)p32 - s - 3; if ((v & 0xFF0000) == 0) return (const char*)p32 - s - 2; if ((v & 0xFF000000) == 0) return (const char*)p32 - s - 1; } } ``` ### 四、使用注意事项 1. **内存对齐** STM32F0的Cortex-M0内核不支持非对齐内存访问,需确保: ```c // 正确示例(强制对齐) uint32_t __attribute__((aligned(4))) buffer[32]; memcpy(buffer, src, 128); // 确保128是4的倍数 ``` 2. **性能优化** - 优先使用`memcpy`而非`strcpy`处理已知长度的数据 - 避免在循环中反复调用`strlen` ```c // 错误用法 for (int i=0; i<strlen(s); i++) { ... } // 每次循环都计算长度 // 正确优化 size_t len = strlen(s); for (int i=0; i<len; i++) { ... } ``` 3. **安全函数替代** 为防止缓冲区溢出,建议使用安全版本: ```c // 使用strncpy替代strcpy char dest[10]; strncpy(dest, src, sizeof(dest)-1); dest[sizeof(dest)-1] = '\0'; // 手动添加结束符 ``` ### 五、编译器特定优化 在STM32CubeIDE中启用硬件加速: ```c #pragma GCC optimize("O3") // 启用最高优化级别 #define __USE_ARM_MEMCPY__ // 启用ARM优化的memcpy ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值