=============================================================================
对于c语言来讲,内存管理是一个很重要的内容,它与指针是息息相关的,因为内存的管理都是通过指针来实现的。
-----------------------------------------------------------------------------
如果一个变量,它处在所有的代码块之外,那么它的生命周期就是和整个程序是一起的,程序启动的时候它就出现了,程序退出时,它才终止。
如果一个变量,它处在代码块之内,那么这个代码块执行的时候它才出现,代码块执行完成后,它才消失。
-----------------------------------------------------------------------------
auto int i = 0;
auto变量(自动变量)是在内存的栈里面,它是一个临时的变量,只有执行代码块的时候,它才会入栈,代码块执行完后,它才出栈。

static int i = 0;
static变量(静态变量)是在内存的静态区里面,整个程序运行期间,该变量都存在,而且静态变量只被初始化一次。

例如:

int i;
for (i = ; i < ; i++)
{
static int a = ; //定义了一个静态变量。
a++;
printf("%d\n", a);
}
输出结果为: --------------------------------------
int i;
for (i = ; i < ; i++)
{
auto int a = ; //定义了一个自动变量。
a++;
printf("%d\n", a);
}
输出结果为:

-----------------------------------------------------------------------------
在代码块之外的变量都是全局变量,那么如果加了static后,依然是全局变量,但是此时变量的作用域局限在定义这个变量的文件内部。
它其实还是放在静态区的,只是外部不能访问而已。

同时函数前面也可以加一个static,如下所示:

void test()    //没有static,默认该函数是全局的。
{
  ;
}

static void test1()   //这个函数只能在定义这个函数的文件内被调用。
{
  ;
}
注意:函数前面加static和本身的静态区没有任何关系,因为所有的函数都放在代码区,而静态区里面放的只是变量而已,不会放函数本身。

即:static放在函数的不同位置对于c语言来讲它的意义是不一样!
-----------------------------------------------------------------------------
extern int a;         //这句话的意思是:a已经定义过了,这里只是声明。

extern void test();  //这句话的意思是:函数test已经定义过了,这里只是声明。
void test();          //对于函数来说,没有extern和 有extern 对于c语言是一样的。(c语言里面一个不太好的地方)

extern int a;        //这句话的意思是:明确的声明了一个变量,一定不是表示定义一个变量。
int a;              //这句话的意思是:如果这个变量已经定义过了,这里就代表声明;如果这个变量没有定义过,这里就代表定义。
                //即:不能确定它是定义还是声明。也即:出现了二义性,比较含糊。
-----------------------------------------------------------------------------

在一个程序加载进内存的时候,操作系统会把不同类型的数据加载进不同的区域里面,例如:

代码区:可执行代码加载进代码区;比如:所有的函数。

静态区:所有的静态变量和全局变量都加载进静态区。实际上静态区是一个综合区,它会分很多子区,其中很多常量也是在静态区另外一个区里面放的。
  常量和普通静态变量有什么区别呢?
  不同点:常量也是在程序运行当中一直存在的,但是常量是只读的,普通的静态变量是可读可写的。
  相同点:他们的生命周期是一样的,整个程序运行的时候他们会出现在内存里面,整个程序执行完成以后他们才从内存里面消失。

栈区:栈是一种先进后出的内存结构,所有的 自动变量、函数的形参、函数的返回值 都是由编译器自动放入内存的栈中。
  当一个自动变量超出其作用域时,会自动从栈中弹出。
  栈区特点是:函数调用时栈出现,函数结束时栈消失。

堆区:堆和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
  堆是一个大容器,它的容量要远远大于栈,但是在c语言中,堆内存空间的申请和释放需要手动通过代码来完成。
-----------------------------------------------------------------------------
c、c++会用到堆和栈,但是需要去手动维护。
c#、java也会用到堆和栈,但是不需要去手动维护。因而付出的代价是:不能够选择你到底使用的是堆还是栈呢。

作为一名使用者,使用c#、java语言,它们已经给你规定好了,有很多类,有很多对象,你尽管拿来用,但是它们这个类或者对象在哪里,我们不知道。
我们也不需要知道,它们也不让你知道,我们就算知道也没用,因为我们也管不了,也处理不了。但是对于c语言,我们可以任意去控制这个变量是出现在栈还是堆里面。

而且c语言还比较简单,因为它所有的都是变量。
而c++和java还有对象,在c++里面可以指定你的对象在栈里面还是在堆里面,即可以选择效率最高的方法来使用对象,而在java里面所有的对象都是出现在堆里面的。

这就是c和c++语言的魅力所在,以及它们做操作系统的原因之一,因为它们可以自由的控制内存中的每一个字节。
-----------------------------------------------------------------------------
用递归代码实现栈的先进后出效果(递归的过程是典型的先入栈后出栈过程)

