C语言动态内存分配和内存操作函数使用详解
编程远泊 人气:01 动态内存分配的介绍
- 手动分配空间手动释放空间,根据自己的需要分配固定大小的内存空间。
- 动态内存分配在堆区分配的空间,堆区空间需要手动分配,手动释放。
- 分配堆区的函数使用malloc函数,释放堆区空间使用free函数。
- 如果堆区空间没有手动释放,当进程结束,系统会回收堆区的空间,一般都是手动释放
2 malloc和free函数
#include <stdlib.h>
malloc函数 ----> 在堆区分配空间
void *malloc(size_t size);
功能:手动在堆区分配内存空间
参数:
@ size : 分配堆区空间的大小,以字节为单位
返回值:
成功:返回分配堆区空间的首地址
失败:返回NULLfree函数 ----> 释放堆区的空间
void free(void *ptr);
功能:手动释放堆区的空间
参数:
@ ptr : 释放堆区空间的首地址
返回值:无
3 测试代码
#include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv[]) { /*your code*/ #if 0 // 回顾:定义指针变量的初始化方式 int *p = NULL; // 指针变量p在栈区分配的空间 int a = 100; // 变量a在栈区分配的空间 p = &a; // 指针p指向栈区空间 char *str = "hello world"; // 指针变量str在栈区分配的空间 // "hello world" 在字符串的常量区 #endif // 定义一个指针变量,指向一个堆区的空间 int *m_p = NULL; m_p = (int *)malloc(sizeof(int)); // 对返回值进行判断 if(m_p == NULL) { printf("malloc memory failed!\n"); return -1; } printf("malloc memory successed!\n"); // 对m_p指针指向的堆区空间进行初始化 *m_p = 1000; printf("打印堆区空间中的值 : %d\n", *m_p); #if 0 // 释放堆区的空间 free(m_p); // 将m_p指向NULL,防止野指针的出现 m_p = NULL; #endif // 为什么必须释放完堆区空间指针让指针指向NULL,如果不指向有可能会出现野指针 printf("释放之前,m_p指针变量中存放的地址 = %p\n", m_p); // 释放堆区的空间, // free函数只是单纯的释放了堆区的空间,别人就可以再次使用这块堆区空间, // free函数并没有将m_p指针变量中存放的地址清空,因此需要成需要手动将m_p指向NULL free(m_p); printf("释放之后,m_p指针变量中存放的地址 = %p\n", m_p); // 释放堆区空间没有将m_p指向NULL,此时依然可以对m_p指向的空间赋值, // 并且编译不会报错,也可以正常指向,但是这样就访问了非法的空间。 *m_p = 2000; printf("打印释放堆区空间之后m_p指向的空间的值 : %d\n", *m_p); // 如果将m_p指向null之后,就可以预防野指针的出现, // 当执行程序时就会报段错误 m_p = NULL; *m_p = 3000; printf("打印释放堆区空间之后m_p指向的空间的值 : %d\n", *m_p);//段错误 return 0; }
练习题:使用malloc在堆区给int *p; 分配(sizeof(int) * 10),大小的空间,通过终端输入的方式对堆区进行初始化,然后使用冒泡排序的方式对堆区空间中的成员进行排序。使用多文件编程的方式实现。
bubbling.h文件:
#ifndef __BUBBLING_H__ #define __BUBBLING_H__ #include <stdio.h> #include <stdlib.h> int *malloc_int(int len); void print(int *p,int len); void bubbling(int *p,int len); void free_p(int *p); #endif
main.c文件:
#include "bubbling.h" int main(int argc, const char *argv[]) { printf("请输入要输入元素的个数>"); int n=0; scanf("%d",&n); int *p=malloc_int(n); printf("请输入要排序的元素>\n"); for(int i=0;i<n;i++){ scanf("%d",p+i); } printf("未排序前>\n"); print(p,n); bubbling(p,n); printf("排序后>\n"); print(p,n); free_p(p); p=NULL; return 0; }
malloc_free.c文件:
#include "bubbling.h" int *malloc_int(int len){ int *q=(int *)malloc(sizeof(int)*len); if(NULL==q){ printf("申请空间失败!\n"); } return q; } void free_p(int *p){ if(NULL==p){ printf("传参错误!\n"); } free(p); p=NULL; }
bubbling.c文件:
#include "bubbling.h" void bubbling(int *p,int len){ if(NULL==p){ printf("传参错误!\n"); } for(int j = 0; j < len-1; j++){ //内层循环控制一趟排序 for(int i = 0; i < len-1-j; i++){ //此处的 -1 是防止越界访问的 //此处的 -j 是因为每趟都可以少比较一个元素 if(p[i] > p[i+1]){//如果是降序 只需要将此处的 > 改成 < 即可 //交换 int temp = p[i]; p[i] = p[i+1]; p[i+1] = temp; } } } }
print.c文件:
#include "bubbling.h" void print(int *p,int len){ if(NULL==p){ printf("传参错误!\n"); } for(int i=0;i<len;i++){ printf("%4d",*(p+i)); } puts(""); }
4 goto的使用场合
常用于出错处理
#include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv[]) { /*your code*/ // 1. 定义3个指针类型的变量,最终都指向一个堆区的空间 int *i_p = NULL; short *s_p = NULL; char *c_p = NULL; i_p = (int *)malloc(sizeof(int)); if ( i_p == NULL) { printf("i_p malloc memory failed!\n"); // 如果失败直接退出,没有问题 goto ERR1; } s_p = (short *)malloc(sizeof(short)); if ( s_p == NULL) { printf("s_p malloc memory failed!\n"); // 如果失败需要先将i_p指向的堆区空间释放 goto ERR2; } c_p = (char *)malloc(sizeof(char)); if ( c_p == NULL) { printf("c_p malloc memory failed!\n"); // 如果失败需要先将i_p和s_p指向的堆区空间释放 goto ERR3; } // 如果都分配成功,不使用时手动释放, // 如果不手动释放,当进行结束之后,系统也会进行回收。 free(c_p); free(s_p); free(i_p); return 0; ERR3: free(s_p); ERR2: free(i_p); ERR1: return -1; }
宏定义函数版:
#include <stdio.h> #include <stdlib.h> #define NODE(T) T *T##_p = NULL; #define MALLOC(T) T##_p=(T *)malloc(sizeof(T)); int main(int argc, const char *argv[]) { /*your code*/ // 1. 定义3个指针类型的变量,最终都指向一个堆区的空间 // int *int_p = NULL; NODE(int) // short *s_p = NULL; NODE(short) // char *c_p = NULL; NODE(char) // i_p = (int *)malloc(sizeof(int)); MALLOC(int) if ( int_p == NULL) { printf("i_p malloc memory failed!\n"); // 如果失败直接退出,没有问题 goto ERR1; } // short_p = (short *)malloc(sizeof(short)); MALLOC(short) if ( short_p == NULL) { printf("s_p malloc memory failed!\n"); // 如果失败需要先将i_p指向的堆区空间释放 goto ERR2; } // char_p = (char *)malloc(sizeof(char)); MALLOC(char) if ( char_p == NULL) { printf("c_p malloc memory failed!\n"); // 如果失败需要先将i_p和s_p指向的堆区空间释放 goto ERR3; } // 如果都分配成功,不使用时手动释放, // 如果不手动释放,当进行结束之后,系统也会进行回收。 free(char_p); free(short_p); free(int_p); return 0; ERR3: free(short_p); ERR2: free(int_p); ERR1: return -1; }
5 memset()
#include <string.h> void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
s:需要操作内存s的首地址
c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
n:指定需要设置的大小
返回值:s的首地址
6 memcpy()
#include <string.h> void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
dest:目的内存首地址
src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错
n:需要拷贝的字节数
返回值:dest的首地址
7 memcmp()
#include <string.h> int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2所指向内存区域的前n个字节
参数:
s1:内存首地址1
s2:内存首地址2
n:需比较的前n个字节
返回值:
相等:=0
大于:>0
小于:<0
加载全部内容