《C程序设计语言(第2版·新版)》第5章 指针与数组

  指针:保存变量地址的变量;在 C中应用广泛,因为1)指针往往是表达某个计算的唯一途径;2)使用指针往往可以写出更高效紧凑的代码;另一方面,1)指针和goto一样,会导致程序难以理解;2)粗心会很容易导致指针指向了错误的地方;

  指针与数组关系密切;
  ANSI C明确制定了操纵指针的规则,并且使用void*代替char*作为通用指针的类型;

5.1 指针与地址

  内存的组织:一系列连续编号或编址的存储单元,单元可以单个操纵也可以以连续成组的方式操纵;通常1个字节可以存放一个char,2个相邻字节可以存放一个short,4个相邻字节可以存放一个long;
 
  指针:能够存放一个地址的一组存储单元,通常是2个或4个字节;例如:
int *p; //这种写法表明*p的结果是int型;
double atof(char *); //函数的值和参数都是指针;
 
  地址运算符&:p=&c; // 把c的地址赋给指针变量p;&只能作用于内存中的对象(即变量与数组元素),不能作用于表达式、常量或register类型的量;
  间接寻址或间接引用运算符*:作用于指针,访问它所指向的对象。例如:
px=&x; *px=0; //x的值变为0
  出现x的地方都可以使用*xp,例如:
*px+=1; 等价于 ++*px; 等价于 (*px)++; //一元运算符遵循从右至左的结合顺序,故须加圆括号;
 
  指针只能指向特定类型对象(例外:指向void类型的指针可以存放指向任何类型的指针,但它不能间接引用其自身);

5.2 指针与函数参数

  C以传值的形式将参数值传递给被调用函数,所以被调用函数不能直接修改主调函数中的变量值。所以void swap(int x, int y)是错误的,void swap(int *px, int *py)才能正确交换两个元素; 调用:swap(&a, &b);

5.3 指针与数组

  通过数组下标所能完成的任何操作都可以通过指针式实现,并且一般来说后者执行更快,不过会增加理解难度;
  声明 int a[10];定义了一个数组,其十个元素储存在相邻内存区域中;
  pa=&a[0];等价于pa=a; //数组名就是该数组第一个元素的地址,
  引用数组元素a[i]等价于*(a+i); &a[i]等价于a+i;相应地指针pa也可带下标pa[i],等价于*(pa+i);即:数组和偏移量实现的表达式可等价地用指针加偏移量实现;
  数组名和指针有一处不同:指针是变量而数组名不是,所以pa=a;和pa++;是对的,而a=pa;和a++;是错的;
  ??(It)数组名传给函数时实际上传的是它的首元素地址,该参数是一个局部变量,因此数组名参数必须是一个指针;该指针在该函数中参与运算,并不会影响指针实参在主调函数中的值;
  定义函数时,形参 char a[];和char *a是等价的,习惯用后者;数组名传给函数,函数会自动判断按照指针还是数组处理,在函数中甚至可以同时使用数组和指针这两种方式;
  可以把非首地址(例如子数组的首地址)传递给函数:f(&a[i])等价于f(a+i);

5.4 地址算术运算

  c语言的一大优点:将指针、数组和地址的算术运算集成在一起;
  有效的指针运算包括四类:
A. 相同类型指针之间的赋值或初始化;
B. 指针同整数进行加减法(按照指针所指向对象的长度按比例缩放);
C. 指向同一数组中元素的两个指针间的减法或比较运算(特例:指针算术运算可使用数组最后元素的下一个地址);
D. (特例)可将指针赋值为0或与0比较(常用<stddef.h>中的符号常量NULL代替0,以说明0是指针的一个特殊值)

5.5 字符指针与函数

  字符串常量是一个字符数组(内部表示中以'\0'结尾),可通过一个指向其首元素的字符指针来访问;
char *pmessage="good bye";//定义了一个指针,初值指向字符串常量,可被修改,但其修改字符串内容未定义
char amessage[]="good bye";//定义了一个数组,其单个元素可修改,但amessage始终指向同一地址
C语言没有提供将整个字符串作为一个整体进行处理的运算符;
  <string.h>中包含strcpy,strcmp等字符串处理函数
 
  *t++的值是指针t执行自增运算前t所指向的字符,后缀++表示在读取该字符之后才改变t的值;(it:如前所述,一元运算符从右至左结合,所以t先结合++然后才结合*,即*(t++),但是++的功效是在使用了t之后才给t自增,已验证无误)
 
  进栈和出栈的标准用法:
