0
点赞
收藏
分享

微信扫一扫

【C语言】常用的字符串函数和内存函数



文章目录

  • ​​1.字符串函数​​
  • ​​1.1 strlen​​
  • ​​1.2 strcpy​​
  • ​​1.3 strcat​​
  • ​​1.4 strcmp​​
  • ​​1.5 strncpy/cat/cmp​​
  • ​​1.6 strstr​​
  • ​​1.7 strcok​​
  • ​​1.8 strerror​​
  • ​​2.内存函数​​
  • ​​2.1 memcpy​​
  • ​​2.2 memmove​​
  • ​​2.3 memcmp​​
  • ​​2.4 memset​​
  • ​​结语????​​


今天我们来学习一些新的字符串函数和内存函数,了解它们背后运行的原理,并完成部分函数的

自我实现​????

1.字符串函数

1.1 strlen

这个函数我们已经很熟悉了,它的作用是计算字符串的大小,以​​\0​​作为结尾

模拟实现如下:

//1.strlen模拟实现
int my_strlen(char* p)
{
assert(p);
int count = 0;
while (*p)
{
count++;
p++;
}
return count;
}

int main()
{
char arr[] = { "abcdef" };
int sz = my_strlen(arr);

printf("sz=%d\n", sz);
return 0;
}

​assert​​:断言,库函数,用于判断指针是否为空,若为空会报错

1.2 strcpy

该函数用于拷贝字符串,将arr2里的内容拷贝到arr1里

char* strcpy(char * destination, const char * source );
//日常使用
strcpy(arr1,arr2);

它有以下几个特点


Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).


  • 源字符串必须以 ‘\0’ 结束
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可修改

如果源字符串里没有\0,该函数就无法正确进行拷贝

【C语言】常用的字符串函数和内存函数_c语言

strcpy拷贝的时候是​​复制​​而不是剪贴,源空间里的内容不会消失

以下模拟实现

需要注意的就是strcpy会将源字符串的’\0’一并拷贝,所以在编写判断条件的时候就要考虑到这个情况

//2.strcpy模拟实现,拷贝
char* my_strcpy(char* a2, const char* a1)
{
char* dest = a2;
assert(a1 && a2);
do
{
*a2++ = *a1;
} while (*a1++);
return dest;
}

int main()
{
char arr1[] = { "abcdef" };
char arr2[15]="xxxxxxxxx";
my_strcpy(arr2, arr1);
puts(arr2);

return 0;
}

1.3 strcat

strcat函数用于追加字符串,简单来说就是把两个字符串接在一起

char * strcat ( char * destination, const char * source );

//以下是使用
char arr1[10] = "hello";
char arr2[] = "bit";
strcat(arr1,arr2);
puts(arr1);//结果为"hellobit"
  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。

注意,使用strcat函数的时候不能自己追加自己,程序会死循环

【C语言】常用的字符串函数和内存函数_数据_02

以下是模拟实现

  • 先让dest找到目的地字符串里的​​\0​
  • 然后进行追加,注意源字符串里的​​\0​​同样会被追加过去
//3,strcat函数
char* my_strcat(char* dest, const char* sour)
{
assert(dest && sour);
char* ptr = dest;
while (*dest)
{
dest++;
}
do
{
*dest++ = *sour;
} while (*sour++);

return ptr;
}

int main()
{
char arr1[10] = "hello";
char arr2[] = "bit";
my_strcat(arr1, arr2);
puts(arr1);
return 0;
}

1.4 strcmp

strcmp我们也是经常使用的,用于比较字符串

int strcmp ( const char * str1, const char * str2 );

该函数在比较字符串的时候,实际上是一个字符一个字符地比较的

【C语言】常用的字符串函数和内存函数_字符串_03

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

模拟实现

在VS编译器下,如果字符串s1大于s2,返回的是1。若小于返回的是-1。但C语言只要求大于的时候返回大于0的数字,小于的时候返回小于0的数字,​所以我们可以直接用字符相减得出返回值

//4.strcmp函数
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while(*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[] = { "abcdef" };
char arr2[] = { "abcd" };

printf("%d\n",my_strcmp(arr2, arr1));
return 0;
}

1.5 strncpy/cat/cmp

以上4个库函数,都对操作数没有要求。

而​​strncpy​​​、​​strncat​​​、​​strncmp​​这三个库函数,对操作数有要求

  • strncmp,最后一个参数4,代表只对比前4个字符的大小
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcwef";
char arr2[] = "abcqqqqqq";
int ret = strncmp(arr1, arr2, 4);

