指针变量作函数参数
函数参数可以是指针类型,作用是将一个变量的地址传送到另一个函数中。
为使在函数中改变的变量值能被主调函数main使用,应用指针变量作为函数参数,在函数执行过程中使指针变量所指向的变量值发生变化,函数调用结束后,这些变量值的变化依然保留下来,这样就实现了“通过调用函数使变量的值发生变化,在主调函数中可以使用这些改变了的值”的目的。
若想通过函数调用得到n个要改变的值,可以如此:
- 在主调函数中设n个变量,用n个指针变量指向它们;
- 设计一个函数,有n个指针形参。在函数中改变这n个形参的值;
- 在主调函数中调用这个函数,将这n个指针变量作实参,将其地址传给函数形参;
- 执行函数过程中,通过形参改变它们所指向的n个变量的值;
- 主调函数就可以使用这些改变了值的变量。
注意:不能企图通过改变指针形参的值而使其指针实参的值改变。
函数调用可以(而且只可以)得到一个返回值(函数值),而使用指针变量作参数,可以得到多个变化了的值。要善于利用指针法。
通过指针引用数组
数组元素的指针
数组元素的指针就是数组元素的地址。
可以用一个指针变量指向一个数组元素。
在C中,数组名(不包括形参数组名,形参数组不占据实际的内存地址)代表数组中首元素(即序号为0的元素)的地址。因此p=&a[0];等价于p=a;
数组名不代表整个数组,只代表数组首元素的地址。“p=a;”作用是把a数组的首元素的地址赋给指针变量p,而不是把数组a各元素的值赋给p。
定义时初始化:int *p=&a[0];等价于int a;p=&a[0];等价于int *p=a;
作用是将a数组的首元素(a[0])的地址赋给指针变量p(而不是赋给*p)。
引用数组元素时指针的运算
在一定条件下(当指针指向数组元素的时候)允许对指针进行加和减的运算。如:
1.加/减一个整数(用+/-或+=/-=),如p+1/p-1;
若指针变量p指向数组中某元素,则p+1/p-1指向下/上一个元素。注意:p+1加的是一个数组元素所占用的字节数。p+1所代表的地址实际上是p+1*d,d为一个数组元素所占的字节数。
若p初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组序号为i的元素。//a代表数组首元素地址,a+i也是地址。计算方法和p+i一致。
*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。
在编译时,对数组元素a[i]按*(a+i)处理,即按照数组首元素地址加上相对应移量得到要找的元素的地址,然后找出该单元中的内容。
[]实际上是变址运算符,即将a[i]按a+i计算地址,然后找出该地址单元中的值。
2.自加/减运算,如p++,++p/p--,--p;
3.两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义)。两个地址相加没有意义
如果指针变量p1和p2都指向同一个数组,则p2-p1结果是p2-p1的值(两个地址之差)除以数组元素的长度。表示两个数组元素之间相差的元素个数(相对距离)。
通过指针引用数组元素
方法:
- 下标法:如a[i]形式;
- 指针法:如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量,其初值p=a。
方法比较:
1.用下标法和*(a+i)效率一致,C编译系统将a[i]转换为*(a+i)处理,即先计算元素地址。因此这两个方法找数组元素费时较多;
2.用*(p+i)最快,简洁高效。用指针变量直接指向元素,不必每次都重新计算地址,像p++这种有规律地改变地址值能大大提高执行效率;
3.而用下标法比较直观,不易出错,能直接知道是第几个元素。用地址法和指针变量没有这么直观。
注意:可以通过改变指针变量的值指向不同的元素。但是不能改变数组名a的值(如a++是不行的),数组名代表数组首元素的地址,是一个指针型常量,其值在程序运行期间是固定不变的。
注意:改变指针变量值的过程中要注意指针变量的当前值。应切实保证指向数组中有效的元素。
当指针变量指向数组元素时,指针变量可以带下标。在程序编译时,下标会转换为地址,对p[i]处理成*(p+i)。即当前元素基础上加i个元素。建议少用,容易出错。
小技巧:
*p++等同于*(p++)相当于a[i++]:先引用p的值,实现*p的运算,然后使p自增1。
*(++p)相当于a[++i]:先使p自加,再取*p。
++(*p)相当于++a[i]:表示p所指向的元素值+1。
用数组名作函数参数
C编译将形参数组名作为指针变量处理。//常用此方法调用函数改变实参数组的值。
void fun(int arr[ ]);等价于void fun(int *arr);
以变量名和数组名作函数参数的比较
实参类型 |
变量名 |
数组名 |
要求形参的类型 |
变量名 |
数组名或指针变量 |
传递的信息 |
变量的值 |
实参数组首元素的地址 |
通过函数调用能否改变实参的值 |
不能改变实参变量的值 |
能改变实参数组的值 |
C调用函数皆以“值传递”方式,用变量名时传递的是变量的值,用数组名时传递的值为地址,因此要求形参为指针变量。
在C语言中下标法与指针法都可以访问同一数组,因此可以用数组名或者指针变量作实参或形参。
注意:实参数组名代表一个固定地址,或者说是指针常量,但形参数组名并非一个固定地址,而是按指针变量处理。
若有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下四种://这四种方法,实际上都是地址的传递。实际上形参都是使用指针变量。
- 实参形参都用数组名;
- 实参用数组名,形参用指针变量;
- 实参形参都用指针变量;
- 实参用指针变量,形参用数组名。
//若用指针变量作实参,必须先使指针变量有确定值,指向一个已定义的对象。
通过指针引用多维数组类型名(*指针变量名)[m];
多维数组元素地址
指针可以指向一维数组中的元素,也可以指向多维数组中的元素。
a[i]从形式上看是a数组中序号为i的元素。若a是一维数组名,则a[i]代表a数组序号为i的元素的存储单元。a[i]是有物理地址的,是占存储单元的。但若a是二维数组,则a[i]是一维数组名,它只是一个地址,并不代表某一元素的值(如同一维数组名只是一个指针常量一样)。
二维数组a的有关指针
表示形式 |
含义 |
地址 |
a |
二维数组名,指向一维数组a[0],即0行首地址 |
2000 |
a[0],*(a+0),*a |
0行0列元素地址 |
2000 |
a+1,&a[1] |
1行首地址 |
2016 |
a[1],*(a+1) |
1行0列元素a[1][0]的地址 |
2016 |
a[1]+2,*(a+1)+2,&a[1][2] |
1行2列元素a[1][2]的地址 |
2024 |
*(a[1]+2),*(*(a+1)+2),a[1][2] |
1行2列元素a[1][2]的值 |
元素值3 |
*a[i]+j和*(*(a+i)+j)与a[i][j]等价 *(a+i)与a[i]等价
//a[0],a[1],a[i]的类型为int *型,指向整型变量,而a的类型为int(*)[4],指向含4个元素的一维数组。
//二维数组名是指向行的,一维数组名是指向列的。
//在指向行的指针前面加一个*,就转换为指向列的指针。 行a或a+1 列*a或*(a+1)
//在指向列的指针前面加一个&,就成为指向行的指针。。 列a[0] 行&a[0]
指向多维数组元素的指针变量
1.指向数组元素的指针变量:类型名 * 指针变量名;
数组元素a[i][j]相对位置计算公式:i*m+j//m为二维数组列数
若开始指针变量p指向a[0][0],则a[i][j]的地址为“&a[i][j]+(i*m+j)”或“p+(i*m+j)”
//C下标从0开始,对计算相对位置比较方便。
2.指向由m个元素组成的一维数组的指针变量:类型名(*指针变量名)[m];
//定义时的()不可少//指针类型为 类型名(*)[m]型
//此时指针指向一个包含m个元素的一维数组。指针的值就是该一维数组的起始地址。
3.用指向数组的指针作函数参数
//一维数组名可以作为函数参数,多维数组名也可以。
//用指针变量作形参,以接受实参数组名传递来的地址。可以有两种方法:
//1.用指向变量的指针变量;//2.用指向一维数组的指针变量。
通过指针引用字符串
字符串引用方式
在C程序中,字符串是存放在字符数组中的。引用的方式有:
- 字符数组存放字符串,可通过数组名和下标引用其中一个字符,也可通过数组名和格式声明“%s”输出该字符串。
- 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
如:char *string=“I love China!”;等价于char *string;string=“I love China!”;
//对字符指针变量string初始化,实际上是把字符串第一个元素的地址(即存放字符串的字符数组的首元素地址)赋给指针变量string,使string指向字符串的第1个字符。
//内存中字符串最后被自动加了一个’\0’。因此能用%s输出,%s逐个输出字符直到’\0’停止
字符指针作函数参数
若想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。效果一致。
函数的形参实参可以用字符数组名或字符指针变量。
调用函数时实参与形参的对应关系
实参 |
形参 |
实参 |
形参 |
字符数组名 |
字符数组名 |
字符指针变量 |
字符指针变量 |
字符数组名 |
字符指针变量 |
字符指针变量 |
字符数组名 |
使用字符指针变量和字符数组的比较
(1)数组放字符,指针放地址;
(2)指针可赋值,数组名不行;
(3)初始化含义,一个地址,一个值char *a="I love";等价于char *[a]; a="I love";
char str[14]="I love";不等价于char str[14];str="I love";
//出错,数组只能定义时候赋初值,而不能用赋值语句进行赋值
(4)存储单元内容,数组分配若干个,指针分配一个;
(5)指针变量值可以改变,数组名的值是固定的;
(6)数组中各元素值可以改变,字符指针指向的字符串常量中的内容不可改变
char a[]="House";char *b="House";
a[2]='r';合法 b[2]='r';不合法,字符串常量不能改变
(7)引用数组元素。字符数组用下标法/地址法。而指针需要先指向数组才可引用
(8)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。(可变格式输出函数)
char *i;i="a=%d,b=%d\n";printf(i,a,b);相当于printf("a=%d,b=%d\n",a,b);
也可使用字符数组实现:char i[]="a=%d,b=%d\n"; 指针更为方便,从赋值角度