《C和C++代码精粹》——2.2 基础

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

本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接

时间: 2024-11-08 19:53:02

《C和C++代码精粹》——2.2 基础的相关文章

《C和C++代码精粹》导读

前言 C和C++代码精粹 本书适合于那些C和C++的职业程序员.假如你已熟悉这两种语言的语法和基本结构,这本书能够为你创建有效的.实用的程序提供实践性的指导.每一个代码范例或程序范例均标明行之有效的用法和技术,这些用法和技术对C/C++这两种重要编程语言的性能发挥起着重要的作用. 对于那些希望在工作中加强自身技术和提高效率的人来说,本书可以算是一本经验之谈.尽管目前人们对面向对象模式的推崇到了白热状态(本书也包括这方面的丰富内容),可是我没有理由不对C++的基础-C表示尊崇.我发现太多的程序开发

《C和C++代码精粹》——第 1 章 更好的C1.1 两种语言简述

第 1 章 更好的C C和C++代码精粹本文仅用于学习和交流目的,不代表异步社区观点.非商业转载请注明作译者.出处,并保留本文的原始链接. 1.1 两种语言简述 C和C++代码精粹20世纪80年代初期,C++起源于AT&T,称为带类的 C,当时Bjarne Stroustrup试图用Simula-67编写仿真程序."类"在Simula中是表示用户定义类型的术语,编写好的仿真程序的关键是能够定义对象反映现实世界.除了把类加到C中使其成为最快的过程语言外,还有什么更好的方法可以得到

在HTML中插入JavaScript代码的示例_基础知识

 在HTML文档中的任何地方可包括JavaScript代码.但也有以下的最佳方法来包含JavaScript在HTML文件.     在 <head>...</head> 部分.     在 <body>...</body> 部分.     在<body>...</body> 和<head>...</head> 部分.     脚本和外部文件,然后包括在<head>... </ head>

《C和C++代码精粹》——2.11 更高深的内容

2.11 更高深的内容 C和C++代码精粹 我们可以很自然地得出以下结论:一个三维数组是二维数组的集合. int a[2] [3] [4]={{{0,1,2,3},{4,5,6,7},{8,9,0,1}}, {{2,3,4,5},{6,7,8,9},{0,1,2,3}}}; 这个数组的第一个元素是一个"二维数组" a[0](从技术上来说,a[0]是一个由3个含有4个整数的数组的数组),为了使指针与数组名a一致,定义: int (*p) [3] [4] = a; 程序清单2.16是一个应

《C和C++代码精粹》——2.7 指针和一维数组

2.7 指针和一维数组 C和C++代码精粹 在程序清单2.7中,会注意到在传递数组 s 时并没有使用它的地址,这是因为C和C++在大多数表达式中把数组名转换成指向它第一个元素的指针.自1984年以来,我已经向成百上千的学生讲授了C和C++,我注意到了指针和数组,特别是指针和多维数组之间的关系造成很多迷惑. 这样说似乎很奇怪,但是C++确实不支持数组,至少C++不像支持第一类数据类型如整型或者甚至结构体那样支持数组.考虑以下的语句: int i=1,j; int a[4]={0,1,2,3},b[

《C和C++代码精粹》——1.10 操纵器

1.10 操纵器 C和C++代码精粹 当标识符 endl出现在一个输出流中时,一个换行字符就被插入并且流被刷新.标识符endl是操纵器的一个例子,即为了副效应而插入到流的一个对象.在〈iostream〉中被声明的系统预定义的操纵器列于表 1.3中.程序清单1.11里的程序在功能上与程序清单1.10的程序等价,但它是用操纵器来代替显式调用setf函数.操纵器经常可以使代码更为高效. 表1.3 简单的操纵器(〈iostream〉) 程序清单1.11 用操纵器改变数据基数 // base2.cpp:

《C和C++代码精粹》——2.5 普通指针

2.5 普通指针 C和C++代码精粹 通常编写能接收指向任意类型参数的函数是很方便的.这是很有必要的,例如,用标准的库函数memcpy,能够从一个地址向另一个地址拷贝一块内存.你也可能想调用memcpy来拷贝自己创建的结构: struct mystruct a,b; /.../ memcpy(&a,&b,sizeof(struct mystruct)); 为了操作任意类型的指针,memcpy把它头两个参数声明为void型指针.可以不需要强制类型转换将任何类型的指针赋予void类型.也可以在

《C和C++代码精粹》——1.2 循序渐进

1.2 循序渐进 C和C++代码精粹 在没有完全掌握C++的情况下也可以有效地使用它.事实上,面向对象技术承诺如果供应商为重用.可扩展性提供设计好的类库,那么建立应用程序的工作就很容易了.现有的开发环境,及其应用程序框架和可视化组件,正在兑现这一承诺. 如果觉得必须要掌握这种语言,可以一步步地去做,并且在这一过程中可以取得丰硕的成果.已出现的3个"顶峰"是: 1.更好的C: 2.数据抽象: 3.面向对象编程. 由于C++比C更安全.更富于表达,所以可以将它作为一个更好的C使用.这个顶峰

《C和C++代码精粹》——2.12 指向函数的指针

2.12 指向函数的指针 C和C++代码精粹 一个指针可以指向函数也可以指向存储的对象.下面的语句声明fp是一个指向返回值为整型(int)的函数的指针: int(*fp)( ); *ftp的圆括号是必需的,没有它的语句 int *fp( ); 将fp声明为一个返回指向整型(int)指针的函数.这就是将星号与类型声明紧密相连的方式成为逐渐受人们欢迎的方式的原因之一. int fp(); //方式说明fp()返回一个指向整型的指针(int ) 当然,这种方式建议你通常应该每条语句只声明一个实体,否则