1. 内存是如何编址的?
2. 如何对变量进行寻址操作?
(1)直接寻址:
[例]
#include<stdio.h>
int main()
{
int a = 100;
/* 故意不加取地址操作符 */
scanf("%d", a);
printf("a=%d.\n", a);
/* */
return 0;
}
[分析]如果不在变量a前加入取地址符号&, 编译器会将当前变量a存储的值当作地址来解释, 加入变量a的值当前为100, 那么用户从键盘输入的整数就会从地址100开始写入内存: 而地址为100的存储单元有可能是一个只读存储单元, 该单元不允许用户对其进行写操作, 因此程序会就此终止.
(2)间接寻址:
[注]C语言提供了一种特殊的数据类型——指针类型, 用于存放变量的地址.
我们只知道上图中灰色区域能保存一个32位地址数据还是不够的, 我们还要知道这片存储区域中存储的地址究竟指向哪里, 这就需要我们对这片存储区域进行初始化操作.
int *pa; pa=&a; 这两条语句还可以用下述两条语句替代: (在定义指针变量pa时同时为其初始化)
[注]在冯·诺依曼体系结构中, 程序代码和数据都是以二进制形式存储的: 内存中存储的数据都是二进制数据, 对于内存中存储的内容而言, 我们认为它是什么它就是什么.
在给指针变量进行赋值操作后, 这个指针变量就指向内存中的某个地址.
我们需要通过指针变量的基类型来确定该指针变量所指数据的数据类型.
一般地, 如果某个指针变量的值是另外一个变量的地址, 那么我们就称这个指针变量是其所指向变量的指针. 换句话说, 指向某个变量的指针变量就是这个变量的指针. 值得注意的是, 虽然指针变量中存储着某个变量的地址(二者在数值上相等), 但在概念上指针变量和该指针变量所指的地址不是完全等同的: 变量的地址是一个常量(不能对其进行赋值操作), 而变量的指针是一个变量, 其值是可以改变的. 改变了指针变量的值, 也就相当于改变了指针变量的指向.
下面我们以一个简单的程序熟悉指针的基本操作.
#include<stdio.h>
int main()
{
int a = 0;
int *pa = &a;
/* */
printf("a = %d.\n", a);
printf("&a = %p.\n", &a);
printf("pa = %p.\n", pa);
/* */
return 0;
}
[注]在不同计算机上运行上述程序, 得到的地址一般是不同的.
3. 使用未初始化的指针会怎样?
使用未初始化的指针而导致的后果是无法预知的, 这是因为未初始化的指针变量是一个随机值, 我们无法预知这个指针究竟指向哪里. 如此操作会为程序的运行带来潜在的风险, 严重时可导致程序崩溃——所以在使用指针变量前必须对其执行初始化操作.
#include<stdio.h>
int main()
{
int a = 0;
int *pa;
/* */
printf("a = %d.\n", a);
printf("&a = %p.\n", &a);
printf("pa = %p.\n", pa);
/* */
return 0;
}
[注]如果实在不知道该让指针变量指向哪里, 就让它指向NULL(在stdio.h中定义为0).
4. NULL为何物?
问题: p = 0 和 p = NULL有何区别?
问题: 空指针就是指向地址为0的存储单元的指针吗?
5. 如何访问指针变量指向的存储单元中的数据?
#include<stdio.h>
int main()
{
int a = 0;
int *pa = &a;
/* */
*pa = 1;
/* 直接寻址 */
printf("a = %d.\n", a);
/* 间接寻址 */
printf("*p = %d.\n", *pa);
/* */
return 0;
}
我们再来看一个稍微复杂的程序.
#include<stdio.h>
int main()
{
int a = 0, b = 1;
int *pa, *pb;
/* */
pa = &a;
pb = &b;
/* */
printf("a = %d, b = %d.\n", a, b);/* 直接寻址 */
printf("*pa = %d, *pb = %d.\n", *pa, *pb);/* 间接寻址 */
/* */
return 0;
}