如何将网站部署到服务器,杭州网站设计开发,石家庄网站制作福州,网站建设与推广的策划方案大家好#xff0c;我是深鱼~ 【前言】#xff1a;
指针的主题#xff0c;在初阶指针章节已经接触过了#xff0c;我们知道了指针的概念#xff1a;
1.指针就是个变量#xff0c;用来存放地址#xff0c;地址的唯一标识一块内存空间#xff08;指针变量#xff09;我是深鱼~ 【前言】
指针的主题在初阶指针章节已经接触过了我们知道了指针的概念
1.指针就是个变量用来存放地址地址的唯一标识一块内存空间指针变量内存单元是由编号的编号地址指针
2.指针/地址/指针变量的大小是固定的4/8个字节32位平台/64位平台
3.指针是有类型的指针的类型决定了指针的-整数的步长指针解引用操作的时候的权限
4.指针的运算 【目录】
一、字符指针 二、指针数组 三、数组指针
3.1数组指针的定义
3.2数组名vs数组名
3.3数组指针的使用
四、数组参数指针参数
4.1一维数组传参
4.2二维数组传参 4.3一级指针传参
4.4二级指针传参
五、函数指针 一、字符指针
一般使用
#includestdio.h
int main()
{char ch w;char* pc ch;*pc w;return 0;
}
还有一种使用方式如下
#includestdio.h
int main()
{char ch w;char* p abcdef;//[abcdef\0]//char arr[]abcdefprintf(%s\n,p);//打印的是整个字符串printf(%c\n,*p);//打印的是aprintf(%c\n, abcdef[3]);//打印的是dreturn 0;
}
这里的字符串abcdef“类似于数组把这个字符串赋给p指针也就是字符串的首地址赋给p指针
假设是 32位平台指针的大小也就是4个字节但是这个字符串却有7个字节算上\0),这样看是放不下的
但是这个代码存在问题p变量赋给了常量字符串如果改变p变量常量字符串是不会更改的
eg这样代码很容易出错误所以一般会在char *p之前写上const char* p abcdef; *p e; 更加安全的写法 const char* p abcdef; 下面再来看一道【经典面试题】
这道题的输出结果是啥
#include stdio.h
int main()
{
char str1[] hello bit.;
char str2[] hello bit.;
const char *str3 hello bit.;
const char *str4 hello bit.;
if(str1 str2)
printf(str1 and str2 are same\n);
else
printf(str1 and str2 are not same\n);
if(str3 str4)
printf(str3 and str4 are same\n);
else
printf(str3 and str4 are not same\n);
return 0;
}
【答案】
str1 and str2 are not same str3 and str4 are same
【解释】
(1)首先创建两个数组分别放入hello bit因为是两个不同的数组那么字符串就存在不同的位置当判断str1和str2也就是两个数组的首地址的时候这两个h地址肯定不相同
(2)str3和str4指针都指向常量字符串因为常量字符串不可修改那么其实也没有必要再创建一个空间来存两个相同的常量字符串如果两个指针同时指向这个常量字符串的时候其实指向的也就是同一个地址那么str3和str4也就相同 【拓展】再加一个 if(str3str4) printf(YES\n); else printf(NO\n); 【答案】NO
【图解】指针变量不同地址不同 p指针变量表示指针变量指向的内存地址 p取指针p的地址表示编译器为变量p分配的内存地址不同变量分配的地址不同而不是这个指针p指向的地址 二、指针数组 指针数组是数组 字符数组-存放字符的数组 整形数组-存放整形的数组 指针数组-存放指针的数组存放在数组的元素都是指针类型的 eg int *arr[5]; //存放整形指针的数组 char * ch[6]; //存放字符指针的数组
【那指针数组一般怎么用呢】
一般不会像这样使用int *arr[3]{abc}
而是这样使用可以用指针数组模拟一个二维数组
int main()
{int arr1[] { 1,2,3,4,5 };int arr2[] { 2,3,4,5,6 };int arr3[] { 3,4,5,6,7 };//int* int* int*//指针数组int* arr[] { arr1,arr2,arr3 };for (int i 0; i 3; i){for (int j 0; j 5; j){printf(%d , arr[i][j]);}printf(\n);}return 0;
}
【图解】指针数组放入三个数组这三个数组的类型是int *指针类型通过指针数组的打印可以模拟二维数组 【程序结果】 再举个栗子建立一个字符类型int *指针数组并打印数组内的元素
#includestdio.h
int main()
{char* arr[5] { hello bit,hehe,penggeC,bitejiuyeke,C };for (int i 0; i 5; i){printf(%s\n, arr[i]);}return 0;
}
【图解】分别把字符串的首元素地址传给了指针然后把这些指针放在数组中 【程序结果】 三、数组指针
3.1数组指针的定义 数组指针是指针类型于字符指针指向字符的指针整形指针指向整形的指针浮点型指针指向浮点型的指针那么数组指针就是指向数组的指针 int a10; int *pa;(整形指针 char cha; char *pcch;字符指针 int arr[10]; int (*p)[10]arr;数组指针 下面我们来理解一下int*p[10] int (*p) [10]中的*表示p是一个指针*p指向[10]表示指向数组的指针int代表数组元素类型 【注意】
不能直接定义一个指针p然后把arr赋给parr是整个数组的地址arr的类型是int *【10】而指针p的类型是int *不对等所以arr应该放在数组指针中
3.2数组名vs数组名 首先数组名的理解 数组名是首元素的地址但是存在两个例外 1.sizeof数组名这里的数组名表示整个数组计算的是整个数组的大小单位是字节 2.数组名这里的数组名表示整个数组取出来的是数组的地址 int main()
{int arr[10];printf(%p\n, arr);//int *printf(%p\n, arr1);printf(%p\n, arr[0]);//int *printf(%p\n, arr[0]1);printf(%p\n, arr);//int *[10]这就是数组指针的类型printf(%p\n, arr1);//指针类型决定了指针1到底几个字节return 0 【注意】这里的10不可省略数组指针要明确指针指向的数组是几个元素 【练习】
char* arr2[5]的数组指针char* *p[5]前面的char*是数组类型
int arr3[]{1,2,3}的数组指针int (*p[3] (数组指针要明确指向数组的大小这个3必须写
3.3数组指针的使用
先来看看用数组指针打印数组元素这其实是多此一举的本来可以直接arr打印数组元素但是还要定义一个数组指针再用数组指针来打印数组元素
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9 };int(*p)[10] arr;for (int i 0; i 10; i){printf(%d , (*p)[i]);}return 0;
} 【那如果把打印的内容换成p[i]结果又如何呢】
p[i]等价于*pi而指针1直接就跳过一个数组地址根本不可能打印出数组的元素
直接把数组名传给p指针这样才能访问数组的元素int *parr
代码运行结果: 【那么数组指针到底有什么用呢】
数组指针用都是用在二维数组上我们来看一个例子打印一个二维数组 正常的方法形参使用二维数组的形式
#includestdio.h
void print(int arr[3][5], int row, int col)
{for (int i 0; i 3;i){for (int j 0; j 5; j){printf(%d , arr[i][j]);}printf(\n);}
}
int main()
{int arr[3][5] { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };print(arr, 3, 5);return 0;
} 数组指针的方法形参使用数组指针的形式 1首先数组名是首元素的地址在二维数组中首元素的地址就是第一行数组元素的地址 2形参int *p[5]代表这个数组指针指向的是数组第一行5个元素的地址 3p[i][j]代表p[i]就相当于*pi每行的地址解引用后就是每行的数组名每行的数组名[j],就可以得到每行的数组元素 #includestdio.h
void print(int (*p)[5], int row, int col)
{for (int i 0; i 3;i){for (int j 0; j 5; j){printf(%d , p[i][j]);}printf(\n);}
}
int main()
{int arr[3][5] { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };print(arr, 3, 5);return 0;
} 学了指针数组和数组指针我们来一起回顾并看看下面代码的意思
int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
1arr是一个能够存放5个整形数据的数组
2parr1是一个数组数组10个元素每个元素的类型都是int *
3parr2是一个指针该指针是指向数组的指向的数组有10个元素每个元素的类型都是int
4parr3是一个数组是存放数组指针的数组这个parr3【10】数组10个元素存放数组指针int (* ) [5]指向的数组有5个元素每个元素是int类型这个比较难不理解也没事
对于4画图理解parr3是10个元素的数组每个元素中都是地址数组指针每个元素的类型都是int* [5],而对应数组中存放的数组指针这个指针指向5个元素的数组 四、数组参数指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数那函数的参数应该如何设计呢
4.1一维数组传参
数组传参形参是可以写成数组的形式的也可以是指针传参的本质是传递了数组首元素的地址
以下6种方式传参都是可以的
#include stdio.h
void test(int arr[])//ok数组的形式传参可以省略数组的元素个数
{}
void test(int arr[10])//ok直接把数组拿过来数组的形式传参
{}
void test(int* arr)//ok指针的形式传参
{}
void test2(int* arr[20])//ok直接把指针数组拿过来数组的形式传参
{}
void test2(int* arr[])//ok数组的形式传参可以省略数组的元素个数
{}
void test2(int** arr)//ok指针的形式传参
{}
int main()
{int arr[10] { 0 };int* arr2[20] { 0 };//指针数组test(arr);test2(arr2);
}
对于最后一种的理解arr2每个元素的类型都是int *arr2也就是取int *元素的首地址一个指针的地址那么就放到二级指针里面去 4.2二维数组传参
void test(int arr[3][5])//ok
{}
void test(int arr[][])//no二维数组只能省略行不可省略列
{}
void test(int arr[][5])//ok
{}
void test(int* arr)//no二维数组首元素地址是第一行的地址所以指针传参不能省略列
{}
void test(int* arr[5])//no这是一个指针数组而不是本来的二维数组
{}
void test(int(*arr)[5])//ok数组指针指针指向数组数组中有5个元素
{}
void test(int** arr)//no二级指针是用来接收一级指针的地址而我们只需要第一行数组的地址一级指针即可
{}
int main()
{int arr[3][5] { 0 };test(arr);
}
二维数组传参
1数组形式传参只能省略行不可省略列
2指针形式传参参数形式应该是数组指针int *arr【列】而不能是指针数组 4.3一级指针传参
#include stdio.h
void print(int *p, int sz)
{int i 0;for(i0; isz; i){printf(%d\n, *(pi));}
}
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9};int *p arr;int sz sizeof(arr)/sizeof(arr[0]);//一级指针p传给函数print(p, sz);return 0;
}一级指针传参形式参数写成一级指针就可以 【思考】
当一个函数的参数部分为一级指针的时候函数能接收什么参数
1传一维数组的数组名
2传变量的地址
3传指针
4.4二级指针传参
#include stdio.h
void test(int** ptr)
{printf(num %d\n, **ptr);
}
int main()
{int n 10;int*p n;int **pp p;test(pp);test(p);return 0;
}
二级指针传参形式参数写成二级指针或者一级指针的地址都可以
【思考】
当函数的参数为二级指针的时候可以接收什么参数
(1)一级指针的地址
(2)二级指针
(3)传指针数组首元素的地址
五、函数指针
类比
数组指针-指向数组的指针-存放的是数组的地址-数组名就是数组的地址
函数指针-指向函数的指针-存放的是函数的地址-那么怎么得到函数的地址呢
#includestdio.h
int Add(int x, int y)
{return x y;
}
int main()
{//函数名就是函数的地址//函数名也是函数的地址printf(%p\n, Add);printf(%p\n, Add);int (*pf1)(int, int) Add;//pf1就是函数指针变量int (*pf2)(int, int) Add;return 0;
}
函数名就是函数的地址函数名也是函数的地址
int (*pf1)(int, int) Add这里的括号不可省略不然前面的部分就跟函数声明一样
利用函数指针进行Add加和
#includestdio.h
int Add(int x, int y)
{return x y;
}
int main()
{int (*pf2)(int, int) Add;//int (*pf2)(int, int) Add;这样写也可以int ret (*pf2)(2, 3);//int ret pf2(2, 3);不用*也可以但是用了*一定得加括号//int ret *pf2(2, 3);这个就相当于*5printf(%d\n, ret);return 0;
} 阅读两段有趣的代码 (* ( void ( * )( ) ) 0 )( ) ; 1void*是函数指针类型
2类型常量-强制类型转换-egint a(int )3.14
3有颜色的就是将0强制类型转换为函数指针类型这个0就变成了地址地址然后调用0地址处的函数这个函数没有参数返回值是void void ( * signal ( int , void(*)(int) ) )( int ); 这个代码是函数声明声明的是signal函数signal函数的参数有2个
一个是int 类型
一个是函数指针类型该类型是void(*)(int) 该函数指针指向的函数参数是int返回类型是void
signal函数的返回类型也是函数指针类型该类型是void(*)(int) 该函数指针指向的函数参数是int返回类型是void
【将这个代码简化】 typedef void*pfun_t)( int );//也就是将void(*)(int)改给名字叫pfun_t pfun_t signal(int ,pfun_t); 本次内容就到此啦欢迎评论区或者私信交流觉得笔者写的还可以或者自己有些许收获的麻烦铁汁们动动小手给俺来个一键三连万分感谢 !