C 语言的各种变量的存储,指针的常见错误的总结

变量
全局变量和局部变量 :
1.局部变量
他是 指在函数内部定义的变量 作用域为定义局部变量的函数 也就是说他只能
在定义他的函数中使用
最值得注意的是 只有在程序执行到定义他的模块时才能生成,一旦执行退出该
模块则起变量消失,需要初始化,否则是不可预知的数字。
2.全局变量
在程序执行的过程中一直有效。不需初始化,默认为0;
区别:
生存周期:全局变量在整个程序有效,局部变量在模块中有效。
分配方式:
全局变量是在所有函数之外声明的变量,局部变量则是在函数体内声明的变
量。全局变量放在内存的全程数据区,局部变量在栈上申请。全局数据区分为
两部分全局数据区和静态数据区,静态数据区专门存放static变量。
编译器会自动初始化全局变量。
变量
静态存储变量和动态存储变量 :
从变量生存期来分,可分为静态存储变量和动态存储变量。静态变量
指在程序运行期间给变量分配固定的存储空间,动态存储变量指程序
运行期间需要动态给变量分配的存储空间。
C语言中主要包括;自动的(auto),静态的(static),寄存器(register),
外部的(extern)
Auto变量:
局部变量不作任何说明,都作为自动变量auto,及动态存储的。Auto可
省略。
Register变量:暗示编译程序相应的变量将将被频繁使用,如果可能
的话,应将其保存在CPU的寄存器中,以指加快其存取速度 ,有几点
使用限制:
1 register变量必须是能被CPU寄存器所接受的类型
2为register变量可能不存放在内存中,所以不能用取址符运算符“ & ”
来获取取址符运算符
3只有局部变量和形参可以作为register变量,全局变量不行
4 80x86系列CPU最多可使用的register变量数目有限。int型可使用8
个通用寄存器
5 静态变量不能定义为register。
变量
static变量及static用法:
静态局部变量
(1)静态局部变量属于静态存储方式,在函数内定义 它的生存期为整个源程序,但是其作
用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管
该变量还继续存在,但不能使用它。
(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
(3)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变
量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以 看出它是一种生存期为
整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它
又可继续使用, 而且保存了前次被调用后留下的 值。
.静态全局变量
全局变量(外部变量)的说明之前再冠以static 就构 成了静态的全局变量。全局变量本身就
是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不
同。这两者的区别虽在于非静态全局 变量的作用域是整个源程序, 当一个源程序由多个
源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制
了其作用域, 即只在 定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使
用它。
static 函数
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文
件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static”关键字即可关键字“static”,译成中
文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而
是指对函数的作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会
与其它文件中的函数同名,因为同名也没有关系。
变量
Extern变量:
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关
键字.它告诉编译器,其声明的函数和变量可以在本模块或其它模块中
使用。
1。对于extern变量来说,仅仅是一个变量的声明,其并不是在定义分
配内存空间。因此可以被extern变量声明多次。
2。通常,在模块的头文件中对本模块提供给其它模块引用的函数和
全局变量以关键字extern声明。也就是说c文件里面定义,如果该函数
或者变量与开放给外面,则在h文件中用extern加以声明。所以外部文
件只用include该h文件就可以了。而且编译阶段,外面是找不到该函
数的,但是不报错。link阶段会从定义模块生成的目标代码中找到此函
数。
如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能
在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显
的区别: extern int f(); 和int f();
使用如下声明正确吗?
在一个源文件里定义了一个数组:
char a[6];
在另外一个文件里用下列语句进行了声明:
extern char *a;
答案是不可以,声明需要严格按照定义的变量来,编译器会当作两个
变量处理。
了?
由c编译的程序占用的存储分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的
另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b;// 栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; 123456\0";//在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); //123456\0放在常量区,编译器可
能会将它与p3所指向的"123456"优化成一个地方。
影响 01 02 03 04, 以大端( big endian )按4字节读出到变量中则为
若内存字节为
0x01020304,以小端( little endian )按4字节读出到变量中则为 0x04030201。
大端(big-endian ):高位字节在低位字节的前面,也就是高位在内存地址低的一端。正常
逻辑顺序。
小端(little-endian ):低位字节在高位字节的前面,也就是低位在内存地址低的一端. X86
采用小端
地址偏移 大端模式 小端模式
0x00 12(OP0) 78(OP3)
0x01 34(OP1) 56(OP2)
0x02 56(OP2) 34(OP1)
0x03 78(OP3) 12(OP0)
测试程序(int 为双字节)
union w
{
int a;
char b[2];
}c;
c.a =1;
if(c.b[0] == 1)//小端
内存与指针
如左图,为一个执行进程的内存地址空间。
高地址
程序栈
向下增长
堆栈段
空洞
堆 向上增长
数据段
BSS
非零数据
代码
代码段就是存储程序文本的,所以有时候也叫做文本
段,指令指针中的指令就是从这里取得。数据段是存储
数据用的,还可以分成初始化为非零的数据区,BSS,
和堆(Heap)三个区域。初始化非零数据区域一般存放静
态非零数据和全局的非零数据。BSS是Block Started by
Symbol的缩写,原本是汇编语言中的术语。该区域主要
存放未初始化的全局数据和静态数据。还有就是堆了,
这个区域是给动态分配内存是使用的,也就是用 malloc
等函数分配的内存就是在这个区域里的。它的地址是向
上增长的。最后一个堆栈段(注意,堆栈是Stack,堆是
Heap,不是同一个东西),堆栈可太重要了,这里存放
着局部变量和函数参数等数据
代码段
低地址
需要注意的是,代码段和数据段之间有明确的分隔,但是数据段和堆栈段之间没有,
而且栈是向下增长,堆是向上增长的,因此理论上来说堆和栈会“增长到一起”,但是操
作系统会防止这样的错误发生,所以不用过分担心。
内存分配
内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
少的内存,程序员自己负责在何时用free 或delete 释放内存。
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, “hello world”);
printf(str);
}
解析:程序崩溃。
因为GetMemory 并不能传递动态内
存。
char *GetMemory(void)
{
char p = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
解析:可能是乱码。
因为GetMemory 返回的是指向“栈内存”
的指针,该指针的地址不是 NULL,但其
原现的内容已经被清除,新内容不可知。
常见内存使用错误
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, “hello”);
printf(str);
}
解析:
(1)能够输出hello
(2)内存泄漏
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,
而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
} 解析:如果非得要用指针参数去申请内存,那么
应该改用“指向指针的指针”.由于“指向指针的指针”
这个概念不容易理解,我们可以用函数返回值来传
递动态内存。
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
解析:篡改动态内存区的内容,后果难以预料,非
常危险。因为free(str);之后,str 成为野指针,
if(str != NULL)语句不起作用。
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, “hello”);
cout<< str << endl;
free(str);
}
解析:用函数返回值来传递动态内存这种方法虽然好用,
但是常常有人把return 语句用错了。这里强调不要用return
语句返回指向“栈内存”的指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值