简单说明
- 程序与数据保存在储存器中;
- 储存器分为易失与非易失;易失:内存;非易失:闪存或硬盘;
- 创建标志符:编译器gcc将标识符转化为地址;可变的标志符称为变量;
- 计算机内存中只有:值与地址;
- 我们不需要控制内存地址分配:操作系统也gcc编译器帮我分配好了;
- 每个数据片拥有唯一地址;
- 地址不允许是0(NULL)和负数;
- 编译器能够将标识符转化为地址;
一、栈
- 计算机将易失内存编组为三种类型:
- 栈内存(主要介绍)
stack:先入后出;放入一项:压栈;移出一项:弹出 - 堆内存(后面在学习)
- 程序内存(储存计算机程序机器码)
- 栈调用
void f1(void)//无返回值;为实参传入
{
}
void f2(void)
{
f1();
}
void f3(void)
{
f2();
}
void mian()
{
f2();
}
- 当函数被调用时,调用后函数地址被压栈。这个地址就是“返回位置”(RL)。
- 相同函数在不同地方调用,那个调用都有一个对应返回值;
- 函数结束后,程序将从储存在栈顶的地址处继续。调用栈顶内容被弹出;
二、函数实参
- 函数实参储存地址在返回位置之上;
- 实参和返回位置共同构成了被调用的栈帧;
- 当函数被调用时,调用后函数地址被压栈。这个地址就是“返回位置”(RL)。
- 相同函数在不同地方调用,那个调用都有一个对应返回值;
- 函数结束后,程序将从储存在栈顶的地址处继续。调用栈顶内容被弹出;
三、局部变量
- 函数有局部变量,局部变量被储存在实参之上;
- 函数实参储存地址在返回位置之上;
- 实参和返回位置共同构成了被调用的栈帧;
- 当函数被调用时,调用后函数地址被压栈。这个地址就是“返回位置”(RL)。
- 相同函数在不同地方调用,那个调用都有一个对应返回值;
- 函数结束后,程序将从储存在栈顶的地址处继续。调用栈顶内容被弹出;
全局变量与局部变量形成对比;通常在源文件顶部进行指定,任何函数都可进行操作;
全局变量被认为是有害的:任何位置都可改变,会导致追踪产生困难;
全局常量被接受经常使用
四、值地址
int f1(int k,int m)
{
return (k+m);
}
void f2(void)
{
int u;
u = f1(1,2);
}
- 不初始化变量:可以储存任何值
- u地址在f1被调用前储存在调用栈,这个地址叫做值地址,因为它是函数f1储存返回值的地址。
栈规则进行合并:
- 一个函数返回了一个值,这个值就会被写到调用函数栈帧的一个局部变量中,这个变量的地址(叫作值地址)储存在调用栈中;
- 函数有局部变量,局部变量被储存在实参之上;
- 函数实参储存地址在返回位置之上;
- 实参和返回位置共同构成了被调用的栈帧;
- 当函数被调用时,调用后函数地址被压栈。这个地址就是“返回位置”(RL)。
- 相同函数在不同地方调用,那个调用都有一个对应返回值;
- 函数结束后,程序将从储存在栈顶的地址处继续。调用栈顶内容被弹出;
关键字return
- 如果void位于函数名前,此函数不会返回任何值。return使此函数停止,程序从调用函数中返回地址继续;
- 不是void型,return会为在调用栈中由值地址给出的变量赋值;
return后任何内容都被忽略不会被执行。函数停止,栈帧会出栈,程序会返回地址继续执行;
五、数组
- 数组被称为“零索引”
- 不需要考虑地址分配,数组中元素地址总是连续的
- 指定数组长度`int arr[5] = {0}
- 不指定数组长度 int arr[ ] = {1,2,3,4}
六、获取地址
- 可以通过变量前叫一个&获取地址;
- 地址也可在printf函数中使用%p打印
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char * *argv)
{
int a = 5;
int c = 17;
printf("a address is %p,c address is %p\n",&a,&c);
return EXIT_SUCCESS;
}
//每次运行地址可能不同
七、可见度
- 每当一个函数被调用时,一个新的栈帧就被压入调用栈。函数只能看到自己的栈帧。
- 虽然标识符可能出现在不同栈帧,但是相同名字却不能在相同的帧栈中被定义两次。