C语言——动态内存管理、C语言的内存布局规律、高级宏定义

目录

一、动态内存管理

1.malloc

2.free

3.内存泄漏

4.calloc

5.realloc

二、C语言的内存布局规律

三、高级宏定义 

1.不带参数的宏定义

2.带参数的宏定义


一、动态内存管理

  • malloc:申请动态内存空间
  • free:释放动态内存空间
  • calloc:中请并初始化一条列内存空间
  • realloc:重新分配内存空间

1.malloc

  • 函数原型:void *malloc (size_t size);
  • malloc函数向条统申请分配 size 个字节的内存空间,并返回一个指向这块空间的指针。
  • 如果函数调用成功,返回一个指向申请的内存空间的指针,由于返回类型是 void 指针(void*),所以它可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size参数设置为0,返回值也可能是NULL,但这并不意味着函数调用失败。

代码举例:

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

int main()
{
	int *ptr;
	
	ptr = (int *)malloc(sizeof(int));
	
	if(ptr == NULL)
	{
		printf("分配内存失败!\n");
		exit(1);
	}
	
	printf("请输入一个整数:");
	scanf("%d",ptr);
	
	printf("你输入的整数是:%d\n",*ptr);
	return 0;
}

运行结果:

  • 初始化内存空间:以mem开头的函数被编入字符串标准库,函数的声明包含在string.h这个头文件中:(memset;-使用一个帝量字节填充内存空间;memcpy:拷贝内存空间;memmove:拷贝内存空间;memcmp:比较内存空间;memchr:在内存空间中搜秦一个字符。)

代码举例:

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

#define N 10

int main(void)
{
	int *ptr = NULL;
	int i;
	
	ptr = (int *)malloc(N * sizeof(int));
	if(ptr == NULL)
	{
		exit(1);
	}
	
	memset(ptr, 0, N * sizeof(int));
	for(i = 0;i < N;i++)
	{
		printf("%d ",ptr[i]);
	}
	putchar('\n');
	
	free(ptr);
}

运行结果:

2.free

  • 函数原型:void free (void *ptr);
  • free函数释放ptr参数指向的内存空间。该内存空间必须是由malloc、calloc或realloc函教申请的。否则,该函数将导致未定义行为。如果ptr参数是NULL,则不执行任何操作。注意:该函数并不会修改ptr参数的值,所以调用后它仍然指向原来的地方(变为非法空间)。

3.内存泄漏

  • 隐式内存泄漏(即用完内存块没有及时使用free函数释放)
  • 丢失内存块地址

4.calloc

  • 函数原型:void *calloc (size_t nmemb,size_t size);
  • calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间(即申请的总空间尺寸为nmemb *size),这些内存空间全部被初始化为0。
  • calloc函数与malloc函数的一个重要区别是:calloc函数在申请完内存后,自动初始化该内存空间为零;malloc函数不进行初始化操作,里边数据是随机的。
  • 下面两种写法是等价的:

 代码举例:

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

int main(void)
{
	int *ptr1 = NULL;
	int *ptr2 = NULL;
	
	//第一次申请的内存空间
	ptr1 = (int *)malloc(10 * sizeof(int));
	
	//进行若干操作之后发现ptr1申请的内存空间竟然不够用!
	
	//第二次申请的内存空间
	ptr2 = (int *)malloc(20 * sizeof(int));
	
	//将ptr1的数据拷贝到ptr2中
	memcpy(ptr2,ptr1,10);
	free(ptr1);
	
	//对ptr2申请的内存空间进行若干操作…
	
	free(ptr2);
	
	return 0;
	 
}

5.realloc

  • 函数原型:void *realloc(void *ptr,size_t size);
  • realloc函数修改ptr指向的内存空间大小为size字节;
  • 如果新分配的内存空间比原来的大,则旧内存块的数据不会发生改变;如果新的内存空间大小小于旧的内存空间,可能会导致数据丢失,慎用!
  • 该函数将移动内存空间的数据并返回新的指针;
  • 如果ptr参数为NULL,那么调用该函数就相当于调用malloc(size);
  • 如果size参数为0,并且ptr参数不为NULL,那么调用该函数就相当于调用free(ptr);
  • 除非ptr参数为NULL,否则ptr的值必须由先前调用malloc、calloc或realloc函数返回。

