1、1.基本类型 1.1变量的分类 以 为划分依据,分为全局变量与局部变量。 例外:函数参数也是局部变量 1.2变量的作用域 作用域的最大单位是文件 (编译的最小单位是文件 ),作用域的最小单位是,全局变量的作用域的文件,局部变量的作用域是 ,变量的作用域,始于定义处,结束预作用域结束。 相同的作用域内不可以有重名的变量。 当局部变量发生重名的时候,局部会覆盖全局的变量,小范围与大范围的变量发生重名的时候,小范围会覆盖大范围的变量 。 1.3变量的生命周期 局部变量的生命周期早所在的函数,全局变量的声明周期同 main()。 1.4修饰类型 修饰类型影响了变量的生命周期和存储区域。 1.4.1au
2、to自动变量 局部变量在缺省存储类型的情况下归为自动变量 作用:修饰局部变量 存储区域 : 栈 生命周期:同所在的函数或作用域 特点:如未初始化,初始值未知。大将军变量,随用随开,用完即消 1.4.2register寄存器变量 存放在 CPU 的寄存器中。对于循环次数较多的循环控制变量及循环体内反复使用 的变量均可定义为寄存器变量。 作用:修饰局部变量 影响了变量的存储区域 存储区域:寄存器 生命周期:同所在的函数或作用域,随用随开,用完即消。 特点:如未初始化,初始值未知。不可取地址,易被编绎器优化 1.4.3static静态变量 在程序执行时存在,并且只要整个程序在运行,就可以继续访问该变
3、量。 作用:修饰局部变量和全局变量 存储区域:数据区 生命周期:同 main() 特点:只初始化一次, 默认初始化为 0,生命周期同 main(); 影响了全局变量的作用域和局部变量生命周期。 1.4.4extern扩展类型 扩展全局的作用域。同文件中,或不同文件中。 作用:修饰全局变量。 存储区域:数据区 生命周期:同 main() 特点:因为是修饰的是全局变量,所以只扩展了其作用域(本文件内或跨文件)。 1.5c中 const的类型 1.const 定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象 #define 一样给出的是立即数,所以, const 定义的常量在程序运行过程
4、中只有一份拷贝,而 #define 定义的常量在内存中有若干个拷贝。 2.提高了效率。 编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一 个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。 1.6总结 变量类型 修饰类型 作用域 生命周期 进程空间 全局 无 本文件或其他文件 同 main 读写数据区 rw static 本文件 读写数据区 rw 局部 register 定义变量所在的 同所在的函数 寄存器 auto 定义变量所在的 栈 static 定义变量所在的,但可被引用 同 main 读写数据区 rw 2.进程空间与内存管理 2.
5、1 程序和进程 程序:源代码经过编译器编译后生成的可执行文件,静 态的概念 进程:可执行文件,运行后被加载在内存中运行,运行的过程,即为进程。以下是可执行文件加载到内存后的分布情况,动态的概念。 2.2 进程空间布局 首先 32 为操作系统采用虚拟内存技术,将 4G 的虚拟地址空间划分为两个部分:用户空间和内核空间。用户空间从 0 到 0xbfffffff,内核空间从 3G到 4G。用户进程不能访问内核。 程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操 作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟 地址到物理内存地址的映射,就可以保 证
6、不同的程序最终访问的内存地址位于不同的区 域,彼此没有重叠,就可以达到内存地址空间隔离的效果。 进程空间分布 如下图: 数据量分布 进程空间 注释 command args 命令行参数 普通局部变量 stack 栈 dynamic lib 加载动态库区 malloc 动态申请空间 heap 堆 data 常量 uninit bss 未初始数据段 普通全局 静态 (全局或局部 ) 未初始化 init 常量 rw 初始化数据段的读写段 初始化 ro 初始化数据段的只读段 常量 text 代码段 注:上面常量是包括初始化的 ro 只读段 2.3 内存管理 2.3.1 栈内存 栈区( stack)有编
7、译器自动分配释放,存放函数的参数,局部变量等 .在windows 中,栈是向低地址扩展的数据结构,是一块连续的内存区域。也就是栈顶的地址和栈的最大容量( windows 是 2M)是系统规定好的。如果申请的空间超过剩余栈空间,将提示 overflow,因此从栈获得的空间较小。栈空间不能返回。 2.3.2 堆内存 堆区( heap)一般由程序员分配和释放,否则就由 OS 回收。堆用于存放全局变量,静态变量,常量字符串和函数代码(函数体的二进制代码 ). 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于系统中有效的虚拟内存。由此可见堆的内存获得比较灵活,空间较大。另外对内存可以在
8、函数中返回。 堆内存的申请与释放 malloc: void *malloc(size_t,Size),malloc 函数在堆中分配参数 _Size 指定大小的内存,单位:字节,函数返回 void*指针。 calloc: void*calloc(size_t nmemb,size_tsize); 第一个参数是所需内存单元数量,第二个参数是每个内存单元的大小(单位:字节), calloc 自动将分配的内存置 0 realloc void*realloc(void*ptr,size_t size); 第一个参数 p 为之前用 malloc 或者 calloc 分配的内存地址, _NewSize 为重
9、新分配内存 的大小 ,单位:字节。成功返回新分配的堆内存地址,失败返回 NULL. 如果参数 p 等于 NULL,那么 realloc 与 malloc 功能一致 realloc 新分配的空间不会像 calloc 自动清空,而是像 malloc 一样需要手动清空 free voidfree(void *p); free 负责在堆中释放 malloc 分配的内存。参数 p 为 malloc 返回的堆中的内存地址 堆内存的常见错误 释放以后继续使用是错误的 通过函数返回堆里的内存 常见错误:栈上的空间不能返回,只读数据区数据不能修改。在返回值的时候要具体情况具体对待,要清楚返回的指针是在常量区还是
10、在栈区等。并且如果指针作为参数的时候是传入本身还是能够修改值。 1.通过返回值返回 2.通过 参数返回 int main01() void(*p)() = func;/定义一个函数指针,指向 func 的地址 int *p1 = (int *)p;/把指向代码区的指针强转为 int * *p1 = 10;/改变代码区的内容是非法的。 return 0; int a1() printf(“a1n“); return 5; int a2() printf(“a2n“); return 6; /函数返回一个指针 / int *get1()/这个是个错误的模型 int a = 10; return /
11、代码的问题时,返回一个栈变量的地址 int *get2()/正确的模型 static int a = 10; return /因为 a是一个静态变量,静态变量的地址在程序运行期间一直有效 int *get3()/正确的模型 return calloc(1, sizeof(int);/返回了一个堆的地址 const char *get_str1()/错误的模型 char s6 = “hello“;/s 在哪里?在栈里 return s;/返回一个栈的地址 const char *get_str2()/正确的模型 return “hello“;/hello 是一个常量,在常量区,常量区和静态区是一
12、样的,唯一的区别是常量区是只读的 const char *get_str3()/正确的模型 const char *s = “hello“; return s; const char *get_str4()/正确的模型 char *p = calloc(10, 1);/堆空间 strcpy(p, “hello“); return p; /函数的参数是指针 void get_str5(char *p)/正确的模型 strcpy(p, “hello“); void get_str6(char *p)/错误的模型 p = calloc(10, 1);/这个地方是给 p这个形参分配了内存,但并没有修改
13、实参的值 strcpy(p, “hello“); /函数终止的时候, p 已经消失了 void get_str7(char *p)/合法的 *p = calloc(10, 1); strcpy(*p, “hello“); void get_str8(char *p)/错误的模型 *p = calloc(10, 1); strcpy(p, “hello“);/p 是一个二级指针,不能当字符串处理 3.结构体 struct 是结构体的关键字 3.1 匿名结构体 仅在本地使用,不能带来多 余的类型名。但需要定义的时候同时定义变量。 struct char name30; char sex; int
14、age; float high; stu; 3.2 有名结构体 一处定义,可以多出使用。 struct student char name30; char sex; int age; float high; stu; struct student stu2; 3.3 别名结构体 更好使用的结构体 typedef struct student char name30; char sex; int age; float high; STUDENT; STUDENT stu, stu2; 3.4 结构体类型变量的初始化以及访问 3.4.1 初始化 凡是构造类型,要么在定义的时候初始化,要么先定义再成
15、员分别初始化。 3.4.2 访问方式 访问访问方式有两种一类是 (.)成员运算符,一类是 (-)指向运算符。 3.4.3 赋值 同类结构体间可以相互赋值。 struct Array int val10; ; int main() struct Array array = 1, 2, 3, 4, 5, 6 ; struct Array array2 = array; for (int i = 0; i 10; i+) printf(“%dn“,array2.vali); return 0; 3.5 结构体数组 举例: struct Person char name30; int voteCoun
16、t; ; int main() struct Person man3 = “zhangsan“, 0 , “lisi“, 0 , “wangwu“, 0 ; printf(“t-candidate as follow-n“); for (int i = 0; i 3; i+) printf(“ |%s“, mani.name); putchar(10); char namebuf30; for (int i = 0; i 10; i+) printf(“pls input your person:“); scanf(“%s“, namebuf); for (int i = 0; i 3; i+) if (strcmp(namebuf,mani.name) = 0) mani.voteCount+; for (int i = 0; i 3; i+) printf(“ |%s -vote count: %dn“, mani.name, mani.voteCount); return 0; 3.6 结构体中嵌套结构体