C语言编写的程序至今仍能和汇编语言程序相媲美,其中一个重要原因就是拥有一种数据类型——指针(内存操作)
1.指针和地址
数据的访问方式——直接访问和间接访问
间接访问方式
通过变量来访问( 计算机用一个别称(变量)来标识存储单元,计算机在寻找内存的数据时就要先把这个别称翻译成地址才行 )
直接访问方式
通过地址来访问
ps: 变量其实就是内存存储单元的一个临时的别名,而地址才是这个存储单元永久的真名
2.指针和指针变量
C语言中的指针,就是变量的地址
存放地址的变量为指针变量
3 .初始化
1) int i = 0;
int * p = i;
2) int i = 0;
int * p ;
p = & i;
注意:int * a, b, c; 定义了一个指针变量a和两个整型变量b和c,
int *a, *b, *c定义了三个指针变量;
4. 指针类型
指针的时候必须指定其类型,不同类型的指针(p++)内存中移动的大小不一样
指针变量里存放的的值只能是变量的地址
5 . 指针和一维数组 ( 数组名存放数组的首地址 )
int a[ 3 ] ={1,2,3}; int a[ 3 ] ={1,2,3};
int * p; 或 int * p = a;
p = &a[ 0 ];
a[ 2 ]等价于 * (p+2);
说明:
a是一个地址,我们知道a其实本来的面目应该是:a+0,只不过这个0我们就省了,*(a+0)也就是a[0],所以我们可以知道a它代表的应该是第一个(下标为0)元素的地址。
int a[ 4 ];
先从另外一个方面来理解什么是数组,其实我们可以把数组假设为一个基本类型变量,它的类型为 int [4].也就是变量a这个对象代表的就是4个int那么大小的一个区间,而且不可分割。它和一般变量不同的地方在于,它的操作受限,不能像一般变量那样,直接通过赋值符号一次性将整个区间的值赋值给另外一个相同的数组变量。而只能通过下标来一个个元素的赋值。
通过上面的分析,&a就好理解了,他就是变量a的地址,这个变量a的作用范围是四个int空间的数据,也就是如果将&a赋给某个变量p,p的类型应该是:int (*)[4];也就是指向包含4个int数据的数组的指针。
再来看看a+1和&a+1的区别,a+1表示的是数组第一个元素地址,&a+1表示的是跨过a数组的下一个地址。通过图来表示可能更加清晰可理解:
6.指针和二维数组
&a[0]与a是等价的 都代表一行首元素的地址 &a[1]与a + 1等价 代表第二行首元素的地址
注:a[0]+0; a[0]+1; a[0] +2; a[0]+3;
等价于 &a[0][0]; &a[0][1]; &a[0][2]; &a[0][3];
7.用指针来替换掉数组的下标
1).对于一维数组 a[ 0 ] 等价于 * (a + 0)
2).对于二维数组a[0][0]等价于a[0]+0或*(a+0 )+ 0
8.字符串指针变量
在C语言中,字符串是以字符数组的形式来实现的
9.与字符数组的不同:
1)赋值的方式不同
对于字符串指针变量
char * s = "hello" ;
或
char * s;
s = "hello";
对于字符数组
int * a[10] ={ "hello"};
而不行:int a[10]
a = “hello”;
2) 地址是否可以改变
指向字符数组的指针变量和字符数组名本质上都是表示数组的首地址
字符串指针可以改变
数组名不可以改变
10.可以用字符串变量和字符数组来表示一个格式化字符串
11.指针和函数
I、用指针来传值的好处:
1)能让形参的变化映射到实参中
2)高效的传值方式(如复制大的对象时)
II、指向函数的指针
正在运行的程序(无论是数据和指令)都被存储在内存中,既然变量都有地址,那么指令也是有地址的,但指针不能被用来指向程序中的任意一条指令
C语言中提供了另一种有一定限制的指令访问方式——指针变量哦可以指向一个函数(这个指针变量所存放的地址实际上就是函数中第一条指令的地址)
III.使用方法:
IV.指向函数的指针最常用的地方:
1)作为参数传给其他函数
2)作为参数以实现地址的传递,也就是将函数名传递给形参
V.返回值为指针的函数:
能够让函数返回多个值的方法:
1)定义全局变量并将希望哈市返回的值存储在全局变
2)把指针当作参数传给函数,
VI.返回指针值函数的错误使用
- <span style="font-family:FangSong_GB2312;font-size:18px;">#include <math.h>
- #include <stdio.h>
- #define M 5
- #define N 3
- float * mean_byPerson(float score[][3])
- {
- float sum = 0.0;
- float r[M]; //定义了一个一维数组,是局部变量,
- float * p = &r[0];
- int i = 0, j;
- for (; i<M; i++)
- {
- for(j =0;j<N;j++)
- sum += *(score[i]+j);
- r[i] = sum/(float)N;
- sum =0.0;
- }
- return p; // 函数一旦返回,它所占用的空间就会被释放,
- } //即使保留一个指向r的指针p,也不可能得到数组r中原有的值
- int main()
- {
- float score[M][N] = //定义一个二维数组来存储这些学生的成绩单,
- { {90,85,72},
- {100,80,75},
- {80,93,98},
- {60,53,75},
- {78,82,39} };
- float * m1 =mean_byPerson(score);
- printf("Everyones' mean score is as below:\n");
- for (int i = 0; i<M; i++)
- {
- printf("%.2f\t", *(m1 + i));
- }
- }
- </span>
- <span style="font-family:FangSong_GB2312;font-size:18px;"></span>
修改方法:在主函数中定义一个存放结果的数组,将该指针传给函数
- <span style="font-family:FangSong_GB2312;font-size:18px;">#include <math.h>
- #include <stdio.h>
- #define M 5
- #define N 3
- float * mean_byPerson(float score[][3],float * r) //将要计算的数组,和存放结果的数组传入函数
- {
- float sum = 0.0;
- // float r[M]; //定义了一个一维数组,是局部变量,
- float * p = &r[0];
- int i = 0, j;
- for (; i<M; i++)
- {
- for(j =0;j<N;j++)
- sum += *(score[i]+j);
- r[i] = sum/N;
- sum =0.0;
- }
- return p; // 函数一旦返回,它所占用的空间就会被释放,
- } //即使保留一个指向r的指针p,也不可能得到数组r中原有的值
- int main()
- {
- float score[M][N] = //定义一个二维数组来存储这些学生的成绩单,
- { {90,85,72},
- {100,80,75},
- {80,93,98},
- {60,53,75},
- {78,82,39} };
- float r[M]; //在主函数中定义了一个数组,就可以避免在函数中定义的变量返回时被回收
- float * m1 =mean_byPerson(score,r);
- printf("Everyones' mean score is as below:\n");
- for (int i = 0; i<M; i++)
- {
- printf("%.2f\t", *(m1 + i));
- }
- }
- </span>
- <span style="font-family:FangSong_GB2312;font-size:18px;"></span>
总结:尽管指针函数可以起到返回一组值的作用,但也存在一定的局限性,即返回的指针所指向的不能是函数中的局部变量
12.复合多维指针的使用
指针数组的定义
数组的元素也可以是是指针类型;
类型说明符 * 数组名[ 数组长度];
int * p[ 3 ]
上述语句表示p是一个指针数组,它由3个数组元素组成,每个元素都是指向一维数组的指针
指针数组的来那个典型用途:
1)可以用来指向一个二维数组
2)可以用来指向一组字符串(原理同上)
13.指向指针的指针一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量
实际用途(类似前面的指针数组):
举例(服务评价打分系统)