- 不同内存中变量的作用域和周期
作用域 | 生命周期 | |
全局内存 | 整个文件 | 应用程序的生命周期 |
静态内存 | 声明它的函数内部 | 应用程序的生命周期 |
自动内存(局部内存) | 声明它的函数内部 | 限制在函数执行时间内 |
动态内存 | 有引用该内存的指针决定 | 直到内存释放 |
- 指针
指针变量包含内存中别的变量、对象或函数的地址。对象即内存分配函数malloc分配的内存。通常指针根据所指的数据类型来声明。指针本身并没有包含所引用数据的类型信息,指针只包含了地址。
- 声明指针
通常在数据类型后面跟星号,再加上指针变量的名字可以声明指针。
先变量初始化,再定义和使用指针变量:
只有在初始化后,指针才能正常工作。比如,如果将指向未初始化内存的指针解引,此时指针的内容可能并不是一个合法地址,而程序没有权限访问不合法地址;同时,就算指针的内容是合法地址,那个地址也可能没有包含合法数据。
- 阅读指针声明(倒过来阅读)
pi是一个变量 | Const int *pi |
pi是一个指针变量 | Const int *pi |
pi是一个指向整数的指针变量 | Const int *pi |
pi是一个指向整数常量的指针变量 | Const int *pi |
- 地址操作符
地址操作符&会返回操作数的地址。如果想将&得到的地址赋给指针,需要将其想转化成指向整数的指针变量。即:
int num=0;
int *pi;
pi = (int*)#
printf("num的地址是%p\n", pi);
- 虚拟内存和指针
在虚拟操作系统上显示的指针地址一般不是真实的物理内存地址。
- 解引指针
通过解引指针修改变量时,左值需是可修改的。
- null的概念
NULL被赋值给指针就意味着指针不指向任何东西。它没有指向任何区域。两个NULL指针总是相等的。
NULL宏是强制类型转换为void的整数变量0。
#define NULL ((void*)0)
NULL指针跟未初始化的指针不同,未初始化的指针可能包含任何值,而包含NULL的指针则不会引用内存中任何地址。有趣的是,我们可以给指针赋值0,但不能赋任何别的整数值。
pi = NULL;
pi = 0;
pi = 100;//语法错误
任何时候都不能对NULL指针进行解引,因为他不包含合法地址。
指针可以作为逻辑表达式的唯一操作数。比如通过下面代码测试指针是否被设置成NULL。没必要显式的跟NULL做比较:if(pi==NULL)
int* pi;
pi = NULL;
if (pi)
{
printf("指针未被设置成NULL");
}
else
{
printf("指针被设置成NULL");
}
- NULL的运用
NULL不应该用在指针之外的上下文中,有时可能有用,但不应该这么用。因为ASCII字符NUL定义为全0的字节。如果代替NUL的话肯定有问题。
- void指针
void指针是通用指针,用来存放任何数据类型的引用。
性质:void指针具有和char指针相同的形式和内存对齐方式。
void指针和别的指针永远不会相等,不过两个赋值为NULL的void指针是相等的。
任何指针都可以被赋给void指针,他可以被转换为原来的指针类型,这样,指针的值和原指针的值是相等的。
int num=1;
int *pi = (int*)#
printf("pi内容:%p,\tpi指向的内容:%d\n", pi, *pi);
void *pv = pi;
pi = (int*)pv;
printf("pi内容:%p,\tpi指向的内容:%d\n", pi, *pi);
void指针只能用作数据指针,不能用作函数指针。
sizeof操作符可以用在void指针上,不过不能用在void上。下例中size_t是用来表示长度的数据类型。
size_t size = sizeof(void*);
printf("size=%d\n", size);
size_t size = sizeof(void);//不合法
- 全局和静态指针
指针被声明为全局或静态,就会在程序启动时被初始化为NULL,下图说明了内存布局。栈帧被推入栈中,堆用来动态分配内存,堆上面的区域用来存放全局或静态变量。
int *globalpi;
void foo()
{
static int *staticpi;
}
int mian()
{
return 0;
}