指针

C Primer Plus(总结)
*(解引用) &(地址运算符)
1、从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象),正如char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。
2、数组名是数组首元素的地址。也就是说,如果flizny是一个数组,下面的语句成立:
flizny == &flizny[0]; // 数组名是该数组首元素的地址
3、至于C语言,ar[i]和*(ar+1)这两个表达式都是等价的。无论ar是数组名
还是指针变量,这两个表达式都没问题。但是,只有当ar是指针变量时,才
能使用ar++这样的表达式
4、如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。例如,sum()函数的原型和定义如下:
int sum(const int ar[], int n); /* 函数原型 */
以上代码中的const告诉编译器,该函数不能修改ar指向的数组中的内容
5、
double * const pc = rates;//表示不能让指针指向别的地方
const double * pc = rates//表示不能修改指针所指向的地方的值
const double * const pc = rates;// 该指针既不能更改它所指
向的地址,也不能修改指向地址上的值
6、指针和多维数组有什么关系?为什么要了解它们的关系?处理多维数组
的函数要用到指针,所以在使用这种函数之前,先要更深入地学习指针。至
于第 1 个问题,我们通过几个示例来回答。为简化讨论,我们使用较小的数
组。假设有下面的声明:
int zippo[4][2]; /* 内含int数组的数组 */
然后数组名zippo是该数组首元素的地址。在本例中,zippo的首元素是
一个内含两个int值的数组,所以zippo是这个内含两个int值的数组的地址。
下面,我们从指针的属性进一步分析。
因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同。
而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素
(一个整数)的地址(即&zippo[0][0]的值)相同。简而言之,zippo[0]是一
个占用一个int大小对象的地址,而zippo是一个占用两个int大小对象的地
址。由于这个整数和内含两个整数的数组都开始于同一个地址,所以zippo
和zippo[0]的值相同。
给指针或地址加1,其值会增加对应类型大小的数值。在这方面,zippo
和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的
对象只占用一个int大小。因此, zippo + 1和zippo[0] + 1的值不同。
解引用一个指针(在指针前使用*运算符)或在数组名后使用带下标的
[]运算符,得到引用对象代表的值。因为zippo[0]是该数组首元素(zippo[0]
[0])的地址,所以*(zippo[0])表示储存在zippo[0][0]上的值(即一个int类型
的值)。与此类似,*zippo代表该数组首元素(zippo[0])的值,但是
zippo[0]本身是一个int类型值的地址。该值的地址是&zippo[0][0],所以
*zippo就是&zippo[0][0]。对两个表达式应用解引用运算符表明,**zippo与
*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。简而言之,
zippo是地址的地址,必须解引用两次才能获得原始值。地址的地址或指针
的指针是就是双重间接(double indirection)的例子。

#include<stdio.h>
int main(void) {
  int zippo[4][2] = { {2, 4}, {6, 8}, {1, 3}, {5, 7} };
  printf(" zippo = %p, zippo + 1 = %p\n", zippo, zippo + 1);
  printf("zippo[0] = %p, zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1);
  printf(" *zippo = %p, *zippo + 1 = %p\n", *zippo, *zippo + 1);
  printf("zippo[0][0] = %d\n", zippo[0][0]);
  printf(" *zippo[0] = %d\n", *zippo[0]);
  printf(" **zippo = %d\n", **zippo);
  printf(" zippo[2][1] = %d\n", zippo[2][1]);
  printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo + 2) + 1));
  return 0;
}

7、指向多维数组的指针
int (* pz)[2]; // pz指向一个内含两个int类型值的数组
一般而言,多重解引用让人费解。例如,考虑下面的代码:
int x = 20;
const int y = 23;
int * p1 = &x;
const int * p2 = &y;
const int ** pp2;
p1 = p2;   // 不安全 – 把const指针赋给非const指针
p2 = p1;   // 有效 – 把非const指针赋给const指针
pp2 = &p1;  // 不安全 –- 嵌套指针类型赋值
前面提到过,把const指针赋给非const指针不安全,因为这样可以使用
新的指针改变const指针指向的数据。编译器在编译代码时,可能会给出警
告,执行这样的代码是未定义的。但是把非const指针赋给const指针没问
题,前提是只进行一级解引用:
p2 = p1; // 有效 – 把非const指针赋给const指针
但是进行两级解引用时,这样的赋值也不安全,例如,考虑下面的代
码:
const int **pp2;
int *p1;
const int n = 13;
pp2 = &p1;  // 允许,但是这导致const限定符失效(根据第1行代码,
不能通过*pp2修改它所指向的内容)
*pp2 = &n;  // 有效,两者都声明为const,但是这将导致p1指向
n(*pp2已被修改)

*p1 = 10;//有效,但是这将改变n的值(但是根据第3行代码,不能修改n
的值)
发生了什么?如前所示,标准规定了通过非const指针更改const数据是
未定义的。例如,在Terminal中(OS X对底层UNIX系统的访问)使用gcc编
译包含以上代码的小程序,导致n最终的值是13,但是在相同系统下使用
clang来编译,n最终的值是10。两个编译器都给出指针类型不兼容的警告。
当然,可以忽略这些警告,但是最好不要相信该程序运行的结果,这些结果
都是未定义的

#include<stdio.h>
int main(void) {
  const int **pp2;
  int *p1;
  const int n = 13;
  pp2 = &p1;
  *pp2 = &n;
  *p1 = 10;
  printf("%d\n", n);
  return 0;
}
#include<stdio.h>
int main(void) {
  int (*p)[2]; 
  int zippo[4][2] = { { 2, 4 }, { 6, 8 }, { 1, 3 }, {5, 7 } };
  p = zippo;
  printf("%d\n", **p);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值