C语言再学习25——常用字符串函数归纳
1. memcpy
内存复制函数。
1.1 头文件
#include <string.h>
1.2 描述
void *memcpy(void *str1, const void *str2, size_t n) ;
从存储区 str2 复制 n 个字符到存储区 str1。
1.3 参数
str1: 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针;
str2: 指向要复制的数据源,类型强制转换为 void* 指针;
n: 要被复制的字节数。
1.4 返回值
该函数返回一个指向目标存储区 str1 的指针。
1.5 注意
(1)复制的内容不同。strcpy 只能复制字符串,而 memcpy 可以复制任意内容,例如字符数组、整型、结构体、类等;
(2)复制的方法不同。strcpy 不需要指定长度,它遇到被复制字符的串结束符 "\0" 才结束,所以容易溢出。memcpy 则是根据其第 3 个参数决定复制的长度;
(3)用途不同。通常在复制字符串时用 strcpy,而需要复制其他类型数据时则一般用 memcpy;
(4)str1指针要分配足够的空间,也即大于等于 n 字节的空间。如果没有分配空间,会出现断错误,造成空间溢出;
(5)str1 和 str2 所指的内存空间不能重叠(如果发生了重叠,使用 memmove() 会更加安全);
(6)memcpy 是按照字节进行拷贝。多字节数据的拷贝如下:
测试:
void memcpy_test(void)
{
char src_buf[5] = {1, 2, 3, 4, 5};
char dst_buf[10] = {0};
int test_buf1[5] = {99, 8522, 987, 3467, 11027};
int test_buf2[5] = {0};
memcpy(dst_buf, src_buf, sizeof(src_buf));
for (int i = 0; i < sizeof(src_buf); i++)
{
printf("dst_buf: %d \r\n", dst_buf[i]);
}
memcpy(test_buf2, test_buf1, sizeof(test_buf1)); //sizeof(test_buf1) = 20字节
for (int i = 0; i < 5; i++)
{
printf("test_buf2: %d \r\n", test_buf2[i]);
}
}
结果输出:
1.6 原型实现
void *Memcpy(void *dest, const void *src, unsigned int count)
{
char *pdest = (char *)dest;
char *psrc = (char *)src;
if (pdest == NULL || psrc == NULL)
{
return NULL;
}
if (pdest == psrc)
{
return;
}
if (pdest > psrc)
{
while (count--)
{
*(pdest + count) = *(psrc + count);
}
}
else
{
while (count--)
{
*pdest++ = *psrc++;
}
}
return pdest;
}
2. memset
将某一块内存中的内容全部设置为指定的值,它是对较大的结构体或数组进行清零操作的一种快捷方法。
2.1 头文件
#include <string.h>
2.2 描述
void *memset(void *str, int c, size_t n) ;
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
2.3 参数
str: 指向要填充的内存块;
c: 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式;
n: 要被设置为该值的字节数。
2.4 返回值
该函数返回一个指向存储区 str 的指针。
2.5 注意
(1)memset 函数按字节对内存块进行初始化,所以不能用它将 int 数组初始化为 0 和 -1 之外的其他值(除非该值高字节和低字节相同);
(2)memset(void *str, int c, size_t n) 中 c 实际范围应该在 0——255,因为该函数只能取 c 的后八位赋值给你所输入的范围的每个字节。无论 c 多大只有后八位二进制有效,而后八位二进制的范围在(0~255)。而对字符数组操作时则取后八位赋值给字符数组,其八位值作为 ASCII 码。
测试:
void memset_test(void)
{
char test_buf1[5];
int test_buf2[5];
memset(test_buf1, 1, sizeof(test_buf1));
memset(test_buf2, 1, sizeof(test_buf2));
for (int i = 0; i < sizeof(test_buf1); i++)
{
printf("tset_buf1 is %x \r\n", test_buf1[i]);
}
for (int i = 0; i < 5; i++)
{
printf("tset_buf2 is %x \r\n", test_buf2[i]);
}
}
结果输出:
2.6 原型实现
void *MyMemset(void *dest, int n, unsigned int len)
{
if (dest == NULL)
return NULL;
char *pdest = (char *)dest;
while (len--)
{
*pdest++ = n;
}
return dest;
}
3. memcmp
把存储区 str1 和存储区 str2 的前 n 个字节进行比较,按字节比较。
3.1 头文件
#include <string.h>
3.2 描述
int memcmp(const void *str1, const void *str2, size_t n) ;
存储区 str1 和存储区 str2 的前 n 个字节进行比较。
3.3 参数
str1: 指向内存块的指针;
str2: 指向内存块的指针;
n: 要被比较的字节数。
3.4 返回值
如果返回值 < 0,则表示 str1 小于 str2;
如果返回值 > 0,则表示 str2 小于 str1;
如果返回值 = 0,则表示 str1 等于 str2。
3.5 注意
(1)该函数是按字节比较的,比较 str1 和 str2 的第一个字节的 ascII 码值。
测试:
void memcmp_test(void)
{
char str1[] = "abcd";
char str2[] = "abcd";
int ret = 0;
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
memcpy(str1, "abce", 4);
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
memcpy(str1, "abcc", 4);
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
}
结果输出:
3.6 原型实现
int My_Memcmp(void *str1, void *str2, int count)
{
char *ptr1 = (char *)str1;
char *ptr2 = (char *)str2;
if (!count)
{
return 0;
}
while (count--)
{
if (*ptr1 == *ptr2)
{
ptr1 = ptr1 + 1;
ptr2 = ptr2 + 1;
}
else
{
return *ptr1 - *ptr2;
}
}
return *ptr1 - *ptr2;
}
4. strlen
strlen 所作的是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符 '\0' 为止,然后返回字符计数器值(长度不包含 '\0 ')。
4.1 头文件
#include <string.h>
4.2 描述
size_t strlen(const char *str) ;
计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
4.3 参数
str: 要计算长度的字符串。
4.4 返回值
该函数返回字符串的长度。
4.5 注意
(1)注意 strlen 和 sizeof 的区别。
测试:
void strlen_test(void)
{
char *ptr = "asdfghjl";
printf("strlen length is %d \r\n", strlen(ptr));
}
结果输出:
输出字符串长度不包括 '\0'。
4.6 原型实现
unsigned int My_Strlen(const char *str)
{
int str_len = 0;
while (*str++ != '\0')
{
str_len++;
}
return str_len;
}
5. strcpy
5.1 头文件
#include <string.h>
5.2 描述
char *strcpy(char *dest, const char *src);
把 src 所指向的字符串复制到 dest。
5.3 参数
dest: 指向用于存储复制内容的目标数组。;
src: 要复制的字符串。
5.4 返回值
该函数返回一个指向最终的目标字符串 dest 的指针。
5.5 注意
(1)若目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况;
(2)src 和 dest 所指内存区域不可以重叠。
测试:
void strcpy_test(void)
{
char test_buf1[6] = "ABCDE";
char test_buf2[100] = {0};
strcpy(test_buf2, test_buf1);
printf("strcpy test %s \r\n", test_buf2);
}
5.6 原型实现
char *My_Strcpy(char *dest, const char *src)
{
char *tmp = (char *)dest;
if ((dest == NULL) || (src == NULL))
{
return 0;
}
while (*src != '\0')
{
*tmp++ = *src++;
}
*tmp = '\0';
return tmp;
}
6. strcmp
比较两个字符串并根据比较结果返回整数。基本形式为 strcmp(str1, str2),若 str1 = str2,则返回零;若 str1 < str2,则返回负数;若 str1 > str2,则返回正数。
6.1 头文件
#include <string.h>
6.2 描述
int strcmp(const char *str1, const char *str2);
把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
6.3 参数
str1: 要进行比较的第一个字符串;
str2: 要进行比较的第二个字符串。
6.4 返回值
如果返回值小于 0,则表示 str1 小于 str2;
如果返回值大于 0,则表示 str1 大于 str2;
如果返回值等于 0,则表示 str1 等于 str2。
6.5 注意
(1)两个字符串自左向右逐个字符相比(按 ASCII 值大小相比较),直到出现不同的字符或遇 '\0' 为止;
(2)只能比较字符串,即可用于比较两个字符串常量或比较数组,不能比较数字等其他形式的参数。
测试:
void strcmp_test(void)
{
int ret = 0;
// char test_buf1[6] = "ABCBE";
// char test_buf2[3] = "AB";
char test_buf1[6] = {1, 2, 3, 4, 5, 6};
char test_buf2[6] = {1, 2, 3, 7, 5, 6};
ret = My_Strcmp(test_buf1, test_buf2);
printf("ret val is %d \r\n", ret);
}
6.6 原型实现
int My_Strcmp(const char *str1, const char *str2)
{
if (str1 == NULL || str2 == NULL)
{
return NULL;
}
while (*str1 == *str2)
{
str1++;
str2++;
if (*str1 == '\0' || *str2 == '\0')
{
break;
}
}
return *str1 - *str2;
}
7. sprintf
字符串格式化命令。函数声明为 int sprintf(char *string, char *format [,argument,…]);,主要功能是把格式化的数据写入某个字符串中,即发送格式化输出到 string 所指向的字符串。
7.1 头文件
#include <stdio.h>
7.2 描述
int sprintf(char *str, const char *format, ...);
发送格式化输出到 str 所指向的字符串。
7.3 参数
str: 指向一个字符数组的指针,该数组存储了 C 字符串;
format : 字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags] [width] [.precision] [length]specifier,具体介绍如下:
flags(标识):
width(宽度):
.precision(精度):
length(长度):
specifier(说明符):
附加参数 -- 根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。
7.4 返回值
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
7.5 注意
(1)格式化数字字符串 sprintf 最常见的应用之一是把整数打印到字符串中;
(2)sprintf 的返回值是字符数组中字符的个数,即字符串的长度,不用在调用 strlen(str) 求字符串的长度;
测试:
void sprintf_test(void)
{
char buf[100] = {0};
double data = 3.141593;
uint16_t len = 0;
uint8_t test_buff[5] = {55, 22, 66, 77, 99};
len = sprintf((char *)buf, "%f", data);
printf("----------------------------------------------------------------- \r\n");
printf("buf is %s \r\n", buf);
printf("sprintf return len is %d \r\n", len);
len = 0;
for (uint16_t i = 0; i < sizeof(test_buff); i++)
{
len += sprintf((char *)buf + i * 2, "%d", test_buff[i]); //数组数据转换为字符串
}
printf("test_buff val is %s \r\n", buf);
printf("sprintf return len is %d \r\n", len);
printf("----------------------------------------------------------------- \r\n");
}
结果输出: