【C3】变量作用域、内存

2021年11月27日    Author:Guofei

文章归类: 语言    文章编号: 10003


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2021/11/27/c3.html

Edit

变量作用域

auto

  • 代码块内部的变量,默认为 auto,即使不加
  • 变量作用域为一个花括号
int a = 0; //写在代码块外,等价于 extern int a = 0;
int main(int argc, char **args) {
    int a = 1; // 写在代码块里,等价于 auto int a = 1;
    {
        int a = 2; //
        printf("%d\n", a);
    }
    printf("%d\n", a);
}

register

前面说过

static

static 静态变量

  • 内存位置在程序执行期间不会变
  • 只能被同一个代码块(如果写在代码块里)或同一文件(如果写在文件里)访问
  • 程序加载后就在内存中出现,程序结束后才消失。整个程序运行中 只初始化一次
  • 对于代码块外的变量,如果不加 static,就是 extern。加了 static,就不是 extern 了,就只能在本文件中运行。
  • 对于函数来说,如果不加 static,默认是全局的。如果加了 static,就在单个文件中生效
int my_fun() {
    static int a = 0; // 整个程序中,只执行一次
    a++; // 程序运行中,一直出现在内存中
    printf("%d\n", a);
    return 0;
}


int main() {
    my_fun();
    my_fun();
    my_fun();
}
// 打印结果:
// 1
// 2
// 3

extern

全局变量 extern :可以“跨文件”使用

// other.c
int a = 5;

// main.c

#include<stdio.h>
extern int a;

int my_fun() {
    a++;
    printf("%d\n", a);
    return 0;
}


int main() {
    my_fun();
    my_fun();
    my_fun();
    a++;

    printf("%d\n", a);

}


// gcc -o main main.c other.c
// ./main
// 打印结果:
// 6
// 7
// 8
// 9

也可以 extern 一个函数

// other.c
#include<stdio.h>

int a = 5; // 在这里定义这个变量

int my_fun2(){
    a++;
    printf("my_fun2 : %d\n", a);
    return 0;
}


// main.c
#include<stdio.h>

extern int a; //这里声明一个全局变量
extern int my_fun2(); // 这里声明,其它地方定义
int my_fun3(); // 不加 extern,也默认为extern


int my_fun() {
    a++;
    printf("%d\n", a);
    return 0;
}


int main() {
    my_fun2();
    my_fun();
    my_fun();
    a++;

    printf("%d\n", a);

}

内存布局

  1. 代码加载到 代码区
  2. 所有的static、external放到 静态区
  3. auto类型、函数的形参、函数的返回值,放到 栈区
    • 每个线程有自己的栈,
    • 栈的最大尺寸固定,超过会溢出
    • 变量离开作用范围后,栈上的数据自动释放。例如,函数调完后,函数内部声明的变量,其内存被清空.
  4. ,容量远远大于栈,堆内存的申请和释放必须通过代码完成
    • 栈大小是有限的,很大的数组适合用堆
    • 如果数组定义时大小不能确定,适合用堆

int func(int a, int b){ // 从右向左入栈。b先入栈

}

需要 #include <stdlib.h>

  • char *s = malloc(n) 分配 n 个字节
    • 只分配内存,不会帮你初始化
  • calloc(cnt, size) 分配 cnt 个单位,每个单位 size 大小
    • 分配的内存自动初始化为 0
  • s1 = realloc(s1, size_new) 重新分配内存大小
    • 有可能重新分配内存地址
    • 原本的 s1 会自动释放
    • 新空间不会自动初始化为 0
char *s = malloc(10); // 在堆中分配10个字节的空间
printf("%p\n", s);
strcpy(s, "abcd");
printf("%p\n", s);

free(s); // 必须手动释放内存,不是释放变量s,而是释放s指向的内存空间
printf("%p\n", s);

s = malloc(20); //s是变量,所以可以再次使用
printf("%p\n", s);

堆可以和“返回指针的函数”连用

下面这段代码编译可以通过,但不符合规范

int *tst() {
    int a = 10;
    return &a;
}

int main() {
    int *p = tst();
    printf("%d", *p);// 函数结束后,a 对应的内存值已经没了,所以p指向一个无效的空间。这个结果是不可预知的
}

正确的做法:

#include<stdio.h>
#include <stdlib.h>
#include <string.h>

int *tst() {
    int *p = malloc(1 * sizeof(int));
    return p;
}

int main() {
    int *p = tst();
    printf("%d", *p);
    free(p); // 不要忘记释放内存
}

当然,字符数组作为指针,被返回时也会又相同的问题

// !!! 这是错的!
char *tst() {
    char a[100] = "hello";
    return a;
}

// 这样才是对的,(调用后别忘了释放内存)
char *tst() {
    char *a= malloc(100);
    strcpy(a,"hello");
    return a;
}


// 其实这样也是对的,因为静态变量,整个程序结束后才会释放内存
char *tst() {
    static char a[100] = "hello";
    return a;
}

// 这个也是对的,常量也是一直在内存中的
const char *tst() {
    const char *a = "hello";
    return a;
}
// 上面等价于,这也是可以的
const char *tst() {
    return "hello";
}

一些注意:

  • p = malloc(10) 分配内存后,不要用 p++,因为 free(p) 会释放之后的10个字节,导致整个程序崩溃
  • malloc 的入参可以是变量,意味着你可以不用魔法数字,而是动态指定大小。

操作系统分配内存时,会一次给出4k(windows,实测MacBook是动态值),而不是每次 malloc 都分配一次。

  • 所以,如果你实现知道大概需要占用多少内存,你可以用 char *s = malloc(4*1024); free(*s) 先把内存空间申请下来

结构体

文件读写

c++


您的支持将鼓励我继续创作!
WeChatQR AliPayQR qr_wechat