深入理解C指针1.4  指针的常见用法_深入理解C指针1.4  指针的常见用法试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > 深入理解C指针 > 1.4  指针的常见用法

深入理解C指针——1.4  指针的常见用法

指针用处很多。在本节中,我们探讨指针的不同用法,包括: • 多层间接引用; • 常量指针。 1.4.1  多层间接引用 指针可以用不同的间接引用层级。把变量声明为指针的指针并不少见,有时候称它们为双重指针。一个很好的例子就是用传统的argv 和argc 参数来给main 函数传递程序参数,第5 章将详细讨论。 下例使用了三个数组。第一个数组是用来存储书名列表的字符串数组: char *titles[] = {"A Tale of Two Cities", "Wuthering Heights","Don Quixote", "Odyssey","Moby-Dick","Hamlet", "Gulliver's Travels"}; 还有两个数组分别用来维护一个“畅销书”列表和一个英文书列表。这两个数组保存的是titles 数组里书名的地址,而不是书名的副本。两个数组都声明为字符指针的指针。数组元素会保存titles 数组中元素的地址,这样可以避免对每个书名重复分配内存,确保每个书名的位置唯一。如果需要修改书名,只改一个地方就可以了。 另外两个数组声明如下。每个数组元素包含一个指向char 指针的指针。 char **bestBooks[3]; char **englishBooks[4]; 接下来初始化这两个数组,然后打印其中一个元素,如下所示。在赋值语句中,右边的值是通过先做下标索引再取地址的操作得到的。比如说第二个语句把titles 数组中第4 个元素的地址赋给bestBooks 的第2 个元素: bestBooks[0] = &titles[0]; bestBooks[1] = &titles[3]; bestBooks[2] = &titles[5]; englishBooks[0] = &titles[0]; englishBooks[1] = &titles[1]; englishBooks[2] = &titles[5]; englishBooks[3] = &titles[6]; printf("%sn",*englishBooks[1]); // Wuthering Heights 本例的内存分配如图1-10 所示。 图1-10:指针的指针 用多层间接引用可以为代码的编写和使用提供更多的灵活性,否则有些操作实现起来会困难一些。在本例中,如果书名的地址变了,那么只需要修改titles 数组即可,不需要修改其他两个数组。 间接引用没有层数限制,当然,使用的层数过多会让人迷惑,很难维护。 1.4.2  常量与指针 C 语言的功能强大而丰富,还表现在const 关键字与指针的结合使用上。对不同的问题,它能提供不同的保护。特别强大和有用的是指向常量的指针。在第3 章和第5 章,我们将看到如何用这种技术来阻止函数的使用者修改函数的参数。 1. 指向常量的指针 可以将指针定义为指向常量,这意味着不能通过指针修改它所引用的值。下例声明了一个整数和一个整数常量,然后声明了一个整数指针和一个指向整数常量的指针, 并分别初始化为对应的整数: int num = 5; const int limit = 500; int *pi; // 指向整数 const int *pci; // 指向整数常量 pi = # pci = &limit; 图1-11 是它们的内存分配情况。 图1-11:指向整数常量的指针 下面的代码会打印这些变量的地址和值: printf(" num - Address: %p value: %dn",&num, num); printf("limit - Address: %p value: %dn",&limit, limit); printf(" pi - Address: %p value: %pn",&pi, pi); printf(" pci - Address: %p value: %pn",&pci, pci); 运行代码会产生类似下面的输出: num - Address: 100 value: 5 limit - Address: 104 value: 500 pi - Address: 108 value: 100 pci - Address: 112 value: 104 如果只是读取整数的值,那么引用指向常量的指针就没事,读取是完全合法的,而且也是必要的功能,如下所示: printf("%dn", *pci); 我们不能解引指向常量的指针并改变指针所引用的值,但可以改变指针。指针的值不是常量。指针可以改为引用另一个整数常量,或者普通整数。这样做不会有问题。声明只是限制我们不能通过指针来修改引用的值。 这意味着下面的赋值是合法的: pci = # 我们可以解引pci 来读取它,但不能解引它来修改它。 考虑下面的赋值语句: *pci = 200; 这会导致如下语法错误: 'pci' : you cannot assign to a variable that is const 指针认为自己指向的是整数常量,所以不允许用指针来修改这个整数。我们还是可以通过名字来修改num 变量,只是不能用pci 来修改。 理论上来说,常量的指针也可以如图1-12 那样可视化,普通方框表示变量,阴影方框表示常量。pci 指向的阴影方框不能用pci 来修改,虚线表示指针可以引用的数据类型。在上例中,pci 指向limit。 图1-12:指向常量的指针 把pci 声明为指向整数常量的指针意味着: • pci 可以被修改为指向不同的整数常量; • pci 可以被修改为指向不同的非整数常量; • 可以解引pci 以读取数据; • 不能解引pci 从而修改它指向的数据。 数据类型和const 关键字的顺序不重要。下面两个语句是等价的: const int *pci; int const *pci; 2. 指向非常量的常量指针 也可以声明一个指向非常量的常量指针。这么做意味着指针不可变,但是它指向的数据可变。下面是这种指针的例子; int num; int *const cpi = # 有了这个声明: • cpi 必须被初始化为指向非常量变量; • cpi 不能被修改; • cpi 指向的数据可以被修改。 从原理上说,这类指针可以用图1-13 来说明。 图1-13:指向非常量的常量指针 无论cpi 引用了什么,都可以解引cpi 然后赋一个新值。下面是两条合法的赋值语句: *cpi = limit; *cpi = 25; 然而,如果我们试图把cpi 初始化为指向常量limit,如下所示: const int limit = 500; int *const cpi = &limit; 那么就会产生一个警告: warning: initialization discards qualifiers from pointer target type 如果这里cpi 引用了常量limit,那常量就可以修改了。这样不对,因为常量是不能被修改的。 在把地址赋给cpi 之后,就不能像下面这样再赋给它一个新值了: int num; int age; int *const cpi = # cpi = &age; 如果采用这种做法会产生如下错误信息: 'cpi' : you cannot assign to a variable that is const 3. 指向常量的常量指针 指向常量的常量指针很少派上用场。这种指针本身不能修改,它指向的数据也不能通过它来修改。下面是指向常量的常量指针的一个例子: const int * const cpci = &limit; 指向常量的常量指针可以用图1-14 来说明。 图1-14:指向常量的常量指针 与指向常量的指针类似,不一定只能将常量的地址赋给cpci。如下所示,我们其实还可以把num 的地址赋给cpci: int num; const int * const cpci = # 声明指针时必须进行初始化。如果像下面这样不进行初始化: const int * const cpci; 就会产生如下语法错误: 'cpci' : const object must be initialized if not extern 对于指向常量的常量指针,我们不能: • 修改指针; • 修改指针指向的数据。 重新赋给cpci 一个新地址: cpci = # 会导致如下语法错误: 'cpci' : you cannot assign to a variable that is const 像下面这样试图解引指针并赋新值: *cpci = 25; 会产生如下错误: 'cpci' : you cannot assign to a variable that is const expression must be a modifiable lvalue 不过,指向常量的常量指针很少用到。 4. 指向“指向常量的常量指针”的指针 指向常量的指针也可以有多层间接引用。在下例中,我们声明一个指向上一节提到的cpci 指针的指针。从右往左读可以帮助我们理解这个声明: const int * const cpci = &limit; const int * const * pcpci; 指向“指向常量的常量指针”的指针可以用图1-15 来说明。 图1-15:指向“指向常量的常量指针”的指针 下面说明它们的使用。这段代码的输出应该是两个500: printf("%dn",*cpci); pcpci = &cpci; printf("%dn",**pcpci); 下表总结了本节讨论的前四种指针。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《深入理解C指针》其他试读目录

• 1.1  指针和内存
• 1.2  指针的长度和类型
• 1.3  指针操作符
• 1.4  指针的常见用法 [当前]
• 1.5  小结