C语言-第七章:字符和字符串函数、动态内存分配

传送门:C语言-第六章-加餐:其他自定义类型

目录

第一节:字符和字符串函数

        1-1.strlen 函数和 sizeof 关键字

        1-2.memcpy 内存拷贝函数

        1-3.memmove 内存拷贝函数

        1-4.memset 内存设置函数

        1-5.strtok 字符串切割函数

        1-6.strerrno 和 perror 错误码函数

第二节:动态内存分配

        2-1.内存分配

        2-2.堆区函数

                2-2-1.malloc 函数

        2-2-2.calloc 函数

        2-2-3.realloc 函数

        2-2-4.free 函数

下期预告:


第一节:字符和字符串函数

        字符和字符串函数的头文件是 string.h,它们与字符串和内存有关。

        1-1.strlen 函数和 sizeof 关键字

        strlen 函数和 sizeof 关键字都可以计算字符串的长度,它们的区别是 strlen 函数不会计算字符串的结束标志 '\0' 而 sizoef 关键字会计算在内,请阅读以下代码:

#include <stdio.h>
#include <string.h>

int main()
{
	char ch[] = "Hello world";
	printf("strlen函数得到的长度:%u\n", strlen(ch));
	printf("sizeof函数得到的长度:%u\n", sizeof(ch));
    return 0;
}

        因为 strlen 和 sizoef 得到的都是一个无符号的整型,所以用%u接收更加恰当。

        streln 的原理:

        它的函数原型如下:

        strlen 的原理就是遍历字符串,只要遇到 '\0' 就结束,所以不管 '\0' 后是否还有数据,它都会结束,请阅读以下代码:

#include <stdio.h>
#include <string.h>

int main()
{
	char ch[] = "Hello world\0Hello world"; 
	printf("strlen函数得到的长度:%u\n", strlen(ch));
	return 0;
}

        它在遇到第一个 '\0' 就结束了,实际上只计算了 "Hello world" 的长度。

        sizeof 的原理:

        sizeof 是通过类型大小得出数值,比如 int 类型就是4,实际上它的数值与字符串的内容无关,请阅读以下代码:

#include <stdio.h>
#include <string.h>

int main()
{
	char ch_1[] = "Hello world";
	char ch_2[100] = "Hello world";
	printf("ch_1的大小:%u\n", strlen(ch_1));
	printf("ch_2的大小:%u\n", sizeof(ch_2));
	return 0;
}

        

        因为 ch_2 的类型是 char[100],大小是100个字节,所以 sizoef(ch_2) 的值是100;

        而 ch_1 虽然没有写大小,但是它会由初始化的字符串被推到成 char[11] ,所以 sizeof(ch_1)的值是11。

        1-2.memcpy 内存拷贝函数

        memcpy 函数的功能是将一个空间的任意大小的数据拷贝到另一个空间中,它的函数原型如下:

        其中destination就是目的地址的指针,source 就是源地址的指针,num就是拷贝的数据大小(单位:字节)。

        它的用法如下:

#include <stdio.h>
#include <string.h>

int main()
{
	char ch_1[] = "Hello world";
	char ch_2[100];
	memcpy(ch_2,ch_1,sizeof(ch_1));
	printf("ch_2:%s",ch_2);
	return 0;
}

        此时 ch_1 的内容就被 ch_2 拷贝了一份,注意这里使用 sizoef 而不用 strlen 是因为字符串的结束标志 '\0' 也要拷贝过去。

        1-3.memmove 内存拷贝函数

        memmove 函数的功能与 memcpy 函数相同,用法也相同,函数原型如下:

         ​​​

        memmove 与 memcpy 在语义上也不同,一个是移动,一个是拷贝;

        memmove 与 memcpy 的语义还体现在对重叠内存的处理,这个区别会在下一次加餐中具体阐明。

        1-4.memset 内存设置函数

        memset 函数可以让一块空间存储自定义的字符,它的函数原型如下:

        ptr 就是目标空间的指针,value 就是要存储的值,num 就是要设置的空间大小(单位:字节)

        它的用法如下:

#include <stdio.h>
#include <string.h>

