2.2 基础
C和C++代码精粹
除了寄存器变量以外,程序中的所有对象都存储在内存中的某处,“某处”有一个地址,在开发平台上内存的每个字节的序号按顺序从0开始,简单地说,地址就是一个字节的顺序号。下面的程序说明了如何找到程序变量的地址:
// address.cpp
#include <cstdio>
#include <iostream>
using namespace std;
main()
{
int i = 7, j = 8;
printf("i == %d, &i == %p\n",i,&i);
cout << "j == " << j << "&j == " << &j << endl;
}
/* 输出:
i == 7, &i == 0012FF88
j == 8, &j == 0x0012ff84
*/
单目运算符&返回一个数据对象的地址。%p格式控制符根据编译器格式的不同来显示地址,通常是十六进制形式。本书所有的例子中,整数和地址都是32位(你的输出可能有所不同)的。
在上面的程序中i和j的内存分布如下:
i和j在内存中碰巧相邻存储这一点并不重要(在某些结构中因对齐的需要而在对象之间存在间隔),注意我的编译器在内存中把i分配在j之后(即i的地址比j的高),这同样不重要。计算机在实际中如何存储一个数的各个位也是随系统而定的,事实上,在PC机中,i中的7不是真正存储在i所占内存的最右端。我们可以假设多数情况不是这样的,因为,不管这些位在物理上如何排列,7在逻辑上都是0X00000007。
指针只不过是一个容纳另一个程序实体地址的变量。在大多数情况下,我们不关心一个地址的实际数值,通常只是用它引用感兴趣的对象,程序清单2.1中的程序说明了指针的使用。由于指针总是指向某种类型的对象,因此被引用类型通常必须出现在声明中,这样,我们说“指向整型”或者“指向字符型”,等等。声明
int *ip;
说明了*ip是一个整型变量,因此ip是一个指向整型变量的指针。在声明语句之外的表达式中如果指针变量前有星号,结果表示指针变量所指向变量的值。通过指针间接地指向内存的过程叫做间接访问或复引用。它可以出现在赋值语句的任何一边。因此在程序清单2.1中的声明,语句
*ip =9;
等效于
i=9;
程序清单2.1 说明指针和间接访问
// pointer.cpp
#include <iostream>
using namespace std;
main()
{
int i = 7, j = 8;
int* ip = &i;
int* jp = &j;
cout << "Address " << ip
<< " contains " << *ip << endl;
cout << "Address " << jp
<< " contains " << *jp << endl;
*ip = 9;
cout << "Now Address " << ip
<< " contains " << i << endl;
*jp = 10;
cout << "Now Address " << jp
<< " contains " << j << endl;
}
//输出:
Address 0x0012ff88 contains 7
Address 0x0012ff84 contains 8
Now Address 0x0012ff88 contains 9
Now Address 0x0012ff84 contains 10
如果在定义指针时没有说明被引用的类型,表达式*ip将没有意义,间接访问也是不可能的。任何时候都不要忘记指针不仅仅是指向内存中的某处,它还指向某种类型的实体。这个规则的唯一例外是指针不指向任何地方的时候。当给一个指针赋特殊的值0时,就会发生这种情况(这时的指针叫作空指针,在C中由宏NULL表示)。不能复引用一个空指针,只能将它与其他指针做比较。
程序清单2.1的内存分布是:
尽管,地址通常以数字的形式出现,但是不要假设指针类型和整型数据类型之间存在任何关系。指针是一种独特的数据类型并且应该作为一种独特的数据类型来看待。只能对指针进行如下操作:
1.在指针中存储和从其中读取被引用类型的对象的地址;
2.改变或读取该地址中的内容(间接访问);
3.在指针上加或减一个整数(仅限在数组中使用);
4.与另一个指针相减或做比较(当两个指针都指向同一数组时);
5.给指针赋值或把它和空指针做比较;
6.作为参数传递给函数,该函数期望一个指向引用类型的指针作为参数。
由于对象的相应内存位置是不重要的(当然,数组元素除外),通常描述内存的逻辑分布更好,如:
通常说法是“ip指向i”或“jp指向j”。
指针的概念如此简单以至于初学者往往由于要寻找星号之外的意义而使他们陷于紧张不安之中。如果想避免挫折和困惑而浪费没有必要的时间,只需记住:
重要的指针原则1:指针是一个地址
注意,上面所说的是“指针是一个地址”而不是“指针保存一个地址”,当然,两种说法都是正确的。指针是地址就像整型是整数一样。人们经常不说整型“保存”一个整数。我仅仅想强调的是当使用指针时要想到它是“地址”,还有什么更简单的说法呢?
由于所有的对象都有一个地址,因此可以定义指向任何类型对象的指针,包括另一个指针对象。程序清单2.2中的程序说明了如何定义指向整型指针的指针。这个程序的拓扑结构是:
如果ipp是指向一个指向整型变量指针的指针,那么ipp是该指针所指向的指针,为了最终得到i中的整数7,需要另一级别的间接访问,即表达式*ipp,换句话说:
**ipp==*ip==i; //事实上,全部指向同一对象
程序清单2.2 说明指向指针的指针
/* ptr2ptr.cpp: 指向指针的指针*/
#include <iostream>
using namespace std;
main()
{
int i = 7;
int* ip = &i;
int** ipp = &ip;
cout << "Address " << ip << " contains " << *ip << endl;
cout << "Address " << ipp << " contains " << *ipp << endl;
cout << "**ipp == " << **ipp << endl;
}
//输出:
Address 0x0012ff88 contains 7
Address 0x0012ff84 contains 0x0012ff88
**ipp == 7
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接