C语言数组fun函数的内存空间,C语言进阶 ~ 内存四区(栈、堆、全局、代码区)...

本文详细介绍了C语言中的数据类型,包括数据类型的本质、别名、void的使用,以及变量的本质。讲解了程序内存的四区模型,包括全局区、栈区和堆区,分析了内存分配与释放的过程。同时,通过实例探讨了函数调用时的内存变化和指针操作。
摘要由CSDN通过智能技术生成

特别声明:该部分是根据B站大佬---什么都想干好的视频学习而来。程序员

目录数组

1.1 数据类型本质分析

1.1.1 数据类型概念

“类型”是对数据的抽象

类型相同的数据有相同的表示形式、存储格式以及相关的操做

程序中使用的全部数据都一定属于某一种数据类型

ccf368474bb1fe9d29dc9ad7e26ef09f.png

1.1.2 数据类型的本质

数据类型可理解为建立变量的模具:是固定内存大小的别名。

数据类型的做用:编译器预算对象(变量)分配的内存空间大小。

注意:数据类型只是模具,编译器并无分配空间,只有根据类型(模具)建立变量(实物),编译器才会分配空间。

#include

int main(void)

{

int a = 10; //告诉编译器,分配4个字节的内存

int b[10]; //告诉编译器,分配4*10 = 40 个字节的内存

printf("b:%p, b+1: %p, &b:%p, &b+1: %p\n", b, b + 1, &b, &b + 1);

//b+1 和 &b+1的结果不同 (+1 ---> +4; +1 ---> +40)

//是由于 b 和 &b 所表明的数据类型不同

//b 表明数组首元素的地址

//&b 表明总体数组的地址

return 0;

}

b+1 和 &b+1的结果不同 (+1 ---> +4; +1 ---> +40)

是由于 b 和 &b 所表明的数据类型不同

b  表明数组首元素的地址

&b 表明总体数组的地址

1.1.3 数据类型的别名

① 给数据类型起别名

#include

typedef unsigned int u32; //给unsigned int类型取别名

int main(void)

{

u32 a;

a = 10;

return 0;

}

② 给结构体类型起别名

#include

#define pi 3.14

// 正常使用结构体 //

struct People

{

char name[64];

int age;

};

给结构体类型起别名

typedef struct People_2

{

char name[64];

int age;

} people_t;

int main(void)

{

// 正常使用结构体 ///的初始化///

struct People p1;

给结构体类型起别名 /的初始化///

people_t p2;

p1.age = 10;

p2.age = 11;

return 0;

}

1.1.4 数据类型之 void

一、函数参数为空,定义函数时,能够用void修饰: int fun(void)

二、函数没有返回值: void fun(void);

三、不能定义vold类型的普通变量,vold a; //err,没法肯定类型,不一样类型分配空间不同

四、 能够定义vold *变量: void *; //ok, 32位是4字节,64位是8字节

五、数据类型本质:固定内存块大小别名

六、void和万能指针,函数返回值,函数参数

void的字面意思是“无类型”,void *则为“无类型指针”,void *能够指向任何类型的数据。

void * memcpy(void *dest, const void *src, size_t len);void指针的意义

七、void指针的意义

C语言规定只有相同类型的指针才能够相互赋值

void*指针做为左值用于“接收”任意类型的指针

void*指针做为右值赋值给其它指针时须要强制类型转换

int *p1 = NULL;

char *p2 = (char *)malloc(sizoeof(char)*20);

1.2 变量的本质分析

1.2.1 变量的概念

概念:既能读又能写的内存对象,称为变量。

#include

#include

int main(void)

{

int i = 0;

// 经过变量直接操做内存

i = 10;

int *p = &i;

printf("&i:%d\n", &i);

printf("p:%d\n", p);

// 经过内存编号间接操做内存

*p = 100;

printf("i = %d, *p = %d\n", i, *p);

system("pause");

return 0;

}

1.3 程序的内存四区模型

ba273cfe89d41b006f3d5a6093d819ab.png

d0e7d50f5531b66e0cd8cf72243aceb0.png

1.3.1 全局区(全局变量、静态变量(const,constant或final等)、文字常量区)

#include

char * getStr1()

{

char *p1 = "abcdefg2";

return p1;

}

char *getStr2()

{

char *p2 = "abcdefg2";

return p2;

}

int main(void)

{

char *p1 = NULL;

char *p2 = NULL;

p1 = getStr1();

p2 = getStr2();

//打印p1 p2 所指向内存空间的数据

printf("p1:%s , p2:%s \n", p1, p2);

//打印p1 p2 的值

printf("p1:%p , p2:%p \n", p1, p2);

return 0;

}