int main()
{
	char str[10];
	memset(str, 'a', 9);
	str[9] = '\0';
	printf("%s\n", str);
	return 0;
}

        这样就成功将 9 个字节的空间赋值成 a 了。

        1-5.strtok 字符串切割函数

        strtok 函数可以对字符串按照给定的分隔符进行切割,每次调用strtok,它就会沿着上一次切割的位置继续切割,并且返回一个切割好的字符串,函数原型如下:

        str 就是目标字符串,delimiters 就是分隔符,如果没有分隔符了就会返回NULL。

        它的用法如下: 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	char str[] = "123#456#789";
	// 第一次调用时要传目标字符串
	char* ptr = strtok(str,"#");
	while (ptr!=NULL)
	{
		printf("%s\n", ptr);
		// 之后调用不传目标字符串,自动根据上次的位置剪切
		ptr = strtok(NULL, "#");
	}
	return 0;
}

        分隔符不仅可以传一个,还可以传多个:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	char str[] = "123#456#789&abc&def";
	// 第一次调用时要传目标字符串
	char* ptr = strtok(str,"#&");
	while (ptr!=NULL)
	{
		printf("%s\n", ptr);
		// 之后调用不传目标字符串,自动根据上次的位置剪切
		ptr = strtok(NULL, "#&");
	}
	return 0;
}

 

        1-6.strerrno 和 perror 错误码函数

        当使用库函数时,如果发生了错误会有一个名为 errno 的全局变量被设置,它的类型是 int ,不同的值代表不同的错误:

        strerror 函数的功能就是把错误码翻译成字符串,把错误码作为参数后,它会返回错误信息的地址,比如打开一个不存在的文件:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main()
{
	FILE* fd = fopen("text.txt","r");
	perror("打开文件失败");
	return 0;
}

 

        我们还可以用 perror 函数代替 printf("errno:%d ,errno info:%s\n",errno,strerror(errno)),

perror 函数就是 printf + strerror(errno):

        perror 使用起来更加的方便快捷

第二节:动态内存分配

        2-1.内存分配

        在C语言中有许多不同的值:变量、常量,它们都存储在内存中,但是在内存的不同分区中,具体的情况如下:

        除了堆区之外,其他区域都有系统自动创建和释放,我们可以调用一些函数来创建和释放堆区空间,这就是动态内存分配。

        2-2.堆区函数

        堆区函数的头文件是 stdlib.h。

                2-2-1.malloc 函数

        malloc 函数之前我们已经见过了,它可以在堆区开辟一块空间,函数原型如下:

        size 就是要开辟的空间的大小(单位:字节),然后返回这块空间的地址,它的使用方法如下:

#include <stdlib.h>

int main()
{
	int* ptr = (int*)malloc(4);
	return 0;
}

        因为它返回的指针是 void* 类型(无类型指针),所以要用(指针类型)把它强转成我们需要的指针类型。

        如果申请堆区空间失败,它会返回NULL,所以每次申请堆区空间后都判断一下是否成功,这是一个良好的代码习惯:

#include <stdlib.h>

int main()
{
	int* ptr = (int*)malloc(4);
	if (ptr == NULL)
	{
		perror("malloc fail");
		return 0;
	}
	return 0;
}

       为了代码看起来简单,后面的代码我就不判断了,但是自己写是一定要加上。

        2-2-2.calloc 函数

 calloc 函数也能申请堆区空间,函数原型如下:

        num 是申请的个数,size 是每个申请多少字节,总共申请 num*size 字节的空间,,如果申请空间失败就返回NULL,它的用法如下:

#include <stdlib.h>

int main()
{
	int* ptr2 = (int*)calloc(3,4);
	return 0;
}

        它与 malloc 除了传参上的不同外,calloc还会将申请空间全部置0:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* ptr1 = (int*)malloc(4);
	int* ptr2 = (int*)calloc(3,4);
	printf("%d\n", *ptr1);
	printf("%d\n", *ptr2);
	return 0;
}

        用调试窗口可以看得更清楚:

        2-2-3.realloc 函数

        realloc 也是申请堆区空间的函数,但是它一般不单独使用,它可以申请一块新空间后,又将数据拷贝到新空间中,函数原型如下:

        ptr 是旧空间的地址,realloc 用它拷贝数据,size 表示新空间的大小(单位:字节)

,如果申请空间失败就返回NULL,它的用法如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* ptr1 = (int*)malloc(4);
	ptr1[0] = 3; // 之前说过 ptr[0]的本质是 *(ptr+0)
	// 扩容
	int* ptr2 = (int*)realloc(ptr1, 8);
	// 释放旧空间,旧指针置空
	ptr1 = NULL;
	
	// 检查数据是否拷贝
	printf("%d\n", ptr2[0]);
	return 0;
}

        

        注意调用 realloc 函数时,它会自动释放旧空间(注意不要再调用 free 了)。

        2-2-4.free 函数

        free 函数用来释放堆区的空间,如果一块堆区空间已经无用了却不释放,这块空间就被浪费掉了,这叫做 内存泄漏。

        对于需要一直运行的程序(比如服务器进程),如果频繁出现内存泄漏,就是导致程序无堆区空间可用,导致进程退出。

        free 函数的原型如下:

        ptr 就是需要释放空间的地址,它的使用方法如下:

#include <stdlib.h>

int main()
{
	int* ptr1 = (int*)malloc(4);
	free(ptr1);
	return 0;
}

        有几点需要注意:

        (1)程序退出时,系统也会自动释放未释放的堆区空间

        (2)free 函数可以传NULL,但是不能传已经释放了空间的指针:

        (3)free 不能从中间分几次释放堆区空间(申请几次就释放几次,申请到哪个指针就释放哪个) :

下期预告:

        下一次是第八章指针的进阶学习,将介绍以下内容:

        字符指针、指针数组、数组指针、数值指针数组、函数指针等

传送门:C语言-第八章:指针进阶

  • 28
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值