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