指针 即 指向某一个 变量 的内存地址
指针变量的定义
定义指针时,建议变量名为 p
开头
1 | int a = 10, b = 20, *pa, *pb = &b; |
此处 pa 为先声明后赋值,pb 为声明时赋值
声明后但未赋值的为野指针,禁止对其存取,因为其被随机指向了某一个内存空间
间接访问符
*
用来声明指针的同时,也是间接访问符
对于指针 int *p
和 int a = 6
令 p = &a
此刻 p 为 a 的内存地址,*p 则等同于 a,均为 6
指针与数组
指针变量+1 表示 跳过该指针变量对应的基类型所占字节数大小的空间
当指向数组时,其基类型为数组元素类型,指针变量+1表示跳过一个数组元素空间,指向下一个数组元素
数组名 a 相当于数组首元素 a[0] 的地址,即 a 等价于 &a[0]
例一:依次将输入的数据填入数组,并依次打印输出
1 | int a[5], *p, i; |
指针与二维数组
二维数组的存储结构为顺序形式,即二维数组中的数据元素在内存中的存储地址是连
访问数组元素的方法:
- 直接访问:a[i][j]
- 间接访问:*(*(a+i)+j)
数组指针和指针数组
数组指针
数组指针,即指向一维数组的指针,是指向含 N 个元素的一维数组的指针
1 | int (*p)[5]; |
一维数组指针 p,该指针 p 只能指向含 5 个元素的整型数组
注:不要漏掉括号,下标运算符[] 比 *运算符 优先级高,p 首先与下标运算符 [] 相结合,说明 p 为数组,该数组中有 5 个元素,每个为 int * 型。即 p 为指针数组。
指向二维数组
1 | int a[M][N]; |
两者变换关系
1 | *(i 行首地址)=i 行首元素地址 |
当定义一个指向一维数组的指针 p,并初始化为二维数组名 a 时,即 p=a,用该指针访问元素 a[i][j] 的两种形式 ((p + i) + j) 与 ((a + i) + j) 非常相似,仅把 a 替换成了 p 而已。
数组指针指向的是一整行,故数组指针每加 1 表示跳过一行
指针数组
即存储指针的数组,数组中的每个元素均是同类型的指针
1 | int *a[5] |
指针数组最主要的用途是处理字符串,c语言中没有 字符串类型,定义字符串时常需要这样
1 | char a[] = "sorry"; |
在 C 语言中,一个字符串常量代表返回该字符串首字符的地址(字符串其实就是字符数组,联想理解数组),即指向该字符串首字符的指针常量,因此根据上式子得。
1 | char a[] = "sorry"; |
打印结果均为 sorry
由于指针数组的每个元素都是指针变量,因此,可以通过指针数组定义字符串数组:
1 | char * c[4]={"if","else","for","while"}; |
通过c[i]
来获取每个字符串
指针与字符串
字符串指针常量
上节已知,字符串常量 “wdnmd” 为一个指针常量,指向第一个字符 w 的地址,打印字符串指针为**从指针地址开始直到遇到’/0’**,同理char a[] = "wdnmd"
,a 也是指针常量
类比数组的指针特性,可得:
1 | char a[] = "abcd" |
注:’/0’为字符串终止符,遇到即为字符串结束,详细见最后一节
既然字符串是个指针,那么可以通过间接访问的方式获取具体某一个字符
1 | char a[] = "abcd" |
##¥¥ 字符串指针变量
通过字符指针变量可访问所指向的字符串常量,但仅限于读取操作
1 | char *pc="abcd"; |
字符串变量
字符数组可以理解为若干个字符变量的集合,如果一个字符串存放在字符数组中,那么字符串中的每个字符都相当于变量,故可把存放在字符数组中的字符串称为变量字符串。
1 | char str[10] = "wdnmd"; |
定义字符数组并显示指定其大小(10),前六个空间分别为有效字符’w’ ‘d’ ‘n’ ‘m’ ‘d’ ‘\0’,第七个往后为’\0’填充
也可以不定义长度
1 | char str[] = "wdnmd"; |
自动为其分配6个空间,5个有效字符和结束符’/0’
允许使用数组下标方式改变数组中的元素,如
1 | str[1] = 'r'; // 改为 "wrnmd" |
字符串结束符
上文已多次提到,不再赘述,注意几点:
- 定义确定长度的变量字符串时,长度一定要考虑到’/0’
- 逐个字符定义字符数组时,如
char a[] = {'a', 'b'}
,一定要手动在最后加一个’/0’,否则输出a为乱码甚至程序崩溃 - 定义
char a[3] = ""
时,默认填充3个’/0’,此时手动给第2、3个元素赋值依然为空字符串,因为第1个元素为’/0’,自动终止
函数与指针
函数的两种调用方式
- 传值调用:传入的实参为固定值,函数内部将此值拷贝一份副本到形参变量,对副本进行操错,不会影响传入的数据,较大的数据时创建副本开销较大
- 传址调用:传入的实参为地址,形参为能接受地址的地址箱即指针变量,通过该地址间接访问要处理的数据,能在函数内改变函数外的数据,不涉及创建副本,开销较小
指针函数
即为返回类型为指针的函数,常用于字符串处理函数,格式
1 | 类型*函数名(形参列表) |
函教指针
即指向函教的指针,C语言中,整型变量在内存中占一块内存空间,该空间的起始地址称为整型指针,可把整型指针保存到整型指针变量中。函数像其他变量一样,在内存中也占用一块连续的空间,把该空间的起始地址称为函数指针。而**
函数名就是该空间的首地址**,故函数名是常量指针。可把函数指针保存到函数指针变量中。
函数指针的定义
注意不要省略括号,省略为声明函数原型
1 | 返回类型(*指针变量名)(函数参数表); |
下面定义了一个函数:
1 | int wdnmd(int a, int b) |
该函数符合 函数指针pf的条件,可以让pf指向此函数
1 | pf=&func; //正确 |
通过函数指针调用函数
如下,声明一个函数原型,定义函数指针并指向原型
1 | int f(int a) |
当函数指针被初始化指向函数后,有三种调用函数方式
1 | int result; |
函数调用时,编译器把函数名转换为对应指针形式,故前两种调用方式含义一样.而第三种调用方式,*pf 转换成对应的函数名 f(),编译时,编译器还会把函数名转换成对应指针形式,从这个角度来理解,第三种调用方式走了些弯路。
函数指针通常主要用于作为函数参数的情形。