linux下示例代码如下:

 #include <stdio.h>

 //用递归代码实现栈的先进后出效果(递归的过程是典型的先入栈后出栈过程)

 void test(int n)
{
printf("%p, n = %d\n", &n, n); //把代码放到递归的前面,叫做先序递归。
if (n < )
{
test(n + );
}
printf("%p, n = %d\n", &n, n); //把代码放到递归的前面,叫做先序递归。
} int main()
{
test(); return ;
}
输出结果为:
0x7ffe5e720b0c, n = 第一个入栈
0x7ffe5e720aec, n =
0x7ffe5e720acc, n =
0x7ffe5e720aac, n =
0x7ffe5e720aac, n =
0x7ffe5e720acc, n =
0x7ffe5e720aec, n =
0x7ffe5e720b0c, n = 最后一个出栈
test()
{
printf("%p, n = %d\n", &n, n); //0x7ffe5e720b0c, n = 0
test()
{
printf("%p, n = %d\n", &n, n); //0x7ffe5e720aec, n = 1
test()
{
printf("%p, n = %d\n", &n, n); //0x7ffe5e720acc, n = 2
test()
{
printf("%p, n = %d\n", &n, n); //0x7ffe5e720aac, n = 3
< ;不符合if的判断条件,推迟if判断语句,则继续执行剩余代码:
printf("%p, n = %d\n", &n, n); //0x7ffe5e720aac, n = 3
}
printf("%p, n = %d\n", &n, n); //0x7ffe5e720acc, n = 2
}
printf("%p, n = %d\n", &n, n); //0x7ffe5e720aec, n = 1
}
printf("%p, n = %d\n", &n, n); //0x7ffe5e720b0c, n = 0
}

-----------------------------------------------------------------------------
如果程序中申请了堆内存,但忘记了free,如果程序退出的时候操作系统会统一进行回收;但如果程序一直不退出,那么这块内存就会一直被占用,
有时更可气的是,你不但不退出程序,而且还在不停的申请新的内存,也不free,最后操作系统的内存被你“吃光了”,导致内存泄漏!
-----------------------------------------------------------------------------
例如:
int *p = malloc(200);
p = realloc(p, 400);   //在p的基础上,将堆内存扩展到400个字节。
p = realloc(p, 100);   //在p的基础上,将堆内存缩小到100个字节。
int *p1 = realloc(NULL,100);   //如果realloc的第一个参数是NULL,那么realloc的作用和malloc是一样的。
-----------------------------------------------------------------------------

1、

 int *test()    //错误的代码
{
int i = ; //i在栈里面,生命周期就是其所处的大括号。
return &i;
} int main()
{
int *p = test(); //p指向了一个无效的地址。
*p = ;
return ;
}
--------------------------------------
int *test() //正确的代码
{
int *i = malloc(sizeof(int)); //i在堆里面。生命周期很长。主动调用free,堆空间释放或者进程结束,操作系统进行内存空间回收。
return i;
} int main()
{
int *p = test();
*p = ;
free(p);
return ;
}

2、

 void test(char *i)    //错误的代码
{
i = malloc(sizeof(char) * ); //i在栈里,指向了堆的地址。
} int main()
{
char *p = NULL;
test(p); //实参的值可以传递给形参,形参的值发生改变,实参的值不会有影响。
strcpy(p, "hello");
free(p);
return ;
}
--------------------------------------
void test(char **i) //正确代码,通过二级指针解决上面这个问题。
{
*i = malloc(sizeof(char) * ); } int main()
{
char *p = NULL;
test(&p);
strcpy(p, "hello");
free(p);
return ;
}
小结:若想通过函数形参给实参分配内存,往往是通过二级指针来实现的。这是在c语言里面常用的技巧。
--------------------------------------
char *test() //正确的代码
{
char *i = malloc(sizeof(char) * ); //i在堆里面。
return i;
} int main()
{
char *p = test();
strcpy(p, "hello");
free(p);
return ;
}

3、

 void test(char *i)    //错误的代码
{
strcpy(i, "hello");
} int main()
{
test("hello"); //在栈里面:i = "hello"是常量。常量不可变。
return ;
}
--------------------------------------
const char *test() //正确的代码 函数的返回值是一个指向常量的指针。即该指针可以指向任何常量的地址。
{
return "hello"; //"hello"是常量。而且是字符串。所以它是const char *类型的。
} int main()
{
const char *s = test();
printf("%s\n", s);
return ;
}

4、

 char *test()    //错误的代码        函数的返回值是指针变量。
{
return "hello"; //"hello"是常量。实际返回值是一个常量。
} int main()
{
char *s = test();
strcpy(s,"aabbcc");
printf("%s\n", s);
return ;
}
--------------------------------------
const char *test() //错误的代码
{
const char a[] = "hello"; //数组a是自动变量,在栈里面。"hello"是常量,在静态区里面。从语法的角度const作用是:不能这样(a[0] = 'a';)去修改它的值。只读。
return a;
} int main()
{
const char *s = test();
printf("%s\n", s);
return ;
}
--------------------------------------
const char *test() //正确的代码
{
static char a[] = "hello"; //此时数组a在静态区里面。
return a;
} int main()
{
const char *s = test(); //从语法的角度const作用是:不能这样(s[0] = 'a';)去修改它的值。只读。
printf("%s\n", s);
return ;
}

