指针
😈
什么是指针
指针=地址
坦白讲,这个地方我看了不知道多少遍,反反复复方阿胶囊
"*"是取地址所存放的值的运算符
"&"是取值的地址的运算符
指针变量wei什么要求类型
#include<stdio.h>
int main()
{
int a = 0X1234;
int *p = &a;
char *c = &a;
printf("p = %p\n",p);
printf("c = %p\n",c);
printf("a = %x\n",*p);//取值运算符会根据变量类型访问不同大小空间
printf("a = %x\n",*c);//所以会出现访问不全的问题(访问不到)
printf("++p = %p\n",++p);//先让值自增一然后打印 整型数加了四个字
printf("++c = %p\n",++c);//字符型只加了一个字节
}
使用指针场景一示例1
用指针交换两个数
封装一个交换两个数的函数,这边我们写两个代码 一个对的一个错的
错误示范
#include <stdio.h>
void huan(int a, int b)//用来承接的是变量
{ //改变的是局部变量
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 88;
int b = 77;
huan(a,b);//传递的是值 详单与复制过去给形参
printf("a = %d,b = %d",a ,b);
}
那么为什么他没有交换呢
是因为我们的值传递的本质值copy 并不是直接对实参进行修改
正确示范
#include <stdio.h>
void huan(int *pa, int *pb)//用来存放的是指针
{ //交换两个数的地址
int tmp;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 88;
int b = 77;
huan(&a,&b);//传递的是地址
printf("a = %d,b = %d",a ,b);
}
这里我们就实现了两个数的交换,我们将存放两个数的的“地址”进行传递,并在函数中交换
使用指针场景一示例2
指针指向固定区域,这边我们的示例代码 就是指定固定区域
上代码:
#include<stdio.h>
int main()
{
int a = 3;
printf("%p\n",&a); //我们先打印出他的地址 是000000000061FE14
volatile unsigned int *p = (volatile unsigned int *)0x000000000061FE33;//(volatile)防止编译器自动优化 (unsigned)无符号的
printf("%p",p);//这里的p的地址是我根据a的地址修改的 就是我指向的地址
}
这边多在单片机 领域会用到
指针小检测
#include<stdio.h>
void jiaohuan(int *pa,int *pb, int *pc)
{
int tmp;
if(*pa<*pb){ //最大值给a
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
if(*pa<*pc){ //最大值给a
tmp = *pa;
*pa = *pc;
*pc = tmp;
}
if(*pb<*pc){ //第二大的值给b
tmp = *pb;
*pb = *pc;
*pc = tmp;
}
}
int main()
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
jiaohuan(&a,&b,&c);
printf("%d>%d>%d",a,b,c);
}
这里跟着写一遍加深一下记忆
数组指针(一维数组)
常规操作 ,先上废话
数组指针是指向数组的指针。在C语言中,数组名本身就是指向数组第一个元素的指针,因此可以使用数组名或者数组指针来访问数组中的元素。在函数中,可以使用数组指针来传递数组参数
废话说完上代码:
#include <stdio.h>
int main()
{
int a[4] = {434,45,2,55};
int *p;//定义一个指针
p = a;//将数组名传递给指针p
printf("%d\n",*p);
printf("%d\n",*a);
}
好好看
指针偏移遍历数组
#include <stdio.h>
int main()
{
int a[4] = {434,45,2,55};
int *p;
int i;
p = a;//将数组名传递给指针p
for(i = 0;i < 4; i++){
printf("%d ",*(p+i));
}
}
这里有一个小小的坑位,我在写注释里了
#include <stdio.h> int main() { int a[4] = {434,45,2,55}; int *p; int i; p = a;//将数组名传递给指针p for(i = 0;i < 4; i++){ printf("%d ",*(p+i)); } p = a;//这边用完之后一定要复位 如果这一行代码没有 那就会出错 for(i = 0;i < 4; i++){//这里我再一次访问数组 printf("%d ",*(p+i)); } }
指针和数组的见怪不怪的写法
#include <stdio.h>
int main()
{
int a[3] = {5,2,56};
int *p;
p = a;
int i;
printf("sizeof a is %d\n",sizeof(a));//3x4=12(个字节)数组中有三个元素 每个元素占四个字节
printf("sizeof p is %d\n",sizeof(p));//8个字节
printf("sizeof pointer is %d\n",sizeof(char *));//8个字节
printf("sizeof pointer is %d\n",sizeof(int *));//8个字节
printf("sizeof int is %d\n",sizeof(int));//4个字节
for(i = 0; i < 3;i++){
printf("%d ",*(p+i));
}
puts("\n");
for(i = 0; i < 3;i++){
printf("%d ",*(a+i));
}
puts("\n");
for(i = 0; i < 3;i++){
printf("%d ",p[i]);
}
}
也许有漏洞,交给你们啦
由上可看出,只要是指针类型的都是占用8个字节。
他们之间有些也是可以混着用的(这句话有争议---我说的),你们要理解可以用,在那用,怎么用,这是关键,好好思考
| 特性 | 数组名 | 指针 |
|---|---|---|
| 类型 | 常量指针 | 变量 |
| 内存空间 | 固定,编译时就已经分配好了 | 动态分配 |
| 赋值操作 | 不可赋值 | 可以改变指向的地址 |
| sizeof操作符 | 返回整个数组所占用的内存空间大小 | 返回指针本身所占用的内存空间大小 |
| 函数参数传递 | 作为参数时自动退化为指向数组第一个元素的指针 | 可以直接传递指针或指针的地址 |
| 表达式运算 | 可以参与表达式运算 | 需要使用解引用运算符*访问所指向的值 |
有特殊标记的,就要注意看,数组名不可++
函数、指针、数组结合
#include <stdio.h>
void initArr(int *parr,int size)
{
int i;
for(i = 0;i<size;i++){
scanf("%d",parr);
parr++;
}
}
void printArr(int *parr,int size)
{
int i;
for(i = 0;i < size;i++){
printf("%d ",*parr);
parr++;
}
}
int main()
{
int arr[5];
int size = sizeof(arr)/sizeof(arr[0]);
initArr(arr,size);
printArr(arr,size);
return 0;
}
这个我不多说 作为对前面我所讲得一个小考核,前面的看懂了 这个也就懂了
练习题
#include <stdio.h>
void initArr(int *parr,int size)
{
int i;
for(i = 0;i<size;i++){
scanf("%d",parr);
parr++;
}
}
void printArr(int *parr,int size)
{
int i;
for(i = 0;i < size;i++){
printf("%d ",*parr);
parr++;
}
puts("\n");
}
void fanZhuanArr(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;
}
}
int main()
{
int arr[5];
int size = sizeof(arr)/sizeof(arr[0]);
initArr(arr,size);
printArr(arr,size);
fanZhuanArr(arr,size);
printArr(arr,size);
return 0;
}
二维数组的地址
这个我讲的容易把你们带到沟里去---但是我会
这边就写一个写上几行代码 参考一下
上代码:
#include<stdio.h>
int main()
{
int i,j;
int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,110,112,114}};
for(i = 0;i < 3;i++){
for(j = 0;j < 4;j++){
printf("地址:0x%p的值为%d\n",&a[i][j],a[i][j]);
printf("地址:0x%p的值为%d\n",a[i]+j,*a[i]+j);
printf("地址:0x%p的值为%d\n",*(a+i)+j,*(*(a+i)+j));
printf("兄弟们 smallsnake 是最帅的 !!!!!!\n");
}
}
}
留下部分看看 挺长的
留下个宝藏
数组指针(二维数组)🤞
数组指针是指向数组的指针。在C语言中,数组名本身就是指向数组第一个元素的指针,因此可以使用数组名或者数组指针来访问数组中的元素。在函数中,可以使用数组指针来传递数组参数 。 int (*p)[m], m为二维数组的列
只有数组指针才是真正的等同于二维数组名
兄弟们 看代码
#include<stdio.h>
int main()
{
int i,j;
int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,110,112,114}};
int (*p)[4];
p = a;
printf("p = %p\n",p);
printf("p++ = %p\n",++p);
p = a; //知道这里为什么要在写一次吗???????????????
for(i = 0;i < 3;i++){
for(j = 0;j < 4;j++){
printf("值为%d\n",*(*(p+i)+j));
}
}
}
做一个题目
用户任意输入行和列,输出二维数组任意行列的数
#include<stdio.h>
void init_hang_lie(int *ph,int *pl)🤞
{
puts("请输入行和列:");
scanf("%d%d",ph,pl);
}
int getseek_look(int (*pa)[4],int hang,int lie)🤞
{
int data = *(*(pa+hang)+lie);
//return pa[hang][lie];//这个也是可以的
return data;
}
int main()
{
int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,110,112,114}};
int (*p)[4];
int hang;
int lie;
p = a;
int data1;
//提示用户输入行列值
init_hang_lie(&hang,&lie);🤞
//抓出那个数
data1 = getseek_look(p,hang,lie);🤞
//打印这个数
printf("第%d行,第%d列的数是:%d",hang,lie,data1);
return 0;
}
别的就不多说了,兄弟们,这几个地方我刚才写的时候又卡住了,有“🤞”标注的地方都是我出问题的地方,很奇葩的问题,不说了🙈说多了都是泪!
函数指针
先上废话:
函数指针是一个指向函数的指针变量,它可以存储函数的地址,并且可以通过该指针变量来调用该函数。函数指针可以用于实现回调函数、动态绑定、多态等高级编程技术,是C语言中非常重要和常用的概念。int (*p)()
"int"类型 | “(*p)”名字 | “()”根据类型传递<里面可以是空的,主要强调是类型,形参可以不写,但是如果有类型要写>
上代码:
#include<stdio.h>
int waha_init(int a)//函数1
{
return ++a;
}
void print_wa()//函数二
{
printf("哇!you are beautiful\n");
}
int main()//主函数
{
int (*waha)();//定义函数指针
int (*wa)();//定义一个函数指针
wa = print_wa;//指向函数
waha = waha_init;
(*wa)();//间接调用
printf("look at me %d",(*waha)(4));
}
这下清晰明了了吧!!!!!!!!!!!😎
冲击你的脑瓜子
其实这个题不算难
两个代码的注释要看
我是把他复杂化了,但其实是废了,不对 是会了😎 将其他的知识能融汇进来 说明你懂了
#include<stdio.h>
int max(int *a,int *b)
{
return *a>*b?*a:*b;
}
int min(int *a,int *b)
{
return *a<*b?*a:*b;
}
int num(int *a,int *b)
{
return *a+*b;
}
int main()
{
int i;
int a = 6;
int b = 7;
int (*Max)();
//int (*Max)(int ,int ) //这种写法是常用的 ,强调的是类型
int (*Min)(); //这种也不会报错
int (*Num)();
Max = max;
Min = min;
Num = num;
printf("1找出大的,2找出小的,3加一起,你想干啥\n");
scanf("%d",&i);
switch(i){
case 1:
printf("哈哈 来啦:%d",(*Max)(&a,&b));
break;
case 2:
printf("哈哈 来啦:%d",(*Min)(&a,&b));
break;
case 3:
printf("哈哈 来啦:%d",(*Num)(&a,&b));
break;
} //方法有很多在
}
这个是通过回调函数完成的
#include<stdio.h>
int get_summarizing(int a,int b,int (*pfunc)(int ,int ))//这里哦,出现的写法都是对的各种写法
{
int ret;
ret = (*pfunc)(a,b);
return ret;
}
int max(int *a,int *b)
{
return *a>*b?*a:*b;
}
int min(int *a,int *b)
{
return *a<*b?*a:*b;
}
int num(int *a,int *b)
{
return *a+*b;
}
int main()
{
int i;
int a = 6;
int b = 7;
int ret;
int (*pfunc)(int,int);
int (*Min)();//不写也可以在某些地方,但是不提倡,但是不报错可以用
int (*Max)(int,int);
int (*Num)(int a,int b);//都是对的
Max = max;
Min = min;
Num = num;
printf("1找出大的,2找出小的,3加一起,你想干啥\n");
scanf("%d",&i);
switch(i){
case 1:
printf("哈哈 来啦:%d\n",(*Max)(&a,&b));
pfunc = max;
break;
case 2:
printf("哈哈 来啦:%d\n",(*Min)(&a,&b));
pfunc = min;
break;
case 3:
printf("哈哈 来啦:%d\n",(*Num)(&a,&b));
pfunc = Num;
break;
}
ret = get_summarizing(&a,&b,pfunc);
printf("这个是通过回调函数实现的,结果是%d",ret);
return 0;
}
🎶煲子们,走起
指针数组
废话走起
指针数组就是一个数组,数组中的每个元素都是指针类型。指针数组中的每个元素都可以指向不同类型的数据。指针数组的访问方式与普通数组类似,可以使用下标操作符[]来访问数组中的元素。指针数组常用于实现复杂的数据结构和算法
小试牛刀
#include<stdio.h>
int main()
{
int a = 20;
int b = 10;
int c = 88;
int i;
int *p[3] = {&a,&b,&c};
for(i = 0;i<4;i++){
printf("%d ",*p[i]);
puts("\n");
}
}
挥刀斩鸡(函数的指针数组)
#include<stdio.h>
int max(int a,int b)//找最大值
{
return a>b?a:b;
}
int min(int a,int b)//找最小值
{
return a<b?a:b;
}
int num(int a,int b)//求和
{
return a+b;
}
int main()
{
int i;
int a = 6;
int b = 7;
int ret;
int (*pfunc[3])(int,int) = {max,min,num};//函数的指针数组
//都是对的
for(i = 0;i < 3;i++){
ret = (*pfunc[i])(a,b);//运算符优先级的问题 所以把*Pfunc[i]加了()
printf("%d\n",ret);
}
return 0;
}
指针函数
废话上
指针函数就是返回指针的函数,它返回的指针指向内存中的某个地址,这个地址存储了某个数据对象的值。使用指针函数时需要注意返回的指针必须指向有效的内存地址,
通常会返回指向堆上分配的内存地址的指针。//这句话其实有点拗口,我理解的不多
int *p;//定义一个指针变量
int* p;//定义一个变量,这个变量是int型的指针类型
两个说法,作用差别不大
啾咪啾咪🤞
实践来啦
练习
#include <stdio.h>
int* student(int i,int (*pa)[5])
{
int *p;
p = (int *)(i + pa);//这屋里进行一个强转,不然会出现报错
return p;
}
int main()
{
int a[3][5]= {{99,100,15,97,98},
{93,78,66,88,89},
{12,13,11,101,100}};
int i;
int *p;
puts("请输入0-2的学号:\n");
scanf("%d",&i);
p = student(i,a);
for(i = 0;i<5;i++){
printf("%d ",*p++);
}
}
作业
这个留给你们,也留给我,我复习拿来练手。这里就不写示例代码了
二级指针
老生常谈
二级指针是指向指针的指针,可以用于传递指针的地址,在函数调用中非常有用。需要注意使用时可能会导致内存泄漏和悬挂指针等问题,需要小心使用,并确保指向的内存地址有效,及时释放内存。
概念性的东西是搞起来就是的头疼,重在理解,理解了,就不疼了
#include <stdio.h>
int main()
{
int data = 992;
int **p2;
int *p;
p = &data;
p2 = &p;//重点
printf("data的地址是%p,p存储的地址是%p\n",&data,p);
puts("\n");
printf("data的值是%d, p的地址是%p\n",data,&p);
puts("\n");
printf("p2保存的地址是%p,p2的地址是%p\n",p2,*p2);
puts("\n");
printf("访问p2的值是%d\n",**p2);
}//这里我写的时候又翻车了,
实战
#include <stdio.h>
void student(int i,int (*pa)[5], int **p2)
{
//父数组偏移i,传给二级指针
*p2 = (int *)(pa + i);//这里进行一个强转,不然会出现报错
}
int main()
{
int a[3][5]= {{99,100,15,97,98},
{93,78,66,88,89},
{12,13,11,101,100}};
int i;
int *p;
puts("请输入0-2的学号:\n");
scanf("%d",&i);
student(i,a,&p);
for(i = 0;i<5;i++){
printf("%d ",*p++);
}
}
一遍肯定是不理解的,没有关系的,我能理解,大家不要慌😃哈哈
🤞二级指针和二维数组的必坑指南
兄弟们 我累了,第86节,我写不下去了,最后这一点想看的你们去找一下吧,在图片里
指针完结
指针搞了4天,确实有点久了,但没办法,查漏补缺,主打一个细节,搞起,继续。