c语言基础学习08_关于内存管理的复习

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

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

例如:

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

----------------------------------------------------------------------------- 在代码块之外的变量都是全局变量,那么如果加了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下示例代码如下:

 1 #include <stdio.h>
 2 
 3 //用递归代码实现栈的先进后出效果(递归的过程是典型的先入栈后出栈过程)
 4 
 5 void test(int n)
 6 {
 7     printf("%p, n = %d\n", &n, n);  //把代码放到递归的前面,叫做先序递归。
 8     if (n < 3)
 9     {   
10         test(n + 1); 
11     }   
12     printf("%p, n = %d\n", &n, n);   //把代码放到递归的前面,叫做先序递归。
13 }
14 
15 int main()
16 {
17     test(0);
18 
19     return 0;
20 }
21 输出结果为:
22 0x7ffe5e720b0c, n = 0    第一个入栈
23 0x7ffe5e720aec, n = 1
24 0x7ffe5e720acc, n = 2
25 0x7ffe5e720aac, n = 3
26 0x7ffe5e720aac, n = 3
27 0x7ffe5e720acc, n = 2
28 0x7ffe5e720aec, n = 1
29 0x7ffe5e720b0c, n = 0    最后一个出栈
test(0)
{ 
    printf("%p, n = %d\n", &n, n);    //0x7ffe5e720b0c, n = 0
    test(1)
    {
        printf("%p, n = %d\n", &n, n);    //0x7ffe5e720aec, n = 1
        test(2)
        {
            printf("%p, n = %d\n", &n, n);     //0x7ffe5e720acc, n = 2
            test(3)
            {
                printf("%p, n = %d\n", &n, n);    //0x7ffe5e720aac, n = 3
                4 < 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、

 1 int *test()    //错误的代码
 2 {
 3     int i = 0;    //i在栈里面,生命周期就是其所处的大括号。
 4     return &i;
 5 }
 6 
 7 int main()
 8 {
 9     int *p = test();    //p指向了一个无效的地址。
10     *p = 10;
11     return 0;
12 }
13 --------------------------------------
14 int *test()    //正确的代码
15 {
16     int *i = malloc(sizeof(int));    //i在堆里面。生命周期很长。主动调用free,堆空间释放或者进程结束,操作系统进行内存空间回收。 
17     return i;
18 }
19 
20 int main()
21 {
22     int *p = test();
23     *p = 10;
24     free(p);
25     return 0;
26 }

2、

 1 void test(char *i)    //错误的代码
 2 {
 3     i = malloc(sizeof(char) * 100);        //i在栈里,指向了堆的地址。
 4 }
 5 
 6 int main()
 7 {
 8     char *p = NULL;
 9     test(p);    //实参的值可以传递给形参,形参的值发生改变,实参的值不会有影响。
10     strcpy(p, "hello");
11     free(p);
12     return 0;
13 }
14 --------------------------------------
15 void test(char **i)    //正确代码,通过二级指针解决上面这个问题。
16 {
17     *i = malloc(sizeof(char) * 100);
18     
19 }
20 
21 int main()
22 {
23     char *p = NULL;
24     test(&p);
25     strcpy(p, "hello");
26     free(p);
27     return 0;
28 }
29 小结:若想通过函数形参给实参分配内存,往往是通过二级指针来实现的。这是在c语言里面常用的技巧。
30 --------------------------------------
31 char *test()    //正确的代码
32 {
33     char *i = malloc(sizeof(char) * 100);    //i在堆里面。
34     return i;
35 }
36 
37 int main()
38 {
39     char *p = test();
40     strcpy(p, "hello");
41     free(p);
42     return 0;
43 }

3、

 1 void test(char *i)    //错误的代码
 2 {
 3     strcpy(i, "hello");
 4 }
 5 
 6 int main()
 7 {
 8     test("hello");    //在栈里面:i = "hello"是常量。常量不可变。
 9     return 0;
10 }
11 --------------------------------------
12 const char *test()    //正确的代码        函数的返回值是一个指向常量的指针。即该指针可以指向任何常量的地址。
13 {
14     return "hello";    //"hello"是常量。而且是字符串。所以它是const char *类型的。
15 }
16 
17 int main()
18 {
19     const char *s = test();
20     printf("%s\n", s);
21     return 0;
22 }

4、

 1 char *test()    //错误的代码        函数的返回值是指针变量。
 2 {
 3     return "hello";    //"hello"是常量。实际返回值是一个常量。
 4 }
 5 
 6 int main()
 7 {
 8     char *s = test();
 9     strcpy(s,"aabbcc");
10     printf("%s\n", s);
11     return 0;
12 }
13 --------------------------------------
14 const char *test()    //错误的代码    
15 {
16     const char a[] = "hello";    //数组a是自动变量,在栈里面。"hello"是常量,在静态区里面。从语法的角度const作用是:不能这样(a[0] = 'a';)去修改它的值。只读。
17     return a;
18 }
19 
20 int main()
21 {
22     const char *s = test();
23     printf("%s\n", s);
24     return 0;
25 }
26 --------------------------------------
27 const char *test()    //正确的代码
28 {
29     static char a[] = "hello";    //此时数组a在静态区里面。
30     return a;
31 }
32 
33 int main()
34 {
35     const char *s = test();    //从语法的角度const作用是:不能这样(s[0] = 'a';)去修改它的值。只读。
36     printf("%s\n", s);
37     return 0;
38 }

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WebDeveloper

跟我学习php字符串常用函数-下篇

1> mixed parse_url ( string $url [, int $component = -1 ] )

10520
来自专栏郑科的专栏

PHP7 新特性简介(一)

PHP7是PHP编程语言全新的一个版本,在性能方面获得了极大的提升。官方的文档显示,PHP7可以达到PHP5.x版本两倍的性能。同时还提供了很多其他语言流行的语...

70200
来自专栏尾尾部落

[剑指offer] 字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符...

22820
来自专栏Java 源码分析

数据结构Queue

​ 栈和队列其实是相同的,只是名字不一样 入栈换成了入队(enqueue),出栈换成了出队(dequeue)。语义 是不同的。入队操作向队尾添加元素,而出...

27750
来自专栏阿凯的Excel

Python读书笔记17(while与列表、字典)

今天分享利用while函数处理列表和字典,顺便温习一下历史知识 一、论如何将一个列表折腾至另外一个列表!(两个列表是独立的) 论折腾列表有几种方法! 先分...

37550
来自专栏magicsoar

C语言和go语言之间的交互 - C语言中使用go语言,使用的go语言又使用了c语言

一、go语言中使用C语言 go代码中使用C代码,在go语言的函数块中,以注释的方式写入C代码,然后紧跟import “C” 即可在go代码中使用C函数 ? 代码...

388100
来自专栏大眼瞪小眼

PHP HashTable总结

本篇文章主要是对 PHP HashTable 总结,下面的参考链接是很好的学习资料。学习“散列”这个数据结构—推荐《数据结构与算法分析 C语言描述》

17910
来自专栏数据结构与算法

15:Challenge 11(主席树裸题)

总时间限制: 10000ms单个测试点时间限制: 1000ms内存限制: 262144kB描述 给一个长为N的数列,有M次操作,每次操作是以下两种之一: (1)...

370130
来自专栏java学习

Java基础总结大全(1)

一、基础知识: 1、JVM、JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性。 ...

38150
来自专栏魂祭心

原 Curry的js实现

37050

扫码关注云+社区

领取腾讯云代金券