printf("%d\n", ret);
return 0;
}

【C语言】常用的字符串函数和内存函数_开发语言_04

  • strncat,最后一个参数3,代表只追加​前3个字符​到目的地
  • strncpy,最后一个参数5,代表只拷贝​前5个字符​到目的到底

【C语言】常用的字符串函数和内存函数_c语言_05

这三个函数的用法非常简单,这里不多赘述!

1.6 strstr

这个函数就是第一次见了,它的作用是在字符串s1里面查找是否有字符串s2

  • 如果有,返回字符串s2在字符串s1里的起始地址
  • 如果没有,返回NULL

【C语言】常用的字符串函数和内存函数_后端_06

模拟实现

str函数的模拟实现相对来说比较复杂

最重要的就是遇到​多个字符相同而最后不同​的情况

  • 需要用另外一个指针C来遍历字符串,找寻C和ptr2所指元素相等的第一个字符
  • 然后用ptr1来和ptr2比较,C保持不变
  • 如果匹配成功,返回C指针
  • 如果匹配失败,C++后赋值给ptr1,继续进行查找

【C语言】常用的字符串函数和内存函数_后端_07

//5.strstr 判断str1里面有没有str2//如果有,返回str1里str2的起始地址//如果str1不包含str2,返回nullchar* my_strstr(const char* str1, const char* str2){  const char* s1 = str1;  const char* s2 = str2;  const char* cur = str1; assert(str1 && str2); if (*str2 == '\0')  {   return (char*)str1; } while (*cur)  {   s1 = cur;   s2 = str2;    while (*s1 && *s2 && *s1 == *s2)    {     s1++;     s2++;   }   if (*s2 == '\0')      return (char*)cur;    cur++;  } return NULL;    }int main(){  char arr1[15] = { "helloworld" }; char arr2[] = { "owo" };  char* p=strstr(arr1, arr2); if (p == NULL)  {   printf("找不到\n");  } else  {   printf("%s\n", p);  } return 0;}

1.7 strcok

该函数用于查找一个字符串中的分隔符

char * strtok ( char * str, const char * sep );
  • sep参数是个字符串,定义了用作分隔符的字符集合

  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记

  • strtok函数找到str中的下一个标记,并将其用​​\0​​结尾,返回一个指向这个标记的指针


    注:strtok函数会改变被操作的字符串(把分隔符改为\0),所以在使用strtok函数切分的字符串一般都是临时拷贝的内容,并且可修改


  • strtok函数的第一个参数不为 NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置

  • strtok函数的第一个参数为 NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记

  • 如果字符串中不存在更多的标记,则返回 NULL 指针

如下如所示,在后续调用的时候,我们可以往strtok函数里传入buf,也可以直接传入NULL,因为传入NULL的时候该函数会在上一次操作的字符串里继续查找分隔符

【C语言】常用的字符串函数和内存函数_字符串_08

这样写很多行太麻烦,我们可以尝试用for循环的方式来简化代码

  • for循环的第一个表达式只会执行一次,让str=strtok第一次查找的返回值
  • 如果该返回值为空(没找到更多的分隔符),停止循环
  • 如果该返回值不为空,就让​​str=strtok(NULL, p)​​,继续查找并打印下一部分
