首页 > 系统相关 >C语言-第七章:字符和字符串函数、动态内存分配

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

时间:2024-09-06 18:22:28浏览次数:11  
标签:ch 函数 int C语言 第七章 动态内存 字符串 include ptr

传送门: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语言-第八章:指针进阶

标签:ch,函数,int,C语言,第七章,动态内存,字符串,include,ptr
From: https://blog.csdn.net/2303_78095330/article/details/141868936

相关文章

  • C语言-第六章-加餐:其他自定义类型
    传送门:C语言-第六章:结构体目录第一节:位段    1-1.位段是什么    1-2.位段的大小第二节:联合体    2-1.联合体是什么    2-2联合体的大小第三节:枚举类型    3-1.枚举是什么第四节:结构体中的柔性数组    4-1.柔性数组......
  • C语言面向对象
    我们在编写程序时,通常采用以下步骤:将问题的解法分解成若干步骤使用函数分别实现这些步骤依次调用这些函数这种编程风格的被称作面向过程。除了面向过程之外,还有一种被称作面向对象的编程风格被广泛使用。面向对象采用基于对象的概念建立模型,对现实世界进行模拟,从而完......
  • C语言学习——sprintf函数详细解释及其用法
    文章目录函数功能:把格式化的数据写入某个字符串参数说明及应用举例解释:连接字符串打印地址信息利用sprintf的返回值使用sprintf的常见问题函数功能:把格式化的数据写入某个字符串头文件:stdio.h函数原型:intsprintf(char*buffer,constchar*format,[arg......
  • 学习C语言结构体(结构体的前世今生)
    1、首先我将使用DevC++这个软件(其实随意一个C++软件都可以)来演示一下结构体的使用方法。这里已经写了一个最简单的HelloWorld!程序。2、对于C语言的数据来说最重要的就是两个功能,一个是定义数据,一个是引用数据。既然结构体也是数据类型,那么他就和其他的数据类型差不多。也分......
  • 20240906_142048 c语言 认识c语言
    C语言是一种广泛使用的编程语言,它以其高效、灵活和接近硬件的特性而闻名。对于零基础的学生来说,学习C语言是一个很好的起点,因为它不仅能帮助你理解计算机程序的基本结构和概念,还能为学习更高级的编程语言(如C++、Java、Python等)打下坚实的基础。下面我将简要介绍C语言的一些基本概念......
  • C语言之动态内存分配与释放
    C语言之动态内存分配与释放通用指针类型void通用类型指针具有以下特点:类型无关,赋值灵活:由于指针本质上是一个存储内存地址的变量,而内存地址是没有类型的,所以void指针可以存储任意类型数据的地址,指向任意类型对象。无论是整数、浮点数、字符或数组、结构体等类型都可以用void指......
  • 新手c语言讲解及题目分享(十九)--数据类型专项练习
    本文主要讲解c语言的基础部分,常见的c语言基础数据类型,这个也非常重要。参考书目和推荐学习书目:通过网盘分享的文件:C语言程序设计电子教材(1).pdf链接:https://pan.baidu.com/s/1JFqSaCKZ0A2Lr944e72NUA?pwd=p648提取码:p648目录前言一.常量与变量1.常量2.变量二.......
  • 新手c语言讲解及题目分享(十八)--基本输入输出函数专项练习
    本文主要讲解c语言的基础部分,基本的输入与输出,通过手动的输入从而得到自己想要的预期值。参考书目和推荐学习书目:通过网盘分享的文件:C语言程序设计电子教材(1).pdf链接:https://pan.baidu.com/s/1JFqSaCKZ0A2Lr944e72NUA?pwd=p648提取码:p648目录前言一.格式输出......
  • C语言 10 数组
    简单来说,数组就是存放数据的一个组,所有的数据都统一存放在这一个组中,一个数组可以同时存放多个数据。一维数组比如现在想保存12个月的天数,那么只需要创建一个int类型的数组就可以了,它可以保存很多个int类型的数据,这些保存在数组中的数据,称为元素://12个月的数据全部保存......
  • C语言习题--程序改错
     1.待修改代码#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){ char*src="hello,world"; char*dest=NULL; intlen=strlen(src); dest=(char*)malloc(len); ch......