=============================================================================

c语言基础学习08_关于内存管理的复习的更多相关文章

  1. c语言基础学习08_内存管理

    =============================================================================涉及到的知识点有:一.内存管理.作用域.自动变 ...

  2. c语言基础学习09_关于复合类型的复习

    =============================================================================struct A{ char array[10 ...

  3. Objective-C 基础教程第九章,内存管理

    目录 Object-C 基础教程第九章,内存管理 前言: 对象生命周期 引用计数 RetainCount1项目例子 对象所有权 访问方法中的保留和释放 自动释放 所有对象放入池中 自动释放池的销毁时间 ...

  4. D01-R语言基础学习

    R语言基础学习——D01 20190410内容纲要: 1.R的下载与安装 2.R包的安装与使用方法 (1)查看已安装的包 (2)查看是否安装过包 (3)安装包 (4)更新包 3.结果的重用 4.R处理 ...

  5. D03——C语言基础学习PYTHON

    C语言基础学习PYTHON——基础学习D03 20180804内容纲要: 1 函数的基本概念 2 函数的参数 3 函数的全局变量与局部变量 4 函数的返回值 5 递归函数 6 高阶函数 7 匿名函数 ...

  6. D03-R语言基础学习

    R语言基础学习——D03 20190423内容纲要: 1.导入数据 (1)从键盘输入 (2)从文本文件导入 (3)从excel文件导入 2.用户自定义函数   3.R访问MySQL数据库 (1)安装R ...

  7. 【CUDA 基础】4.2 内存管理

    title: [CUDA 基础]4.2 内存管理 categories: - CUDA - Freshman tags: - CUDA内存管理 - CUDA内存分配和释放 - CUDA内存传输 - 固 ...

  8. C语言中储存类别和内存管理

    C语言中储存类别和内存管理 储存类别 C语言提供了多种储存类别供我们使用,并且对应的有对应的内存管理策略,在了解C中的储存类型前,我们先了解一下与储存类型相关的一些概念. 1. 基础概念 对象:不同于 ...

  9. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

随机推荐

  1. S7-200以太网通信

    一.西门子网络系统 二.s7-200通过以太网模块接入以太网 三.S7-200可以接入的以太网系统 四.S7-200以太网通讯实验 五.实验硬件系统组成 六.S7-200作为服务器的配置 1.进入以太 ...

  2. redis中使用 check-and-set 操作实现乐观锁

    WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为. 被 WATCH 的键会被监视,并会发觉这些键是否被改动过了. 如果有至少一个被监视的键在 EXEC 执行之前 ...

  3. LAMP第四部分mysql操作

    1. 忘记root密码编辑mysql主配置文件 my.cnf 在[mysqld]字段下添加参数  skip-grant  ,重启数据库服务,这样就可以进入数据库不用授权了 mysql -uroot , ...

  4. c#加密解密源码,md5、des、rsa

    从网上找来的代码,顺手改改,用起来更方便. 配置文件 using System; using System.Collections.Generic; using System.Text; using ...

  5. 4、树莓派的中文:安装ftp,安装gcc,安装qt,编译qt程序,运行qt界面程序

    本博文仅作本人操作过程的记录,留作备忘.自强不息 QQ1222698 1.安装FTP:sudo apt-get install vsftpd 2.配置FTP,修改,/etc/vsftpd.conf # ...

  6. JMeter数据库操作详解

    Jmeter提供了JDBC连接的插件,通过执行SQL语句的java API,实现对数据库的访问和查询,同时可以操作一次向数据库插入上百条上千条数据. 一.安装驱动包 将需要连接JDBC的jar包放入j ...

  7. admin

    执行顺序 : Admin 执行admin.py,导入models 第一次进来的时候,先创建admin.site对象(如果下次再有引入,不会重新创建) 拿到对象后执行该对象下的register()方法 ...

  8. chromedriver与chrome版本映射列表

    chromedriver与chrome版本映射列表: chromedriver版本 支持的Chrome版本 v2.30 v58-60 v2.29 v56-58 v2.28 v55-57 v2.27 v ...

  9. button的padding属性在i8下和chrome下表现不一致

    button的padding属性在i8下和chrome下表现不一致 在ie8下会撑破很多像素,撑破布局 padding: 10px 48px; padding: 1px 35px \0; /* pro ...

  10. form表单样式

    <BODY> <div id="modify-data"> <form class="modify-data-form"> ...