提示:本文仅是对个人学习经历的一个记录
前言:
作为一个小白,关于我的学习经历,我想在此做一个记录。
本文关于C语言指针部分内容。
PS:目前作者在学指针部分的内容,所以代码是基指针部分知识编写的。
1、指针
1.1、指针==地址
例如:int a = 10(类型,变量名,内存地址,值)
访问变量的两种方式:
- 变量名能访问
- 通过地址也能访问
& 取地址运算符 * 将地址内的值读出运算符
#include <stdio.h> int main() { int a = 10; printf("a = %d\n",a); //输出a的值 printf("a的地址是:%p\n",&a); //输出a的地址 printf("a = %d\n",*(&a)); //*取值运算符,把它后面跟的内存地址中的数据"取出来" return 0; }
结果:
1.2、指针变量==存放地址的变量
1.2.1、定义一个指针变量(* 的标识作用)
- 例如:int *p,这里的*是一个标识符,告诉系统我是一个指针变量,用来保存别人的地址
- * 的标识作用,只产生在指针变量定义或声明的时候
1.2.2、使用一个指针变量(* 的运算作用)
- printf("地址访问:a = %d\n",*(&a));
- printf("指针变量访问:a = %d\n",*p);
#include <stdio.h> int main() { //什么是整型变量,存放整型数的变量 //什么是字符变量,存放字符型数据的变量 //什么是指针变量,存放指针的变量 //什么是指针变量,存放地址的变量 int a = 10; int *p; //这里的*是一个标识符,告诉系统我是一个指针变量,用来保存别人的地址,与下面的运算符不同 p = &a; printf("变量名访问:a = %d\n",a); //输出a的值 printf("a的地址是:0x%p\n",&a); //输出a的地址 printf("地址访问:a = %d\n",*(&a)); //*取值运算符,把它后面跟的内存地址中的数据"取出来" printf("指针变量访问:a = %d\n",*p); return 0; }
结果:
1.3、指针变量区分类型
思考:既然指针变量是存放别人地址的变量,那什么要区分类型呢?
类型决定指向空间的大小,决定增量
#include <stdio.h> int main() { int a = 0x1234; int *p = &a; char *c = &a; printf("address:p = %p\n",p); printf("address:c = %p\n",c); putchar('\n'); printf("pointer_p: a = %x\n",*p); printf("pointer_c: a = %x\n",*c);//取值的时候出了问题,取值运算符会根据 //指针变量类型访问不同大小的空间 putchar('\n'); printf("address:++p = %p\n",++p);//增量不同 printf("address:++c = %p\n",++c); return 0; }
结果:
2、为什么需要用指针
函数封装时使用。
练习1、封装一个函数,实现两个数的交换。
#include <stdio.h> void changeData(int *data1,int *data2) { int tmp; tmp = *data1; *data1 = *data2; *data2 = tmp; } int main() { int data = 10; int data2 = 20; printf("交换前:data=%d | data2=%d\n",data,data2); changeData(&data,&data2); printf("交换后:data=%d | data2=%d\n",data,data2); return 0; }
结果:
练习2、指针指向固定的区域
#include <stdio.h> int main() { int a = 10; printf("address of a is 0x%p\n",&a); volatile unsigned int *p = (volatile unsigned int *)0x000000000061FE66; printf("p = 0x%p",p); return 0; }
结果:
2.1、示例
例一、输入三个数a,b,c; 要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现。
#include <stdio.h> void changeData(int *data1,int *data2,int *data3) { int tmp; if( *data1<*data2){ tmp = *data1; *data1 = *data2; *data2 = tmp; }if( *data1<*data3){ tmp = *data1; *data1 = *data3; *data3 = tmp; }if( *data2<*data3){ tmp = *data2; *data2 = *data3; *data3 = tmp; } } void initDtat(int *data1,int *data2,int *data3) { puts("请输入三个整数:"); scanf("%d%d%d",data1,data2,data3); } int main() { int a; int b; int c; initDtat(&a,&b,&c); puts("交换前:"); printf("a = %d | b = %d | c =%d\n",a,b,c); changeData(&a,&b,&c); puts("交换后:"); printf("a = %d | b = %d | c =%d\n",a,b,c); return 0; }
结果:
3、通过指针引用数组
3.1、定义一个指针变量指向数组
//可以用一个指针变量指向一个数组元素。
//例如:
int a[10] = {1,3,5,7,9,11,13,15,17,19}; //定义a为包含10个整型数据的数组
int *p; //定义p为指向整型变量的指针变量
p = &a[0]; //把a[0]元素的地址赋给指针变量p
//指针指向数组首元素的地址
在C语言中,数组名(不包括形参数组名,形参数组并不占据实际的内存单元)代表数组中首元素(即序
号为0的元素)的地址。因此,下面两个语句等价:
- p = &a[0]; //p的值是a[0]的地址
- p = a; //p的值是数组a首元素(即 a[0])的地址
3.2、指针增量和数组的关系
#include <stdio.h> int main() { int arry[3] = {1,2,3}; int *p; //p = &arry[0];//数组的首地址就是首个元素的地址 p = arry;//数组名就是数组的首地址 for( int i = 0;i<3;i++){ printf("第%d元素是:%d |address:0x%p\n",i,*(p+i),(p+i)); } /* printf("0元素是:%d\n",*p); printf("1元素是:%d\n",*(p+1));//p+1地址偏移 printf("2元素是:%d\n",*(p+2)); */ return 0; }
结果:
3.3、通过指针引用数组元素
3.3.1、数组访问方法:
- 下标法
- 指针法
3.3.2、指针法
1、偏移
#include <stdio.h> int main() { int arry[3] = {1,2,3}; int *p; p = arry; for( int i = 0;i<3;i++){ printf("%d ",*p++);//优先级,先*p在p++ } putchar('\n'); p = arry;//指针回调 for( int i = 0;i<3;i++){//再次访问时 printf("%d ",*p++);//优先级,先*p在p++ } putchar('\n'); return 0; } // for( int i = 0,j = 0;i<3;i++)//i和j都是整形
结果:
2、取内容
- 见怪不怪:指针当作数组名,下标法访问
- 见怪不怪:数组名拿来加
- 数组和指针变量的大小
#include <stdio.h> int main() { int arry[3] = {1,2,3}; int *p = arry; printf("size of arry is:%d\n",sizeof(arry));//3*4=12 printf("size of p is:%d\n",sizeof(p));//os(操作系统),用8个字节来表示一个地址 printf("size of int is:%d\n",sizeof(int)); printf("size of pointer is:%d\n",sizeof(int*)); printf("size of pointer is:%d\n",sizeof(char*)); printf("%d\n",*p);//指针访问 printf("%d\n",p[2]);//指针当作数组名,下标访问 printf("%d\n",*arry); for( int i = 0;i<3;i++){ printf("%d ",p[i]);//指针当作数组名,下标访问 } putchar('\n'); for( int i = 0;i<3;i++){ printf("%d ",*(p+i));//指针访问 } putchar('\n'); for( int i = 0;i<3;i++){ printf("%d ",*(arry+i));//数组名拿来加 } putchar('\n'); for( int i = 0;i<3;i++){ printf("%d ",*p++);//指针偏移访问 } p = arry;//指针回调 putchar('\n'); /* for( int i = 0;i<3;i++){ printf("%d ",*arry++);//编译不过,指针常量,arry就是arry[0],不能改变 } putchar('\n'); */ return 0; }
结果:
3、数组名和指针区别
- 指针常量,arry 是定死的,不能改变(数组名)
- 指针变量 ,p 是变量,可以改变(指针)
3.3.3、两种方法效率对比
对于使用指针和数组下标的选择:
- 系统在使用数组下标对数组成员变量进行访问时,开销比较大,指针的访问效率是远远大于数组名的访问效率的。但是只有在指针正确访问时,才成比下标法更有效率。
- 下标法更加容易理解,在可读性方面,也更加的具有优势,具体怎么选择,也没有一定的说法。
3.4、实例练习
例1、函数封装数组初始化,遍历
#include <stdio.h> void initArry(int *parr,int size) { int i; for( i=0;i<size;i++){ printf("请输入第%d个数据:\n",i+1); scanf("%d",parr++); } putchar('\n'); } void printArry(int *parr,int size) { int i; for( i=0;i<size;i++){ printf("%d ",*parr++); } putchar('\n'); } int main() { int arry[5]; int size = sizeof(arry)/sizeof(arry[0]); initArry(arry,size);//实际参数,数组的首地址: 名,首个元素的地址 printArry(arry,size); return 0; }
结果:
例2、将数组中的n个元素按逆序存放
#include <stdio.h> void revangeArry(int *parr,int size) { int i; int j; int tmp; for( i=0;i<size/2;i++){ j = size-1-i; tmp = *(parr+i); *(parr+i) = *(parr+j); *(parr+j) = tmp; } } void initArry(int *parr,int size) { int i; for( i=0;i<size;i++){ printf("请输入第%d个数据:\n",i+1); scanf("%d",parr++); } putchar('\n'); } void printArry(int *parr,int size) { int i; for( i=0;i<size;i++){ printf("%d ",*parr++); } putchar('\n'); } int main() //逆序输出 { int arry[5]; int size = sizeof(arry)/sizeof(arry[0]); initArry(arry,size); printArry(arry,size); revangeArry(arry,size); printArry(arry,size); return 0; }
结果:
4、指针和二维数组
补充:段错误检测方法:
gcc demo.c -g
gdb a.exe
r q y
4.1、多维数组元素的地址
为了说清楚指向多维数组元素的指针,先回顾一下多维数组的性质,以二维数组为例。设有一个二维
数组a,它有3行4列。它的定义为
- int a[3][4] = {{1,3,5,7}, {9,11,13,15}, {17,19,21,23}};
a是二维数组名。a数组包含3行,即3个行元素:a[0],a[1],a[2]。而每一个行元素又是一个一维数组,
它包含4个元素(即4个列元素)。例如,a[0]所代表的一维数组又包含4个元素:
- a[0][0], a[0][1], a[0][2], a[0][3]。
可以认为二维数组是“数组的数组”,即二维数组a是由3个一维数组所组成的。二维数组本质还是数组,不同点是数组元素还是个数组。
a[0], a[1], a[2] 既然是一维数组名,而C语言又规定了数组名代表数组首元素地址,因此 a[0] 代表一维
数组a[0]中第0列元素的地址,即&a[0][0];也就是说 a[1] 的值是&a[1][0], a[2] 的值是&a[2][0]。
换种说法总结:
- a表示父数组的地址,(a + 1),父数组(行)偏移一位,偏移了一个子数组
- a[0],*a表示子数组的地址(名),(a[0] +1),子数组(列)偏移一位
- a表示父数组地址,它存储的是子数组的地址,*a表示子数组地址
#include <stdio.h> int main() { int arry[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; printf("arr是父数组地址:%p,偏移1后是:%p\n",arry,arry+1); printf("arr[0]是子数组地址:%p,偏移1后是:%p\n",arry[0],arry[0]+1); printf("arr[0]是子数组地址:%p,偏移1后是:%p\n",*(arry+0),*(arry+0)+1); return 0; }
结果:
4.2、认知加深
- 前已述及,a[0] 和 *(a+0) 等价,a[1] 和 *(a+1) 等价,a[i] 和 *( a+i) 等价。因此,a[0]+1 和 *(a+0)+1都是 &a[0][1] 。a[1]+2和*( a+1)+2 的值都是&a[1][2]。请注意不要将* (a+1)+2错写成*(a+1+2),后者变成*(a+3)了,相当于a[3]。
- 进一步分析,欲得到a[0][1]的值,用地址法怎么表示呢?既然a[0]+1和*(a+0)+1是a[0][1]的地址,那么,*(a[0]+1)就是a[0][1]的值。同理,*( *(a十0)+1)或*( * a十1)也是a[0][1]的值。* (a[ i ]+j)或* ( *(a+i)+j)是 a[ i ][ j ]的值。务请记住* (a+i)和 a[ i ]是等价的。
- 有些读者可能不理解,为什么a+1和*(a十1)等价呢?他们想,a+1(是地址)和* (a+1)(是内容)怎么都是同一个值呢?的确,二维数组中有些概念比较复杂难懂﹐要仔细消化,反复思考。
4.3、总结
#include <stdio.h> int main() { int arr[3][4] = {{11,12,13,14}, {21,22,23,24}, {31,32,33,34}}; int i; int j; for( i=0;i<3;i++){ for( j=0;j<4;j++){ printf("add:0x%p | data:%d \n",&arr[i][j],arr[i][j]); printf("add:0x%p | data:%d \n",arr[i]+j,*(arr[i]+j)); printf("add:0x%p | data:%d \n",*(arr+i)+j,*(*(arr+i)+j)); printf("========================================\n"); } putchar('\n'); } return 0; }
结果:
![]()
5、数组指针
数组指针是指向数组的指针,也称为指向数组的指针变量。它是一种特殊的指针类型,可以用来表示数组的地址,通过数组指针可以访问数组的元素。
数组指针的定义方式和普通指针类似,只不过需要在类型说明符和指针符号 ' * ' 之间加上一对方括号 '[]' 表示这是一个指向数组的指针,例如:
int (*p)[3]; // 定义指向具有 3 个元素的 int 数组的指针
上述定义中的 'p' 是一个指向具有 3 个元素的 'int' 数组的指针,可以使用指针访问数组的元素,例如:
int a[2][3] = {{1,2,3}, {4,5,6}};
p = a; // 将指针指向数组的首元素
printf("%d\n", (*p)[0]); // 输出数组的第一个元素 1
printf("%d\n", p[1][2]); // 输出数组的第五个元素 6
在上述示例中,先将数组 'a' 的首元素的地址赋给 'p',然后使用指针访问数组中的元素,可以使用 '(*p)[i]' 或 'p[i][j]' 的形式进行访问。需要注意的是,由于数组指针和数组名等价,所以也可以使用数组名进行访问,例如 '*a' 相当于 '(*p)'。
#include <stdio.h> int main() { int arr[3][4] = {{11,12,13,14}, {21,22,23,24}, {31,32,33,34}}; int i; int j; int *p; // p = &arr[0][0]; //p = arr; //能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组? //数组指针,定义一个指针,指向一个数组! //数组指针才是真正等同于二维数组 int (*p2)[4]; //指向一个含有4个元素的数组 p2 = arr; printf("p2=%p\n",p2); printf("++p2=%p\n",++p2); for( i=0;i<3;i++){ for( j=0;j<4;j++){ //printf("%d\n",*p++); } } return 0; }
结果:
![]()
例1、输出二维数组任意行列的数
#include <stdio.h> void tipsInput(int *ph,int *pl) { printf("请输入要取值的行列数:\n"); scanf("%d%d",ph,pl); puts("done\n"); } int getTheData(int (*p)[4],int hang,int lie) { int data; data = *(*(p+hang)+lie); return data; //return p[hang][lie]; } int main() { int arr[3][4] = {{11,12,13,14}, {21,22,23,24}, {31,32,33,34}}; int hang,lie; int data; //1、提示输入行列 tipsInput(&hang,&lie); //2、找出对应的数 data = getTheData(arr,hang-1,lie-1); //3、输出结果 printf("第%d行%d列的值是:%d\n",hang,lie,data); return 0; }
结果:
![]()
6、函数指针
6.1、定义
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起
始地址(又称入口地址)称为这个函数的指针。
- 函数名就是地址
6.2、如何定义一个函数指针变量
函数指针变量是指向函数的指针,它可以存储函数的地址,以便在程序中通过指针调用相应的函数。定义函数指针变量的方式如下:
返回类型 (*函数指针变量名)(参数列表)
其中,`返回类型` 是所指向函数的返回值类型;`函数指针变量名` 是定义的函数指针变量的名称;`参数列表` 是所指向函数的参数列表类型。例如,以下代码定义了一个函数指针变量 `pfun`,它指向一个无参数无返回值的函数:
void (*pfun)();
上述代码中,`pfun` 是一个指向无参数无返回值 `void` 类型函数的函数指针变量。
定义好函数指针变量后,就可以将其指向相应的函数,以便通过指针调用该函数。假设有以下函数定义:
void fun() {
printf("Hello, World!\n");
}
则可以使用以下代码将 `pfun` 指向 `fun` 函数,并通过 `pfun` 指针调用该函数:
pfun = fun; // 将函数指针指向函数
(*pfun)(); // 通过指针调用函数
在上述代码中,首先将函数指针 `pfun` 指向 `fun` 函数,然后通过 `(*pfun)()` 的形式调用该函数。也可以直接使用 `pfun()` 的形式调用函数,因为函数指针在调用时会自动转换成相应的函数调用语句。
6.3、使用函数指针
#include <stdio.h> int inCData(int data1) { return ++data1; } void putWelcome() { puts("欢迎来到我的世界!!"); } int main() { void (*p)();//定义一个函数指针变量 int (*p2)(int data1); int data; p = putWelcome;//指向函数 p2 = inCData; putWelcome();//函数名调用(直接) (*p)();//函数指针调用(间接) data = inCData(10); (*p2)(10); printf("p2测试:%d\n",data); printf("p2测试:%d\n",(*p2)(10)); return 0; }
结果:
![]()
6.4、好用之处
- 根据程序运行过程的不同情况,调用不同的函数
例1、有两个整数a和 b,由用户输入1,2或3。如输人1,程序就给出a和b中大者,输入2,就给出a和b中小者,输入3,则求a与b之和。
#include <stdio.h> #include <stdlib.h> int getMax(int data1,int data2) { return data1>data2 ? data1:data2; } int getMin(int data1,int data2) { return data1<data2 ? data1:data2; } int getSum(int data1,int data2) { return data1+data2; } int dataHandler(int data1,int data2,int (*pfunc)(int,int)) { int ret; ret = (*pfunc)(data1,data2); return ret; } int main() { int a = 3; int b = 4; int cmd; int ret; int (*pfunc)(int,int); puts("请输入1(求Max),2(求Min),3(求Sum):"); scanf("%d",&cmd); switch(cmd){ case 1: pfunc = getMax; break; case 2: pfunc = getMin; break; case 3: pfunc = getSum; break; default: puts("输入错误!\n@请输入1(求Max),2(求Min),3(求Sum):"); exit(-1); //输入错误时pfunc是野指针 } ret = dataHandler(a,b,pfunc); printf("ret = %d\n",ret); return 0; }
结果:
![]()
7、指针数组
7.1、定义
- 它是数组,数组的每一项都是一个指针变量
指针数组是一个数组,其中的每个元素都是一个指针。也就是说,指针数组是一个保存多个指向不同对象的指针的数组,它的元素都是指针类型,每个指针指向一个独立的对象。
定义指针数组的语法为:
数据类型 * 数组名称[元素个数]
其中,`数据类型` 是指针指向的数据类型;`数组名称` 是指针数组的名称;`元素个数` 是指针数组中的元素个数。类型名中应包括符号“*”,如“int * ”表示是指向整型数据的指针类型。
以下是定义一个指针数组的示例:
int a = 1, b = 2, c = 3;
int *ptr[3] = {&a, &b, &c};
上述代码定义了一个名为 `ptr` 的指针数组,它由 3 个 `int` 类型的指针数组成。定义时使用了大括号 `{}` 可以初始化指针数组的元素,将 `a`、`b`、`c` 的地址赋值给了指针数组的元素。
由于[]比*优先级高,因此ptr先与[3]结合,形成ptr[3]形式,这显然是数组形式,表示ptr数组有3个元素。然后再与ptr前面的“*"结合,“*”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。
注意不要写成:
int (*ptr)[3]; //这是指向一维数组的指针变量
可以通过下标访问数组元素,例如可使用 `ptr[0]` 访问 `a` 的内存地址,使用 `ptr[1]` 访问 `b` 的内存地址。若要访问指针数组元素所指向的值,则需要使用指针解引用运算符 `*`,例如,使用 `*ptr[0]` 就可以访问 `a` 存储的值 `1`。
7.2、实例
- 函数指针数组
#include <stdio.h> #include <stdlib.h> int getMax(int data1,int data2) { return data1>data2 ? data1:data2; } int getMin(int data1,int data2) { return data1<data2 ? data1:data2; } int getSum(int data1,int data2) { return data1+data2; } int main() { int a = 3; int b = 4; int ret; int (*p[3])(int,int) = {getMax,getMin,getSum};//函数指针数组 for( int i=0;i<3;i++){ ret = (*p[i])(a,b); printf("ret = %d\n",ret); } return 0; }
结果:
![]()
8、指针函数
- 返回指针值的函数
8.1、概念
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
指针函数是返回指针类型的函数,它的返回值是一个指针,该指针指向内存中的某个地址。指针函数可以接受参数,也可以不接受参数。使用指针函数可以有效地减少内存的开销,节省代码的编写时间,并且具有灵活性高和可重用性强等优点,在软件工程中应用广泛。
例如“int * a(int x,int y);”,a是函数名,调用它以后能得到一个int*型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。
请注意在* a两侧没有括号,在a的两侧分别为*运算符和()运算符。而()优先级高于* ,因此a先与()结合,显然这是函数形式。这个函数前面有一个* ,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。
定义指针函数的语法为:
返回类型 *函数名称(参数列表)
其中,`返回类型` 是指针指向的数据类型;`函数名称` 是指针函数的名称;`参数列表` 是指针函数的参数列表类型。
以下是一个简单的例子,它定义了一个指针函数 `getMax`,该函数接受两个整型参数 `a` 和 `b`,并返回它们中的较大值:
int* getMax(int a, int b) {
if (a >= b) {
return &a;
} else {
return &b;
}
}
在上述代码中,`getMax` 函数返回一个指向整型变量的指针,该指针指向 `a` 或 `b` 中较大的那个数。
我们可以通过以下代码调用 `getMax` 函数来获取两个整型数的最大值:
int a = 10, b = 20;
int *ptr = getMax(a, b);
printf("The max number is %d\n", *ptr);
在上述代码中,将 `a` 和 `b` 作为参数传递给 `getMax` 函数,获取返回值(一个指向整型数的指针),然后使用指针解引用运算符 `*` 获取指针指向的值,即找到了两个数中的最大值。在输出语句中使用 `%d` 格式化字符来输出整型数。
8.2、实例练习
例1、有a个学生,每个学生有b门课程的成绩。要求在用户输人学生序号以后,能输出该学生的全部成绩。用指针函数来实现。
#include <stdio.h> int* getPosPerson(int data1,int (*pstu)[4])//指针函数,返回指针的函数 { int *data; data = (int *)(pstu+data1);//父数组偏移//强转成指针类型 return data; } int main() { int scores[3][4] = {{1,2,3,11}, {4,5,6,59}, {7,8,9,99}}; int *ppos; int pos; puts("请输入你需要查看的学生的学号:(0,1,2)"); scanf("%d",&pos); ppos = getPosPerson(pos,scores); for( int i=0;i<4;i++){ printf("%d ",*ppos++); } putchar('\n'); return 0; }
结果:
![]()
例2、对例1中的学生,找出其中有不及格的课程的学生及其学生号。
#include <stdio.h> int* bujige(int (*p)[4]) { int* pt = NULL;//对于及格的同学返回空 for( int i=0;i<4;i++){ if( *(*p+i)<60){//比较子数组元素 pt = *p;//把子数组赋给返回值 } } return pt;//返回有不及格的同学的学号(子数组) } int main() { int scores[3][4] = {{88,82,73,90}, {89,79,69,60}, {76,82,91,39}}; int* bujige(int (*p)[4]);//定义一个指针函数,参数是数组指针 int* p; int i,j; for( i=0;i<3;i++){ p = bujige(scores+i);//传入子数组(子数组就是父数组的元素),返回值赋给指针p if( p!=NULL){//如果返回值不为空,说明这个同学有不及格的成绩 printf("学号为%d的同学有成绩不合格!\n",i);//打印学号(子数组号) for( j=0;j<4;j++){ printf("%d ",*(p+j));//打印成绩子数组元素(成绩) } } } return 0; }
结果:
![]()
9、二级(多级)指针
- 认知考虑的时候,其实所有东西跟一级指针一样,差别就是保存的是指针变量的地址。
9.1、概念
二级指针是指一个指针变量的指针,也就是说,指向指针的指针。二级指针也被称为指针的指针,它在程序中的作用是可以访问或修改指针的值。
定义二级指针的语法为:
数据类型 ** 变量名称
其中,`数据类型` 表示所指向的指针指向的数据类型;`变量名称` 是定义的二级指针变量的名称。例如,以下是一个定义二级指针的示例:
int a = 10;
int *p = &a;
int **pp = &p;
在上述代码中,`p` 是一个指向整型数 `a` 的指针,`pp` 是一个指向指针 `p` 的指针,也就是一个二级指针。可以使用 `**pp` 访问 `a` 存储的值。例如:
printf("%d\n", **pp); // 输出 a 存储的值 10
在上述代码中,使用 `**pp` 解析出指针 `p` 指向的值 `&a`,然后再次使用指针解引用运算符 `*`,从而得到存储在 `a` 中的值 `10`。
二级指针在二维数组、链表等数据结构中常常使用,能够更方便、高效地访问或修改数据结构中指针的值。但是,使用二级指针需要注意指针的内存分配和释放,以免出现内存泄漏等错误。
9.2、实例练习
#include <stdio.h> int main() { int data = 100; int *p = &data; printf("data的地址是:%p\n",&data); printf("p保存data的地址:%p |内容是:%d\n",p,*p); printf("p的地址是:%p\n\n",&p); /* int *pp = &p; printf("pp保存p的地址:%p\n",pp); printf("*pp是:%p\n",*pp); */ int **p2 = &p; printf("p2保存p的地址:%p\n",p2); printf("*p2是:%p\n",*p2); printf("**p2来访问data:%d\n",**p2); int ***p3 = &p2; printf("p3保存p2的地址:%p\n",p3); printf("*p3是:%p\n",*p); printf("***p3来访问data:%d\n",***p3); return 0; }
结果:
![]()
- 当你通过函数调用来修改调用函数指针指向的时候
- 就像通过函数调用修改某变量的值的时候一样
#include <stdio.h> void getPosPerson(int data1,int (*pstu)[4],int **ppos) { *ppos = (int *)(pstu+data1); } int main() { int scores[3][4] = {{1,2,3,11}, {4,5,6,59}, {7,8,9,99}}; int *ppos; int pos; puts("请输入你需要查看的学生的学号:(0,1,2)"); scanf("%d",&pos); getPosPerson(pos,scores,&ppos); for( int i=0;i<4;i++){ printf("%d ",*ppos++); } putchar('\n'); return 0; }
结果:
![]()
9.3、二级指针不能简单粗暴指向二维数组
#include <stdio.h> int main() { int scores[3][4]={ {1,2,3,11}, {4,5,6,59}, {7,8,9,99} };//int (*p)[4]; // int a[]={1,2,3} // int *p = a; /* int **p; p = scores; // *p ? printf("scores:%p\n",scores); printf("p = %p\n",p); printf("*p = %p\n",*p);//*p是一个野指针,不是我们认为的会变成列地址 printf("*scores = %p\n",*scores);//*scores是列地址 **p = 100;//段错误 printf("done\n");//不会执行24行 */ int (*p2)[4] = scores; int **p3 = &p2; **p3 = 100; printf("%d\n",scores[0][0]); return 0; }
结果:
10、总结
各种指针的定义:
1,一个整形数: int a;
2,一个指向整形数的指针: int *a;
3,一个指向指针的指针,它指向的指针指向一个整形数: int **a;4,一个有10个整形数的数组: int a[10];
5,一个有10个指针的数组,每个指针指向一个整形数: int *a[10];6,一个指向有10个整形数的数组的指针: int (*a)[10];
7,一个指向指针的指针,被指向的指针指向一个有10个整形数的数组: int (**a)[10];8,一个指向数组的指针,该数组有10个整形指针: int *(*a)[10];
9,一个指向函数的指针,该函数有一个整形参数并返回一个整形数: int (*a)(int);
10,一个有10个指针的数组,每个指针指向一个函数,该函数有一个整形参数并返回一个整形数: int (*a[10])(int);
11,一个函教的指针,指向的函数的类型是有两个整形参数并且返回一个函数指针的函数,返回的函数指针计指向有一个整形参数且返回整形数的函款: int (*(*a)(int,int)) (int);
总结:
以上就是今天要讲的内容,本文仅仅简单介绍了基于指针部分的知识。
如有错漏,望批评指正。让我们共同进步吧!!