int main(){ const char* p = "@."; char arr[] = "zpengwei@yeah.net"; char buf[50] = { 0 };//"zpengwei@yeah.net"  strcpy(buf, arr); char* str = NULL; for (str = strtok(buf, p); str != NULL; str=strtok(NULL, p))  {   printf("%s\n", str);  }    return 0;}

【C语言】常用的字符串函数和内存函数_开发语言_09

1.8 strerror

char * strerror ( int errnum );


C语言中规定了一部分错误码,这些错误码有他们对应的错误信息


这个函数的作用比较特殊:将错误代码翻译成提示信息

【C语言】常用的字符串函数和内存函数_字符串_10

errno是C语言提供的一个全局变量,可以直接使用,放在errno.h文件中

当库函数使用发生错误时,会把errno这个全局的错误变量设置为本次执行库函数产生的错误码

这时候可以用strerror函数将errno错误码翻译成错误信息

#include <errno.h>//需要调用errno.h头文件printf("%s\n", strerror(errno));

【C语言】常用的字符串函数和内存函数_字符串_11

2.内存函数

2.1 memcpy

这个函数的作用也是拷贝内容,和strcpy不同,memcpy可以拷贝任意类型

void * memcpy ( void * destination, const void * source, size_t num );
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
  • 该函数在遇到 ‘\0’ 的时候并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的

使用方法如下

【C语言】常用的字符串函数和内存函数_数据_12

模拟实现

在之前的​​qsort快速排序函数​​​的模拟实现里面,我们接触到了​​void*​​​指针,以及用​​char*​​指针来进行单个字节访问的模拟方法

在这里我们使用​​void*​​指针进行数据的拷贝

  • 要注意的是我们不能直接对void*指针进行++,而要将其强制类型转换成char*指针后+1
1.memcpy模拟实现void* my_memcpy(void* dest, const void* sour, int num){ assert(dest && sour); void* ptr = dest; while (num--) {   *(char*)dest = *(char*)sour;    dest = (char*)dest + 1;   sour = (char*)sour + 1; } return ptr;}int main(){ int arr1[10] = { 1,2,4,5,9,7,8 }; int arr2[10] = { 0 }; my_memcpy(arr2, arr1, 5 * sizeof(int)); for (int i = 0; i < 5; i++) {   printf("%d ", arr2[i]); } return 0;}

2.2 memmove

void * memmove ( void * destination, const void * source, size_t num );

这个函数和memcpy的功能基本一致,只有一点不同

  • memmove在拷贝的时候,源地址和目的地可以重叠

如图所示,我们可以将arr1数组的一部分拷贝回该数组里面

【C语言】常用的字符串函数和内存函数_字符串_13

但如果你测试一下,就会发现vs编译器下memcpy也是能够拷贝内存重叠的数据的

  • C语言并没有对memcpy函数做出如下要求,部分编译器的memcpy可能就不支持这样操作
  • 为了避免出错,我们在拷贝内存重叠数据的时候最好使用memmove函数

【C语言】常用的字符串函数和内存函数_开发语言_14

模拟实现

在编写该函数的时候,我们需要注意拷贝的顺序

如果重叠部分还是​从前向后​拷贝的时候,就会出现后面的内容被前面拷贝来的数据篡改,结果不符合要求的情况

【C语言】常用的字符串函数和内存函数_后端_15

  • 如果我们的目的地在源地址的​后面​,就应该​从后向前​拷贝,避免数据被改写

  • 如果我们的目的地在源地址的​前面​,就应该​从前向后​拷贝

  • 这里的前/后都是指有重叠的情况,如果没有重叠,从前往后/从后往前都不影响

【C语言】常用的字符串函数和内存函数_c语言_16

最终的函数模拟如下

void* my_memmove(void* dest, const void* sour, int num)
{
assert(dest && sour);
void* ptr = dest;
if (dest < sour)
{
while (num--)
{
*(char*)dest = *(char*)sour;
dest = (char*)dest + 1;
sour = (char*)sour + 1;
}
}
else
{
while (num--)
{
*((char*)dest+num) = *((char*)sour+num);
}
}

return ptr;
}

int main()
{
int arr1[10] = { 1,2,4,5,9,7,8,3,0,6};
//1 2 4 1 2 4 5 9 0 6
my_memmove(&arr1[3], arr1, 5 * sizeof(int));

for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}

return 0;
}

2.3 memcmp

int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );

这个函数的作用是:以字节为单位进行比较

【C语言】常用的字符串函数和内存函数_字符串_17

模拟实现

int my_memcmp(const void* ptr1, const void* ptr2, int num)
{
assert(ptr1 && ptr2);
while (num--)
{
if (*(char*)ptr1 == *(char*)ptr2)
{
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
else
{
return *(char*)ptr1 - *(char*)ptr2;
}
}
return 0;
}

int main()
{
int arr1[5] = { 1,3,5,6,8 };
int arr2[5] = { 2,4,7,9,0 };
int arr3[5] = { 1,3,4,7,9 };

int ret1 = my_memcmp(arr1, arr3, 9);
int ret2 = memcmp(arr1, arr3, 9);
printf("my_memcmp:%d\n", ret1);
printf("memcmp:%d\n", ret2);
return 0;
}

运行结果

【C语言】常用的字符串函数和内存函数_后端_18

2.4 memset

void * memset ( void * ptr, int value, size_t num );

这个函数的作用是把内存中​​ptr​​​所在位置的num个字节的内容改为​​value​

【C语言】常用的字符串函数和内存函数_开发语言_19

示例代码如下图所示

【C语言】常用的字符串函数和内存函数_开发语言_20

结语????

今天的内容有点小多,这些函数以后我们就会经常接触啦~

熟能生巧!



举报

相关推荐

0 条评论