指针与数组
![珊瑚](https://img-blog.csdnimg.cn/img_convert/9734b940a12ffa367a8351593eaa5d39.jpeg)
一、指针与地址
- 内存的组织方式——地址
内存以字节为单位,可操纵单个或一组字节的存储空间进行存储数据。对内存的所有字节空间连续编址或编号,这就是内存地址。
- 指针
不同数据类型可操纵的连续字节内存空间有所不同,如字符 char 类型可操纵一个字节,可存储 256 个数二进制数。指针不能存储表达式、常量和 register 类型的变量的,只能储存变量和数组元素的地址。
- 指针的定义声明
*号最好紧靠标识符的左边,不易出错些,定义形式如下:
类型 *标识符;
指针类型与其值的类型一致。
- 指针如何获取对象地址
一元运算符“&”,可用于取对象的地址。下列赋值语句取对象地址放入指针中:
指针变量 = &要获取地址的对象;
- 指针如何使用其值
一元运算符 * 是间接寻址或间接引用运算符。通过间接寻址符访问指针的值的值,地址指向的数据。
*指针变量
这样就可访问指针的值,即地址的值了。
- 指针也是变量
同类型指针之间不必通过间接寻址符交换值(值是其他对象的地址),空类型指针比较特殊。
二、指针与函数参数
- 函数的值传递
调用函数向被调用函数的参数传递数据是复制一个值副本,参数变量装的是副本值,不会改变调用函数中对象的值,但若是复制的是对象的地址呢?情况有所不同,地址是唯一的,寻址就可间接的访问调用函数的对象了。
- 函数指针参数
定义形式如下:
类型 函数名 (类型 *对象名,类型 *对象名,…)
三、指针与数组
- 什么是数组
数组是一组有限大小的连续内存空间上的同类型数据有序集合。下标从0开始递增的,访问数组相应下标的元素。
类型 数组名[元素个数下标];
- 数组与指针
数组由一个或多个同类型并且在内存说紧邻有序排放的数据集合,使用0下标开始代表有限个数组元素。数组名存储元素0的地址。地址可进行算术运算。
指针 = 数组名[下标]或数组名,(指针 加减 指向第几个元素)——地址,(指针 加减 指向第几个元素)——寻址访问。反之指针也可像数组一样。
注意:数组不是变量,不可储存其他地址,不可自加和自减地址,而这些指针可以。数组名a,(a + i)只是对地址加i,并没有改变数组a的地址。
特别点:当参数声明为数组时,传给形参的不管数组名代表首地址,还是用取地址符取元素地址,数组作为函数参数时,参数实际上最终是指针,指针用法和数组用法都可用。就是退化为指针了,所以一般使用指针作为数组的形参。
四、地址算术运算
- 指针变量和数组的地址
地址的最小单位是字节。不同类型的数据对象可操控的字节数量,即某种数据类型的对象的长度有所不同,如一个字符型对象可操纵长度是一个字节,如超过一个字节,以对象的首地址为它的地址。
指针——指针是一个储存地址的变量,初始化的地址一般是本程序已知或0的地址,C语言保证0地址永远不是有效地址,0地址可与整数0相互转换,为清楚知道是指针,所以定义宏NULL表示地址0。
数组——形式如 “数组标识符[从0开始的N个下标]”,代表定义了地址连续的N个同类型对象,标识符加下标表示数组的各个元素对象,使用取地址符&可获得各个元素地址;数组名不加下标表示元素下标为0的地址。
- 地址的运算
- 指针的算术运算的一致性
指针变量加1,指向的地址会加到类型的整数倍,加N,加到该类型整数倍N个后的地址。所有的指针运算都会自动考虑类型的长度。
- 有效的指针运算
同类型指针之间的赋值运算;指针和整数之间的加减法运算;指向相同数组的指针之间的减法运算和比较运算;将指针赋值为0或指针与0之间的比较运算。
- 无效或非法的指针运算
除了有效运算外,其他都是非法或无效的。两个指针之间进行的加法、乘法、除法、移位运算或屏蔽运算;不同类型的两个指针,不经强制转换是不能进行赋值运算(两指针之一是void *类型除外)。
- 指针的算术运算的一致性
五、字符指针
- 字符串常量
字符串常量是一个字符数组,如,“I am a string"。字符串内部表示,是以空字符’\0’结尾,所以实际字符数要多一个。
- 字符指针
字符串常量赋值给字符指针,只是赋值字符数组的第一个元素地址(字符串常量第一个字符地址)。字符指针还可是字符串的下一个对象地址或其他字符对象地址。引用时可根据结尾符’\0’大做文章。
六、指针数组与指针的指针
- 指针的指针
指针也是变量,所以本身分配了地址空间。指针的指针,是一个变量,存的是另一个指针变量的地址。
类型 **指针名;
单目运算符*从右到左匹配,*指针名定义一个指针,后面表示储存类型是某类型的*指针。
- 指针数组
指针数组是某种类型的指针构成的数组,可存放该类型的指针元素。
数组的元素是该指针的地址,与指针的指针一样的道理,只不过存指针的是一个数组。
七、多维数组
多维数组的元素一维数组构成的,一个数组是由一层数组构成,就是二维数组、由二层数组构成就是三维数组,依次类推,一般常用一维数组和二维数组。- 声明和初始化
一般来说,数组的第一维(下标)的大小可以不指定,其他维都必须确定大小。初始化跟一维数组一样,只不过元素又是数组,所以元素初始化用子列表,就是把元素换成一对花括号。
例子:类型&bnsp;数组名[][确定大小] = {
{,},
{,},
,
…
,
{,}
}
- 多维数组作为函数参数
作为函数参数,可以写成三种形式,下面用二维数组举例:
函数返回类型 函数名(参数类型 数组名[常量][常量]){…}
函数返回类型 函数名(参数类型 数组名[][常量]) {…}
函数返回类型 函数名(参数类型 (*数组名)[常量]){…}
第三种,是声明为指向一维数组的指针,数组在某些方面互转是可以滴!这叫数组指针。
八、指针数组的初始化
指针数组的元素是指针,所以初始化列表值是一些同类型指针,如用储存字符串的的指针数组举例:char *name[] = {
"zifu",
"zifu1","zifu2",
...,
"zifumo"
};
元素是字符指针,所以指针数组实际存的是字符数组的首字符地址。
九、指针与多维数组
搞清楚指针与多维数组的关系,是很有必要的。如指针数组里面的指针与二维数组的关系与区别。二维数组的第二维大小必须指定,且分配了同类型元素指定的数量大小储存空间,是固定的。一维指针数组,没有默认数组大小,而是通过静态初始化和代码中显式初始化,他的维度可同二维数组的二维一样,等同二维数组,但也可是随意的,比如存放字符指针的字符指针数组,元素长度是随意的。
如:char zifu[][5] = {
"hello",
"world" };
char *pzifu[] = {
"hello.",
"hello,world" }
总结:指针数组可以模拟二维数组,不是真正意义上的二维数组,但指针数组的可变元素长度,是像二维数组不能模拟的。
十、命令行参数
在支持C语言的环境中,可以在程序开始时调用程序主函数main(int argc, char *argv[])。第一个参数argc是操作系统用于统计字符指针数组的字符串个数的。第二个参数argv字符指针数组用于输入参数的,数组的首元素,C语言约定为程序名字的字符串,所以argc参数值必须至少为1,字符串之间使用空格隔开,形如argv[argc]的值必须为空指针。命令行中:程序名字字符串 其他字符串 ...
十一、指向函数的指针
- 空类型指针——void*
没有指定指针类型的指针,仅仅是一个地址。任何类型的指针都可强制转换为空类型指针,也可把空类型指针转换所需的类型。空类型指针不可地址运算,因为没有类型,也不可寻址,没有类型数据无意义。
- 指向函数的指针
函数虽说不是变量,但和数组名一样,储存了一个函数的首地址,函数名相当于数组名常量指针。声明一个指向函数的指针变量,可用最函数调用和把函数作为函数参数。一般形式如下:
指针类型 (*函数指针名)(void *,void *);
十二、复杂的声明
特别内容
——数组,数组名是常量指针,表示数组元素首地址。获取带以下标元素的地址,使用&取地址符。——数组与指针,数组转指针,获取数组名的值,就可通过地址运算遍历数组了。
——指针数组,是可存储某类型指针的数组,它是数组。数组名依然是代表元素首地址,即首个元素指针的地址。数组名加下标,表示对应的指针的值,也就是指针的内容。获取带下标的元素指针的地址使用&。
——指针数组作为参数,实际上数组会退化成指针形式处理,指针数组里储存的是指针,理所当然退化成二级指针,也就是指针的指针。二级指针会得到指针数组的元素首地址。其他元素指针,使用遵循地址运算规则就可。
C语言运算符优先级与求值次序
运算符优先级 | 结合性 |
---|---|
() [ ] -> . | 从左至右 |
! ~ ++ – + - * & (type) sizeof | 从右至左 |
* / % | 从左至右 |
+ - | 从左至右 |
<< >> | 从左至右 |
< <= > >= | 从左至右 |
== != | 从左至右 |
& | 从左至右 |
^ | 从左至右 |
| | 从左至右 |
&& | 从左至右 |
|| | 从左至右 |
?: | 从右至左 |
= += -= *= /= %= &= ^= |= <<= >>= | 从右至左 |
, | 从左至右 |
注:结合性是运算符匹配操作数,同优先级一般从左至右计算表达式,有不同优先级运算符根据结合性再计算。