传统数组的缺点
1.数组的长度必须事先指定,且必须是常整数,不能是变量。
例子:
int a[5]; //OK
int len = 5; int a[len]; //Error
2.传统形式定义的数组,该数组的内存程序员无法释放
在一个函数运行期间,系统为该函数中数组分配的空间会一直存在,直到该函数运行结束,数组的空间才会被释放。
3.数组的长度一旦定义,其长度就不能在改变
数组的长度不能在函数运行的过程中动态的扩充或缩小。
4.A函数定义的数组,在A函数运行期间可以被其他函数调用,A函数终止后不能被其他函数所调用。
传统方式定义的数组不能跨函数使用。
malloc函数的简单使用
使用前需要加载头文件 #include<stdlib.h>
#include<stdio.h>
#include<malloc.h>
/*
malloc是memory(内存)allocate(分配)的缩写
*/
int main(){
int i = 5; // 分配了四个字节,静态分配
int * p = (int *)malloc(4);
/*
1.要使用malloc函数,必须添加malloc.h这个头文件
2.malloc函数只有一个形参,并且形参是整形
3.形参的整形,表示请求系统为本程序分配4个字节
4.malloc函数只能返回第一个字节的地址
5.11行分配了12个字节,p占8个字节,p所指向的内存占了4个字节
6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的
*/
*p = 5; // *p代表的就是一个int变量,只不过*p 这个整型变量的内存分配和第10行的方式不同
free(p); // 表示把p所指向的内存给释放掉,p本身只能在所在函数运行结束时由系统释放
return 0;
}
这行代码需要进行深入理解。
动态内存分配举例——动态数组的构造
静态数组的分配
int a [5]; // 如果int占4个字节,则本数组共包含20个字节,每4个字节被当作一个int变量来使用
#include<stdio.h>
#include <malloc.h>
int main(){
int a[5]; // 如果int占4个字节,则本数组共包含20个字节,每4个字节被当作一个int变量来使用
int len;
int * pArr;
printf("请输入要存放的元素的个数:");
scanf("%d", & len);
pArr = (int *)malloc(sizeof(int) * 5);
// 上一行动态构造了一个一维数组,该一维数组的长度是len, 类似于 int pArr[len];
// pArr[1] = 12;
// printf("%d", *(pArr+1));
for(int i = 0; i < len; i++){
scanf("%d", pArr+i);
}
// 读取元素,和上面的for循环代码一样
// for(int i = 0; i < len; i++){
// scanf("%d", &pArr[i]);
// }
for(int i = 0; i < len; i++){
printf("%d ", *(pArr+i));
}
free(pArr); // 释放动态分配的内存
return 0;
}
realloc函数的简单使用
前面介绍静态数组不能改变长度,而动态数组可以调整数组的长度,当需要更多的空间时可以进行扩充,当数组长度过长时可以进行减小。在C语言中通过realloc函数实实现对重新调整动态分配内存的大小。它的原型如下:
void* realloc(void* ptr, size_t size);
// 第一个参数为指向原内存块的指针,第二个重新调整的大小size
需要注意的是,realloc
函数会尽可能地在原内存块上直接进行扩展或缩小,如果原内存块的大小不够,无法直接进行扩展,那么realloc
会自动寻找一个新的内存块来满足需求。如果返回的指针与原指针不同,说明内存块的位置发生了改变。在使用realloc
重新分配内存后,不要忘记释放原内存块,以避免内存泄漏。
以下是简单的代码实例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* nums = (int*)malloc(5 * sizeof(int)); // 分配一个初始大小的内存块
if (nums == NULL) {
printf("内存分配失败\n");
return 1;
}
// 使用分配的内存块
// 假设我们需要将内存块扩展到10个整数大小
int* resized_nums = (int*)realloc(nums, 10 * sizeof(int));
if (resized_nums == NULL) {
printf("内存重新分配失败\n");
free(nums); // 释放原内存块
return 1;
}
// 使用重新分配的内存块
free(resized_nums); // 释放重新分配的内存块
return 0;
}
静态内存和动态内存的比较
静态内存
- 静态内存由系统分配,由系统释放
- 静态内存由栈分配
动态内存
- 动态内存是由程序员手动分配,手动释放
- 动态内存是在堆分配的
跨函数使用内存的问题
静态函数不能跨函数使用,下面是一个案例,程序虽然没有报错,但是不能这么使用。
#include <stdio.h>
void f(int ** q){ // q也是一个指针变量,无论q是什么类型的指针变量,都只占有4个字节
int i = 5;
// *p等价于p
// *p = i; // error 因为 *q = i等价于 p = i;
*q = &i; // 让p指向i
}
int main(){
int * p; // p是一个指针变量
f(&p);
printf("%d", *p); // 本语句没有错误,但是逻辑上有问题
/* f终止后,其内的所有内存都会被释放,不能跨函数使用
这里没有编译器没有报错,这也是指针的难点。
*/
return 0;
}
动态内存分配可以实现跨函数使用
#include <stdio.h>
#include <malloc.h>
void f(int ** q){
*q = (int *)malloc(sizeof(int)); // 等价于 p = (int *)malloc(sizeof(int));
// 上面一行当函数运行结束后内存没有被释放
**q = 5;
}
int main(){
int * p; // p是一个指针变量
f(&p);
printf("%d", *p);
return 0;
}
数组的定义(数据结构中)
数组的静态分配
typedef struct{
ElemType data[Maxsize];int length;
}SqList; //顺序表类型
数组的动态分配
typedef struct{
ElemType *data;int length;
}SqList; //顺序表类型Sqlist L;
L.data = (ElemType *)malloc(sizeof (ElemType) * MaxSize);