作用域 内存布局 static volatile register
函数外定义的是全局变量 ----------> 整个程序都可以访问到,不过不同文件需要extern
函数内定义的是局部变量
局部变量也分块作用域 :
int a = 2;
int main()
{
int a = 5;
{
int a = 10;
}
}
同一个函数内,子函数也可以访问到内部的变量
#include <stdio.h>
#include <string.h>
int a;
void foo()
{
printf("%d\n", a); //竟然可以输出5
}
int main()
{
a = 5;
foo();
}
#include <stdio.h>
#include <string.h>
int a;
void foo()
{
printf("%d\n", a); //结果输出0,因为他寻找的是全局变量a 全局变量自动初始化为0
}
int main()
{
int a = 5;
foo();
}
#include <stdio.h>
#include <string.h>
void foo()
{
printf("%d\n", a); //报错,因为没有声明
}
int main()
{
int a = 5;
foo();
}
函数挨个调用的特殊现象:
- #include <stdio.h>
#include <string.h>
void fun()
{
int a;
printf("%p %d\n",&a ,a);
a = 10;
printf("%p %d\n",&a ,a);
}
int main()
{
fun(); //垃圾值 10
fun(); // 10 10 第二次调用,第一个a应该还是垃圾值,但是fun函数释放的内存刚好被再次使用,所以还是10
}
如果在两次调用函数之间,执行一下其他操作,结果才是正常现象
#include <stdio.h>
#include <string.h>
void fun()
{
int a;
printf("%p %d\n",&a ,a);
a = 10;
printf("%p %d\n",&a ,a);
}
int main()
{
fun(); //垃圾值 10
printf("****\n");
fun(); //垃圾值 10
}
两个文件的话,需要 extern 声明
a.c int b = 10;
b.c extern int b;
static的三个作用:
①限制了作用域
static int a
限制了a只能使用在本文件, 其他文件extern也没用
②延长生命周期
#include <stdio.h>
#include <string.h>
void fun()
{
static int a;
printf("%p %d\n",&a ,a); // 这个地址和局部变量在栈区开辟的地址不一样的地址
a = 10; // static 在 数据段 开辟的空间 和全局变量在的空间一样 就是说程序结束也释放
printf("%p %d\n",&a ,a);
}
int main()
{
fun(); //0 10
//不是垃圾值,未初始化的会在Bss区,全局变量也不会是垃圾值,是0,
// 在栈区也就局部变量才会出现垃圾值
printf("****\n");
fun(); //10 10
}
③封装了私有数据
static修饰函数,不同文件的函数,无法在调用这个被修饰的函数。
static void fun( void )
{
}
修饰变量也一样。见①。 static已经在编译的时候有值了
内存存储位置介绍:
栈区 高地址到低地址
堆区 低地址到高地址
而针对堆区的内存,一般由程序员进行分配和释放, 使用堆内存的原因一般是“栈上内存比较小,不够用”、“系统管理内存的方式死板,不方便用”这两大类原因。对于堆上的内存,被程序员手动分配后,若程序员不释放就可能会出现“内存泄漏”。很多企业级的应用,都因为内存泄漏而在“正常”运转很长时间后,轰然“坍塌”。
全局区、文字常量区和程序代码区在我们入门阶段,暂时还可以不去过多理解(甚至看不懂也无妨),只需要知道他们的大致作用即可——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放;文字常量区是用于储存常量字符串的, 程序结束后由系统释放;程序代码区用于存放函数体的二进制代码。
命令行参数:
#include <stdio.h>
#include <string.h>
int main(int argc, char * argv[])
{
int i;
printf("%d\n",argc);
for(i = 0; i < argc ; i++)
printf("%s\n",argv[i]);
}
#include <stdio.h>
#include <string.h>
int main(int argc, char * argv[])
{
if(argc < 3)
{
printf("you need args\n");
return -1;
}
int n = atoi(argv[1]);
int i;
for(i=0;i<n;i++)
printf("%s\n",argv[2]);
}
atoi : 把字符串转变成数字(类似于js的parseInt)
1234 -----> 1234
abcd -----> 0
123abcd -----> 123
abcd123 -----> 0
const使用介绍:
把变量变成了常量
const int a = 3; //还是在栈上开辟的地址,可以修改。只是给编译器看的,不可以修改。
a = 5;//错误
int *p = &a;
*p = 5;//可以变
两个等价
const int a
int const a
两个等价
const int *a const修饰的是 *a, 表示里面内容不可变,但是a的地址可以变
int const *a
int * const a const修饰的是 a, 表示a的地址不可变, 但是里面内容可以改变
const int * const a a的地址不能变,内容也不可变
如果 const在 * 的前面 就表示约束了*a 即*a不可变 即原来的内容不可改变
如果 *在const的前面 就表示约束不了*a 只是约束了a 表示地址不可变
在函数接口中,很多都使用了const
volatile
防止编译器优化对内存的读写
每次访问被volatile的变量 都需要从内存从新取值
volatile char * ch = (volatile char *)0x8000;
while(*ch) //加了volatile每次都向内存取值,不加的话,会放在cpu寄存器,值不变化。
{
printf("hello\n");
Sleep(1);
}
register
希望被修饰的变量放到寄存器里
但不一定放到寄存器里。
寄存器变量不能取其地址