第五章:指针与数组
定义
- 指针是保存变量地址的变量
通用指针类型
- void*
初始化
- 初始值只能是0或者表示地址的表达式,对于后者必须是在此前已经定义的具有适当类型的数据的地址
注意
- 指针与整数之间不能相互转换,但是0是唯一的的例外:常量0可以赋值给指针,指针也可以和常量0进行比较,程序中经常使用符号常量NULL代替常量0,这样便于更清晰地说明常量0是一个特殊值(NULL定义在stddef.h中)
- 指向相同数组的元素的指针之间的算术或比较运算才有意义,但是指向不同数组的元素的之间的指针的算术或比较运算没有定义,(特列,指针的算术运算中可使用数组最后一个元素的下一个元素的地址)
指针与地址
-
机器的一个字节可以存放一个char类型的数据两个相邻的字节可以存储一个short 4个可存储一个long
而指针通常占用连续的两个或四个字节 -
取地址操作符:&
- &只能用于内存中的对象,即变量和数组元素,不能作用于表达式、常量或者register类型的变量
-
间接寻址或间接引用运算符 *
-
作用于指针则访问指针指向的对象
-
注意运算符的优先级和结合顺序
- ++*p 一元运算符从右到左结合
(*p)++ 需要加括号才是对指针p指向的对象进行++
- ++*p 一元运算符从右到左结合
-
指针与函数参数
- 在函数定义中,形式参数 char s[] 和 cahr *s是等价的,更习惯使用后一种形式
指针与数组
-
数组
- 数组类型的变量或者表达式的值是该数组第0个元素的地址
- 在计算数组元素a[i]的值时,C语言实际上先将其转换为*(a+i)的形式,然后再进行求值
- 如果p是一个指针,那么也可以在它的后面加下标,p[i]和*(p+i)是等价的,简而言之,一个通过数组和下标实现的表达式可等价的通过指针和偏移量实现
-
指针和数组的不同之处
- 指针是变量,因此,p=a ;p++都是合法的,但是数组名不是变量,上述的操作是非法的
地址算术运算
-
对于指针p,p+i每次移动的距离取决于p的类型
-
合法的指针运算
- 相同类型之间的赋值运算、指针同整数之间的加法减法运算、指向相同数组中元素的两个指针之间的减法或比较运算、将指针赋值为0或指针与0之间的比较运算、不同类型的指针但是其中一个指针是void*类型的情况进行赋值也行
字符指针与函数
- 字符数组:char amessage[] = “nw is the time”;
字符指针:char *pmessage = “now is the time”;
amessage中的单个字符可以修改,但是amessage始终指向的是同一个位置
pmessage指向一个字符串常量,指向的地址可以修改,但是修改字符串的内容,结果是没有定义的(人话就是不行)
指针数组以及指向指针的指针
多维数组
- 一般来说,除了数组的第一维下标可以不指定大小外,其余各维都必须明确指定大小
指针数组的初始化
- char *name[] = {“Illegal month”,“January”, “February”, “March”,“April”, “May”, “June”,“July”, “August”, “September”,“October”, “November”, “December”}; //TODO 二维数组存这样的数据做对比
指针与多维数组
-
int a[10][20];
int *b[10];- 那么,从语法角度讲,a[3][4]和 b[3][4]都是对一个int 对象的合法引用。但a是一个真正的二维数组,它分配了200个int类型长度的存储空间,并且通过常规的矩阵下标计算公式20xrow+col (其中,row表示行,col 表示列)计算得到元素a [row] [col]的位置。但是,对b来说,该定义仅仅分配了10个指针,并且没有对它们初始化,它们的初始化必须以显式的方式进行,比如静态初始化或通过代码初始化。假定b的每个元素都指向一个具有20个元素的数组,那么编译器就要为它分配200个int类型长度的存储空间以及10个指针的存储空间。指针数组的一个重要优点在于,数组的每一行长度可以不同,也就是说,b的每个元素不必都指向一个具有20个元素的向量,某些元素可以指向具有2个元素的向量,某些元素可以指向具有50个元素的向量,而某些元素可以不指向任何向量。
命令行参数
-
argc
-
argv
- 按照C语言的约定,argv[0]的值是启动该程序的程序名,因此,argc的值至少为1
- ANSI标准要求,argv[argc]的值必须为一空指针
指向函数的指针
- C语言中,函数本身不是变量,但是可以定义指向函数的指针。这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等
- 由于任何类型的指针都可以转换为void类型,并且在将它转换回原来的类型时不会丢失信息,所以,调用函数时可以将参数强制转换为void类型。比较函数的参数也要执行这种类型的转换。这种转换通常不会影响到数据的实际表示,但要确保编译器不会报错
- int (*comp) (void *,void *)
它表明comp是一个指向函数的指针,该函数具有两个void *类型的参数,其返回值类型为int
(*comp)(v[i], v[left])
上述语句是对函数指针comp的调用