首先给出大佬的链接:https://zhuanlan.zhihu.com/p/272920885
在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序 A 并不能访问应用程序 B,当然一些特殊技巧可以访问,但此文并不详细进行说明。例如在计算机中,一个视频播放程序与一个浏览器程序,它们的内存并不能访问,每个程序所拥有的内存是分区进行管理的。在计算机系统中,运行程序 A 将会在内存中开辟程序 A 的内存区域 1,运行程序 B 将会在内存中开辟程序 B 的内存区域 2,内存区域 1 与内存区域 2 之间逻辑分隔。
在程序A开辟的内存区域1会被分为几个区域,这就是内存四区。内存四区分为栈区、堆区、数据区与代码区。
栈区
堆区
数据区
代码区
首先看个实例:
#include
int e = 0;
int main(){
int a = 0;
int b = 0;
char c = '0';
static int d = 0;
printf("a: %d \n", &a);
printf("b: %d \n", &b);
printf("c: %d \n", &c);
printf("d: %d \n", &d);
printf("e: %d \n", &e);
}
运行结果:
a: 6422300
b: 6422296
c: 6422295
d: 4227108
e: 4227104
我们可以观察到变量 a 的地址是 6422300 变量 b 的地址是 6422296,由于 int 的数据大小为 4 所以两者之间间隔为 4;再查看变量 c,我们发现变量 c 的地址为 6422295,与变量 b 的地址 6422296 间隔 1,因为 c 的数据类型为 char,类型大小为 1。在此我们观察发现,明明我创建变量的时候顺序是 a 到 b 再到 c,为什么它们之间的地址不是增加而是减少呢?
那是因为栈区的一种数据存储结构为先进后出。可以想象有一个桶,往里面装大米,然后再拿出来,先进去的大米总是后面才能拿出来。首先栈的顶部为地址的“最小”索引,随后往下依次增大,但是由于堆栈的特殊存储结构,我们将变量 a 先进行存储,那么它的一个索引地址将会是最大的,随后依次减少;第二次存储的值是 b,该值的地址索引比 a 小,由于 int 的数据大小为 4,所以在 a 地址为 6422300 的基础上往上减少 4 为 6422296,在存储 c 的时候为 char,大小为 1,则地址为 6422295。由于 a、b、c 三个变量同属于一个栈内,所以它们地址的索引是连续性的
通过上述内容得知,全局变量与静态变量都应该存储在静态区,创建了一个变量 d,变量 d 为静态变量,运行代码后从结果上得知,静态变量 d 的地址与一般变量 a、b、c 的地址并不存在连续,他们两个的内存地址是分开的,而全局变量e则和d处在同一块,都在数据区。
从以上运行结果中证实了上述内容的真实性,并且也得到了一个知识点,栈区、数据区都是使用栈结构对数据进行存储。在以上内容中还说明了一点栈的特性,就是容量具有固定大小,超过最大容量将会造成栈溢出。
查看如下代码:
#include
int main(){
char arr_char[1024*1000000];
arr_char[0] = '0';
}
以上代码定义了一个字符数组arr_char,并设置了大小为1024*1000000,设置该数据是方便查看大小;随后在数组头部进行赋值。这是程序运行出错,原因是造成了栈的溢出。在平常开发中若需要大容量的内存,需要使用堆。堆并没有栈一样的结构,也没有栈一样的先进后出。需要人为的对内存进行分配使用。
代码如下:
#include
#include
#include
int main(){
char *p1 = (char *)malloc(1024*1000000); // 使用malloc手动开辟空间
strcpy(p1, "这里是堆区"); // 往内存空间p1中传数据“这里是堆区”
printf("%s \n", p1);
}
在 C 语言(不是 C++)中,malloc 和 free 是系统提供的函数,成对使用,用于从堆中分配和释放内存。malloc 的全称是 memory allocation 译为“动态内存分配”。
malloc和free的使用:
在开辟堆空间时我们使用的函数为malloc,malloc在C语言中是用于申请内存空间,malloc函数的原型如下:void *malloc(size_t size);在malloc函数中,size是表示需要申请的内存空间大小,申请成功将会返回该内存空间的地址,申请失败则会返回NULL,并且申请成功也不会自动进行初始化。该函数的返回值说明为 void *,在这里 void * 并不指代某一种特定的类型,而是说明该类型不确定,通过接收的指针变量从而进行类型的转换。在分配内存时需要注意,即时在程序关闭时系统会自动回收该手动申请的内存 ,但也要进行手动的释放,保证内存能够在不需要时返回至堆空间,使内存能够合理的分配使用。释放空间使用free函数,函数原型如下:void free(void *ptr);
free函数的返回值为void,没有返回值,接收的参数为使用malloc分配的内存空间指针。
重新调整内存的大小和释放内存:
当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议在不需要使用内存时,都应该调用函数free()来释放内存。或者通过调用函数realloc()来增加或减少已分配的内存块的大小。
使用realloc()和free()函数来看看下面的实例:
#include
#include
#include
int main(){
char name[100];
char *description;
strcpy(name, "Zara Ali");
// 动态分配内存
description = malloc(30 * sizeof(char));
if(description == NULL){
fprintf(stderr, "Error - unable to allocate required memory \n");
}else{
strcpy(description, "Zara ali a DPS student.");
}
// 假设您想要存储更大的描述信息
description = realloc(description, 100 * sizeof(char));
if(description == NULL){
fprintf(stderr, "Error - unable to allocate required memory \n");
}else{
strcat(description, "she is in class 10th");
}
printf("Name = %s \n", name);
printf("Description: %s \n", description);
// 使用free()函数释放内存
free(description);
}
结果:
┌──(rootkali)-[~/Desktop/c_test]
└─# ./realloc_free
Name = Zara Ali
Description: Zara ali a DPS student.she is in class 10th
执行程序时,可以从命令行传值给C程序,这些值被称为命令行参数。
命令行参数是使用main()函数参数来处理的,其中,argc是指传入参数的个数,argv[]是一个指针数组,指向传递给程序的每个参数。
实例:
#include
int main(int argc, char *argv[]){
if(argc == 2){
printf("The argument supplied is %s \n", argv[1]);
}else if (argc > 2){
printf("Too many arguments supplied. \n");
}else{
printf("One argument expected. \n");
}
printf("argv[0]: %s \n", argv[0]);
printf("argv[1]: %s \n", argv[1]);
}
结果:
┌──(rootkali)-[~/Desktop/c_test]
└─# ./minglinghangcanshu testing
The argument supplied is testing
argv[0]: ./minglinghangcanshu
argv[1]: testing
应当指出的是,argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针,*argv[n] 是最后一个参数。如果没有提供任何参数,argc 将为 1,否则,如果传递了一个参数,argc 将被设置为 2。多个命令行参数之间用空格分隔,但是如果参数本身带有空格,那么传递参数的时候应把参数放置在双引号 “” 或单引号 ‘’ 内部。