代码举例:

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

int main(void)
{
	int i,num;
	int count = 0;
	int *ptr = NULL;//这里必须初始化为NULL 
	
	do
	{
		printf("请输入一个整数(输入-1表示结束):");
		scanf("%d",&num);
		count++;
		
		ptr = (int *)realloc(ptr,count * sizeof(int)); 
		if(ptr == NULL)
		{
			exit(1);
		}
		
		ptr[count-1] = num;
	}while(num != -1);
	
	printf("输入的整数分别是:");
	for(i = 0;i < count;i++)
	{
		printf("%d ",ptr[i]);
	}
	putchar('\n');
	
	free(ptr);
	
	return 0;
	
}

运行结果:

二、C语言的内存布局规律

 

  • 代码段(Text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
  • 数据段(Initialized data segment)通常用来存放已经初始化的全局变量和局部静态变量。
  • BSS段(Bss segment/Uninitialized data segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称,这个区段中的数据在程序运行前将被自动初始化为数字0。
  • 是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩展或缩小。当进程调用malloc等玉数分配内存时,新分配的内存就被动态添加到堆上;当利用free等函数释放内存时,被释放的内存从堆中被剔除。
  • 是函数执行的内存区域,通常和堆共享同一片区域。

堆和栈的区别:

(1)申请方式:

  • 堆由程序员手动申请
  • 栈由系统自动分配

(2)释放方式:

  • 堆由程序员手动释放
  • 栈由系统自动释放

(3)生存周期:

  • 堆的生存周期由动态申请到程序员主动释放为止,不同函数之间均可自由访问
  • 栈的生存周期由函数调用开始到函数返回时结束,函数之间的局部变量不能互相访问

(4)发展方向:

  • 堆和其它区段一样,都是从低地址向高地址发展
  • 栈则相反,是由高地址向低地址发展

代码举例:

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

int main(void)
{
	int *ptr1 = NULL;
	int *ptr2 = NULL;
	
	ptr1 = (int *)malloc(sizeof(int));
	ptr2 = (int *)malloc(sizeof(int));
	
	printf("stack: %p -> %p\n",&ptr1,&ptr2);
	printf("heap: %p -> %p\n",ptr1,ptr2);
	
	return 0;
	
}

运行结果:

三、高级宏定义 

1.不带参数的宏定义

  • 为了和普通的变量进行区分,宏的名字通常我们约定是全部由大写字母组成
  • 宏定义只是简单地进行替换,并且由于预处理是在编译之前进行,而编译工作的任务之一就是语法检查,所以编译器不会对宏定义进行语法检查
  • 宏定义不是说明或语句,在末尾不必加分号
  • 宏定义的作用域是从定义的位置开始到整个程序结束
  • 可以用 #undef 来终止宏定义的作用域
  • 宏定义允许嵌套

代码举例:

#include <stdio.h>

#define R 6371
#define PI 3.14
#define V PI * R * R * R * 4/3

int main(void)
{
	int r;
	float s;
	
	printf("请输入圆的半径:");
	scanf("%d",&r);

	s = PI * r * r;
	printf("圆的面积是:%.2f\n",s);
	
	printf("地球的体积大概是:%.2f\n",V);
	
	return 0;
}

运行结果:

2.带参数的宏定义

代码举例:

#include <stdio.h>

#define MAX(x,y)(((x) > (y)) ? (x) : (y))

int main(void)
{
	int x,y;
	
	printf("请输入两个整数:");
	scanf("%d%d",&x,&y);
	printf("%d 是较大的那个数!\n",MAX(x,y));
	
	return 0;
	
}

运行结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值