b60289c1e6fb036d0a39d3cdedc06d68.png

问题:内容一致, 为何两个指针的地址值也是同样的?

① 程序执行到   int main(void)

1089967235fc13da6de090a43f108ceb.png

② 程序执行到       char *p1 = NULL;  char *p2 = NULL;

72ec9631a44f12464eb2522e2e1db845.png

③ 程序执行到 getStr1()        因为我的缘由在全局区中应该为“abcdefg1\0”

82b4511029f84a0b4da267155f956159.png

7c43dc7272cbd4555edfaab3e11113fc.png

④ 程序执行到 p1 = getStr1();   因为我的缘由在全局区中应该为“abcdefg1\0”

f1e55c109ad5bcf467d5d35e411f565e.png

⑤ 程序执行到 p2 = getStr2();    因为我的缘由在全局区中应该为“abcdefg1\0”

c387ada6c5051a7842062b2218ad1388.png

由于字符变量是一致的,因此并无从新放在另外一个内存区域,用的是同一个。

另注意:

打印p1 p2 所指向内存空间的数据

printf("p1:%s , p2:%s \n", p1, p2);

打印p1 p2 的值

printf("p1:%p , p2:%p \n", p1, p2);

1.3.2 栈区(栈区(stack) :① 由编译器自动分配释放,存放函数的参数值,局部变量的值等。② 函数运行时分配,函数结束时释放。由编译器自动分配释放 ,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。)

#include

char *get_str(void)

{

char str[] = "abcdedsgads"; //str在栈区,字符常量在全局区

printf("在子函数中:str = %s\n", str);

return str;

}

int main(vo1d)

{

char *p = NULL;

p = get_str();

printf("在主函数中:p = %s\n",p);

printf("\n");

system("pause");

return 0;

}

请问打印出来的内容一致吗?为何不一致?

5927d36e403cc299ae7d51ca6eade491.png

① 程序执行到 get_str();  包括第一次打印,到此都是正常的。

30febe523d8efe30fa267bb171c59f34.png

② 程序执行到 p = get_str();  讲数组的首地址赋值给p,到此也是正常的。

c549f56a5827771bd85ea8aeaf46356a.png

③ 程序运行完  p = get_str();  还未运行 printf("在主函数中:p = %s\n",p);时

注意此时的变化:栈区(stack) :① 由编译器自动分配释放,存放函数的参数值,局部变量的值等。②函数运行时分配,函数结束时释放。由编译器自动分配释放 ,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

323c779d92ed4560255952fd4bb3efd4.png

④ 程序运行至打印      打印出来了乱码

1.3.3 堆区(heap) : 通常由程序员分配释放(动态内存申请与释放),若程序员不释放,程序结束时可能由操做系统回收。

接着栈所说的内容,咱们该如何取出这一串字符呢?

#include

#include

char *get_str(void)

{

char *p1 = NULL;

p1 = (char *)malloc(100);

if (p1 == NULL)

{

return NULL;

}

strcpy

(p1,"adsagldsjg1k");

return p1;

}

int main(vo1d)

{

char *p = NULL;

p = get_str();

if (p != NULL)

{

printf("在主函数中:p = %s\n",p);

free(p);

p = NULL;

}

printf("\n");

system("pause");

return 0;

}

8e899937c3f3374f11e063d494c68e90.png

① 程序运行至  char *p = NULL;

e3576c89cd5e4ea1ee2119290acff5a0.png

② 程序运行至  get_str();  中的 char *p1 = NULL;

84ef4108f922bea3dc802885a6f75961.png

③ 程序运行至p1 = (char *)malloc(100);

dc11f79d96991290e11e8ad09bbfe0a6.png

④ 程序运行到 strcpy(p1,"adsagldsjg1k");

b785906d21ea64e6c554598cc86ddb11.png

⑤ 程序运行到  p = get_str();

316591cbdcc36ae65f774dcb27d414c3.png

⑥ 程序运行到  printf("在主函数中:p = %s\n",p);

d6bb7fd402128cbf9c98a584ff3ef49e.png

⑦ free(p); 只是解除了程序对于堆区地址0x20的使用权,并无将0x20处的数据清除。

1.4 函数的调用模型

a93d1fa40632236ae3f9300df10d27dc.png

1.5 栈的生长方向和内存存放方向

8dcf87f65b60b11898d87b515e0c2fbe.png

#include

int main(void)

{

int a;

int b;

char buf[4];

printf("&a: %p\n", &a);

printf("&b: %p\n", &b);

printf("buf的地址 : %p\n", &buf[0]);

printf("buf+1地址: %p \n", &buf[1]);

return 0;

}

36bdd1b5a0042edce26c8394575f43de.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值