1.你的数据放在那里
1。未初始化的全局变量(.bss段)
2.初始化的全局变量(.data段)
3.常量数据(.rodata段)用来存放常量数据的。
4.代码(.text段)test段存放代码(如函数)和部分整数常量,它与rodata段很相似。
5栈(stack)栈用来存放零时变量和函数参数
6.堆(heap)是最灵活的一种内存,他的生命周期完全由使用者掌控
内存的分配方式
1.从静态存储区域分配。
内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。
2.在栈上创建。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束后这些存储单元被自动释放。
3.从堆上分配。
亦称动态内存分配,动态内存的生存期由程序员决定,使用非常灵活,但问题也多。
3.常见的内存错误及对策
(1)内存分配未成功,却使用了它。
解决办法:在使用之前检查指针是否为NULL,如果指针P是函数 的参数,那么在函数的入口用“assert(p!=NULL)”进行检查;如果是用malloc或new来申请内存,应该用“if(p ==NULL)”或if(p!=NULL)进行放错处理。
(2)内存分配成功但为初始化就应用它。
错误原因:一是没有初始化的概念;二是误以为内存的默认初始值全为0.
(3)内存分配成功并且已经初始化,但操作越过了内存的边界。
(4)忘记了释放内存,造成了内存泄漏。
(5)释放了内存却继续使用它。
4.段错误及其调试方法
产生了段错误就是访问了错误的内存段,一般是你没有权限,或者根本没有对应的物理内存,尤其是常见的访问0地址。
方法一:用GDB一步一步查找段错误
方法二:分析cero文件
方案三:段错误时启动调试
方案四:利用backtrace和objdump进行分析
5.指针和数组的特性比较
(1)修改内容
(2)内容复制与比较
(3)计算内存容量
数组要么在静态存储区被创建,要么在栈上被创建,数组名对应一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是可变,所以通常用指针来操作动态内存,指针远比数组灵活,但也更危险。
6.宏定义#define
宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。
(1)无参宏定义
#define 标识符 字符串
宏定义必须写在函数外面,可以用#undef终止宏定义
宏名在源程序中若用引号括起来,则预处理不对他进行宏代换
宏定义允许嵌套
对输出格式进行宏定义,可以减少麻烦。
(2)带参宏定义
#define 宏名(形参表) 字符串
区别:1.带参宏定义中,形式参数不分配内存,因此不必做类型定义;而宏调用中的实参有具体的值,要用他们去代换实参,因此必须做类型说明。
2.在宏定义中形参是标识符,而宏调用中的实参可以是表达式
7.宏定义使用技巧
(1)防止一个头文件被重复包含
(2)得到指定一个字节上的一个字节或字
(3求最大值和最小值
(4)得到一个field在结构体(struct)中的偏移量
………
使用一些宏跟踪和调试
8.结构体
struct是个神奇的关键字,它将一些关联的数据打包成一个整体,方便使用。
一般形式:
struct 结构体名
{
类型名1 成员名1;
类型名2 成员名2;
类型名3 成员名3;
};
struct tsudent
{
char name[10];
cahr sex;
int age;
float score;
};
(2)定义结构体类型的变量、指针变量和数组
(3)为结构体变量赋初始值
9.#define 与typedef的区别
#define原本在C语言中就是为了定义常量,而typedef则是常用来定义关键字、冗长的类型别名。宏定义只是简单的字符串代换,而typedef则不是原地扩展,它的新名字具有一定的封装性,以至于新名字的标识符具有更易定义变量的功能。