*p++=val; //将val压入栈中
val=*--p; //将栈顶元素弹出到val中

5.6 指针数组以及指向指针的指针

  char *lineptr[MAXLINES]; 是一个指针数组(例中用来存储待排序文本行的首字符地址),其中lineptr[i]是字符指针;又lineptr是一个数组名,指向其第一个元素*lineptr,该元素也是一个指针,指向第一个文本行的首字符;

5.7 多维数组

  类似矩阵,但使用不如指针数组广泛;
  C语言中二维数组实际是一种特殊的一维数组,它的每个元素也是一个一维数组;按行存储
char daytab[2][13]={{...},{...}};//定义一个二维数组,daytab[i][j]可引用元素
  如果二维数组作为参数产地给函数,那么在函数声明中必须指定数组列数,行数无所谓:因为函数调用传递的是一个指针;以下三种都可以:
A. f(int daytab[2][13]){...}
B.f(int daytab[][13]){...} //一般说来,除数组的第一维(下标)可以不指定大小外,其余各维都必须明确指定大小;
C.f(int (*daytab)[13]){...} //因行数无关紧要,这种声明表明参数是一个指针,指向具有13个整型量的一维数组;若去掉括号:
int *daytab[13] //相当于声明了一个有13个整型指针元素的一维数组;

5.8 指针数组的初始化

  例如字符串数组
char *month_name[]={"Illegal month","January","February", ...}; //编译时会自动统计初值个数并填入数组长度;

5.9 指针与多维数组

  很容易混淆指针数组与二维数组之间的区别:
int a[10][20]; //分配了200个int长度的存储空间,通过20*i+j计算a[i][j]的位置;
int *b[10]; //仅仅分配了10个指针且没有初始化,每个指针都可以指向一个数组;具有重要优点:可以指向不同长度的数组(也可以不指向任何对象);
  指针数组最常用的还是用来存放不同长度的字符串,如5.8中例;

5.10 命令行参数

  C语言可以在程序开始执行时将命令行参数传递给程序;调用main时它带有两个参数:
argc(用于计数,表示运行程序时命令行中参数的数目);
argv(用于参数向量,一个指向字符串数组的指针,每个字符串对应一个参数;通常用多级指针处理这些字符串);
  约定:argv[0]的值是启动该程序的程序名,故argc至少是1;可选参数argv[1]~arf[argc-1];ANSI要求argv[argc]必须为一空指针;(空指针:未指向任意地点或者指向NULL(整数0)的指针;void*是无确定类型指针);
 
... ... //p100-101未看

5.11 指向函数的指针

  函数本身不是变量,但是可以定义指向函数的指针;它可以被赋值、存放于数组、传递给函数以及作为函数的返回值等等;
  排序程序通常包含3部分:判断两个对象次序的比较操作;颠倒对象次序的交换操作;用于比较和交换直到排序正确的算法。排序算法与比较和交换操作无关,所以在排序算法中调用不同的比较和交换函数,即可按照不同的标准排序。
 
  int (*comp)(void *, void *) 表明comp是一个指向函数的指针,*comp代表一个函数;调用方法:
(*comp)(v[i], v[left])
其中圆括号是必须的以保证各个部分正确结合;没有括号的形式:
int *comp(void *, void *)
表明comp是一个返回int型指针的函数;

5.12 复杂声明

  C语言常因为声明(尤其函数指针)的语法问题收到批评;C的语法力图使声明和使用相一致;
  因为c的声明不能从左往右读并且使用了太多圆括号,所以情况较复杂时容易混淆;如5.11例;
  实际中很少使用复杂声明;使用typedef通过简单步骤合成,可以创建复杂声明;
 
... ... //p106-109未看,讲用程序将声明转换为文字描述,以及相反过程;

 

posted @ 2015-09-17 21:39  fFaXzz  阅读(285)  评论(0编辑  收藏  举报