C语言关于指针的复习整理
一、指针的定义
计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如 int 占用 4 个字节,char 占 用 1 个字节。为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编 号是唯一的,根据编号可以准确地找到某个字节。
我们将内存中字节的编号称为地址(Address)或指针(Pointer)。地址从 0 开始依次增加,对于 32 位环境,程 序能够使用的内存为 4GB,最小的地址为 0,最大的地址为 0XFFFFFFFF
二、指针变量
1、定义
数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。
int a=1;
int *p;
p=&a;
printf("%d\n", *p);
printf("%#X, %#X\n", &a, p);
输出为
1
0X28FF10 , 0X28FF10
由第一个输出可以看出,p指向a的地址,利用p可以得到a的取值
%#X 表示以十六进制形式输出,并附带前缀 0X,由输出结果可以看出两者的到相同的值,证明p为指针变量,指向并保存a的地址。
2、取址符、解引用符
定义一个指针变量,应该用 *,然后用 **数据类型+***的格式来定义
int *p
由定义可以看出,要想给指针变量赋值,应该用取地址符“&”:
int *p=&a;
1、指针变量也可以连续定义,例如:
1. int *a, *b, *c;
a、b、c 的类型都是 int* 注意每个变量前面都要带*。
2. 如果写成下面的形式,那么只有 a 是指针变量,b、c 都是类型为 int 的普通变量:
int *a, b, c;
3、通过指针变量取得数据
int a=1;
int *p;
p=&a;
printf("%d\n", *p);
输出a=1。
看起来*p和a是等价的,通过 *p 和 a 获取到的数据一样,但它们的运行过程稍有不同:a 只需要一次运算就能够取得数据,而 *p 要经过两次 运算,多了一层“间接”。
4、通过指针变量赋值
1 int a=1;
2 int *p=&a;
3 *p=100;
4 printf("%d\n", a);
输出 a=100.
第2行代码中 * 用来指明 p 是一个指针变量,第3 行代码中 * 用来给指针指向的数据赋值。
三、数组指针
1、定义
数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元 素在内存中是连续排列的,整个数组占用的是一块内存。
如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。
int arr[] = { 1, 2, 3, 4, 5 };
int *p = arr;
arr 本身就是一个指针,可以直接赋值给指针变量 p。arr 是数组第 0 个元素的地址,因此不用加 &,
也可以写作:
int arr[] = { 1, 2, 3, 4, 5 };
int *p = &arr[0];
//也可以改变指针指向
p=&arr[1] ;
arr、p、&arr[0] 这三种写法都是等价的,它们都指向数组第 0 个元素,或者说指向 数组的开头。
但是“arr 就是一个指针”这种表述并不准确,严格来说应该是“arr 被转换成了一个指针”。
2、用法
如果一个指针变量 p 指向 了数组的开头,那么 p+i 就指向数组的第 i 个元素;如果 p 指向了数组的第 n 个元素,那么 p+i 就是指向第 n+i 个元素。
要记住数组的下标是从零开始的,如果p开始指向的是arr[0],那么p+2指向的是arr[2]
由此得出一些利用下标和指针遍历数组的方法:
- 使用下标
也就是采用 arr[i] 的形式访问数组元素。如果 p 是指向数组 arr 的指针,那么也可以使用 p[i] 来访问数组元素, 它等价于 arr[i]。 - 使用指针
也就是使用 *(p+i) 的形式访问数组元素。另外数组名本身也是指针,也可以使用 *(arr+i) 来访问数组元素,它等价 于 *(p+i)。
如下代码:
#include <stdio.h>
int main()
{
int arr[] = { 99, 15, 100, 888, 252 };
int *p = &arr[0]; //也可以写作 int *p = arr+0;
//第一种方法
printf("%d, %d, %d, %d, %d\n", *(p), *(p+1), *(p+2), *(p+3), *(p+4) );
//第二种方法
for(int i=0;i<5;i++)
{
printf("%d",*(a+i));
}
//第三种方法
for(int i=0;i<5;i++)
{
printf("%d",*(p+i));
}
//第三种方法
for(int i=0;i<5;i++)
{
printf("%d",*(p+i));
}
//第四种
for(int i=0;i<5;i++)
{
printf("%d",p[i]);
}
return 0;
}
3、关于p++ ,++p, (*p)++
*p++ 等价于 *(p++),表示先取得第 n 个元素的值,再将 p 指向下一个元素,即为将p的地址向后移
*++p 等价于 *(++p),会先进行 ++p 运算,使得 p 的值增加,指向下一个元素,整体上相当于 *(p+1),所以会 获得第 n+1 个数组元素的值。
(*p)++ 就非常简单了,会先取得第 n 个元素的值,再对该元素的值加 1。假设 p 指向第 0 个元素,并且第 0 个元素的值为 99,执行完该语句后,第 0 个元素的值就会变为 100。
4、字符串数组
字符串数组的用法与数组用法类似:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "hello world";
char *p = str;
int len = strlen(str), i;
for(i=0; i<len; i++){
printf("%c", *(p+i));
}
printf("\n");
//使用p[i]
for(i=0; i<len; i++)
{
printf("%c", p[i]);
}
printf("\n");
//使用*(str+i)
for(i=0; i<len; i++)
{
printf("%c", *(str+i));
}
//使用%s输出
printf("%s\n",p);
printf("\n");
return 0;
}
除了字符数组,C 语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:
char *str = “hello world”;
或者:
char *str;
str = “hello world”;
四、指针数组
1、定义
如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。
一般形式为:
int *p[3];
2、用法
1. #include <stdio.h>
2. int main(){
3. int a = 1, b = 22, c = 130;
4. //定义一个指针数组
5. int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *arr[]
6. printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
7. return 0;
8. }
arr 是一个指针数组,它包含了 3 个元素,每个元素都是一个指针,在定义 arr 的同时,我们使用变量 a、b、c 的 地址对它进行了初始化,这和普通数组类似。
也可以和字符串结合
9. #include <stdio.h>
10. int main(){
11. char *str[3] = {
12. "afdsf",
13. "qtedggew",
14. "wbfdhdn"
15. };
16. printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
17. return 0;
18. }
五、二级指针
1、定义
1. int a =100;
2. int *p1 = &a;
3. int **p2 = &p1;
- 指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。
- C 语言不限制指针的级数,每增加一级 指针,在定义指针变量时就得增加一个星号*。p1 是一级指针,指向普通类型的数据,定义时有一个*;p2 是二级 指针,指向一级指针 p1,定义时有两个*。
2、获取指针数据
想要获取指针指向的数据时,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推,
如:
1. int a =100;
2. int *p1 = &a;
3. int **p2 = &p1;
4. printf("%d,%d\n",*p1,**p2);
六、指针函数和函数指针
1、指针函数
C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数。
常用代码形式:
1. #include <stdio.h>
2. #include <string.h>
3. int *fun1(int x, int y){
4. x+=y;
5. return &x;
6. }
7. int *fun2(int *x, int* y){
8. x+=y;
9.
10. return x;
11. }
12. int main(){
13. int m=10,n=20;
14. int *p;
15. p=fun1(m,n);
16. printf("%d\n",*p);
17. p=fun1(&m,&n);
18. printf("%d\n",*p);
19. return 0;
20. }
用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、 局部数组和形式参数,函数返回的指针请尽量不要指向这些数据, C语言没有任何机制来保证这些数据会一直有效, 因此在调用完该函数后,应该及时使用运行输出。
2、函数指针
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和 数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的 内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。
用法:
1. #include <stdio.h>
2.
3. //返回两个数中较大的一个
4. int max(int a, int b){
5. return a>b ? a : b;
6. }
7. int main(){
8. int x, y, num;
9. //定义函数指针
10. int (*pmax)(int, int) = max; //也可以写作int (*pmax)(int a, int b)
11.
12. scanf("%d %d", &x, &y);
13. num = (*pmax)(x, y);
14. printf("Max value: %d\n",num);
15. return 0;
16. }