C语言指针详解

C语言指针详解

  • 字符指针
    • 1.如何定义
    • 2.类型和指向的内容
    • 3.代码例子
  • 指针数组
    • 1.如何定义
    • 2.类型和内容
  • 数组指针
    • 1.如何定义
    • 2.类型和指向类型
    • 3.数组名vs&数组名
    • 数组指针运用
  • 数组参数&指针参数
    • 一维数组传参
    • 二维数组传参
    • 一级指针传参
    • 二级指针传参
  • 函数指针
    • 1.如何定义
    • 2.类型和指向内容
    • 3.函数名vs&函数名
    • 4.两个有趣的代码
  • 函数指针数组
    • 1.如何定义
    • 2.类型和内容
    • 3.1代码例子(switch语句实现计算器)
    • 3.2代码例子(函数指针数组实现计算器)
  • 指向函数指针数组的指针
    • 1.如何定义
    • 2.类型和指向内容
  • 回调函数
  • 一个小知识点如何找到指针和数组的类型和(指向)存储内容

博主主页zoro-1
祝大家有个好心情,给大家分享一下我拍的彩虹
在这里插入图片描述
在这里插入图片描述

字符指针

1.如何定义

int main() {
char ch='w';
char *pc=&ch;
*pc='w';
return 0;
}

2.类型和指向的内容

譬如char pc=&ch;
将=左边pc去掉就是指针类型char

将*和pc去掉就是指针指向的内容char

3.代码例子

例1

int main()
{
const char* pstr = "hello";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;}

这时我就要问一个问题了,pstr存储的是什么?
没错pstr存储的是hello的首元素地址

例2

#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");
return0;

大家猜猜这段代码会输出什么?
在这里插入图片描述
代码解释:str1,str2是存放char的数组的数组名,那么他们存放的就是数组首元素的地址,即使他们的内容相同,但地址是随机的,而str3,str4是被const修饰的指针变量,存放的也是字符的地址只不过这里的hello bit.在这里是存放在常量池的下一个str4不需要开辟新的空间,所以str3,str4他们指向的是同一个hello bit.所以相同

一个小知识点:
如果 const 用于修饰字符串常量,那么该字符串常量将存储在常量存储区(Constant Storage Area)。
常量存储区是用于存储常量字符串和全局常量的特殊内存区域,其中的数据在程序运行期间保持不变。

指针数组

1.如何定义

int main(){
int arr[5]={1,2,3,4,5};
int arr1[5]={2.3.4.5.6};
int arr2[5]={3,4,5,6,7};
int*p[3]={arr,arr1,arr};
}

2.类型和内容

去掉名字p就是类型
去掉p【3】就是存储的内容int*

数组指针

1.如何定义


int arr[5]={1,2,3,4,5};
int (*p)[10]=&arr;
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指
向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

2.类型和指向类型

去掉名字p就是类型
去掉*p就是指向内容

3.数组名vs&数组名

数组名是数组的首元素地址
但有两个例外:
1.&arr得arr表示整个数组的地址
2.sizeof(arr)表示整个数组的大小

#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}

大家猜猜这段代码执行结果是什么,如果看过我之前的指针初阶就应该知道他们的输出内容一样
在这里插入图片描述
代码解释:arr是数组首元素地址,&arr是整个数组的地址,虽然他们输出的内容一样,但是他们的权重不一样,
arr+1会跳过4字节,&arr+1会跳过整个数组也就是10*4,一共40个字节

#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}

在这里插入图片描述
这段代码就能很好解释&arr和arr的区别了

数组指针运用

我们很少用在一维数组,多数用在二维数组,接下来我用代码让大家感受一下数组指针
一维数组:

#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
//但是我们一般很少这样写代码
return 0;
}

二维数组:

一个数组指针的使用:
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);//相当于*(*(arr+i)+j)
}
printf("\n");
}
}
#include <stdioh>
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
我们发现这两个遍历数组的1方法只有接收数组第一个形参不一样这里写成int arr[3][5]是为了大家好理解,
编译器会将int arr[3][5]转化成int (*arr)[5]
print_arr2(arr, 3, 5);
return 0;
}

在这里插入图片描述

数组参数&指针参数

在写代码时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

一维数组传参

#include <stdio.h>
void test(int arr[])//ok?
显然是可以的传一维数组用一维数组接收
{}
void test(int arr[10])//ok?
显然是可以的只是加上了长度
{}
void test(int *arr)//ok?
这种可以int*arr是整型指针,一维数组传过来是第一个元素地址,第一个元素也是整型
{}
void test2(int *arr[20])//ok?
这种显然可以的传的是指针数组,也是用指针数组接收
{}
void test2(int **arr)//ok?
传的是指针数组的首元素地址,而元素又是指针所以用二级指针接收,二级指针解引用一次得到首元素(首元素就是指针int*{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}

二维数组传参

void test(int arr[3][5])//ok?
显然是可以的传二维数组用二维数组接收
{}
void test(int arr[][])//ok?
虽然形参是二维数组,但是不能省略列
{}
void test(int arr[][5])//ok?
行可以省略列不能省略所以可以
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?
二维数组的第一个元素是一维数组不能用整形指针接收而应该用数组指针接收
{}
void test(int* arr[5])//ok?
这是指针数组所以不对
{}
void test(int (*arr)[5])//ok?
这是数组指针所以可以{}
void test(int **arr)//ok?
这是二级指针不行,因为传过来的是一维数组地址
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

总结:
一维整形数组传参数,可以用一维整形数组接收,也可以用整形指针接收
一维指针数组,可以用一维指针数组接收,也可以用二维指针接收
二维整形数组传参数,可以用二维整形数组接收,也可以用数组指针接收

一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d\n", *(p+i));
}
}
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);
//也可以直接写成printf(p,sz);
return 0;
}

二级指针传参

当函数参数是二级指针,可以接收哪些参数

void test(char **p)
{
}
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];
test(&pc);
test(ppc);
test(arr);//Ok?
return 0;
}

函数指针

上面介绍了整形指针,数组指针,那么函数有没有指针呢?
答案是有

1.如何定义

#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
void (*p)()=&test;
return 0;
}

2.类型和指向内容

去掉名字p就是类型
去掉*p就是指向内容

3.函数名vs&函数名

#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

注:函数名是函数地址,&函数名也是函数地址,没有区别
在这里插入图片描述

4.两个有趣的代码

:推荐《C陷阱和缺陷》
这本书中提及这两个代码。
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

*代码1解释是将0看做成地址强制转化成函数指针然后解引用,调用函数
代码2解释signal()是一个函数有两个参数int,viod(*)(int),返回值是一个函数指针
代码2简化
typedef void(pfun_t)(int);
pfun_t signal(int, pfun_t);

函数指针数组

有指针数组,那么有没有函数指针数组呢
答案是有

1.如何定义


#include <stdio.h>
#include <string.h>int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}int main()
{//int (*pf1)(int, int) = Add;//int (*pf2)(int, int) = Sub;//int (*pf3)(int, int) = Mul;//int (*pf4)(int, int) = Div;//函数指针数组//int (*pfArr[4])(int, int) = {Add, Sub, Mul, Div};//return 0;
}

2.类型和内容

去掉pfArr就是类型
去掉pfArr【4】就是存储的内容函数指针

3.1代码例子(switch语句实现计算器)

如果让你们实现自算器的简易功能,你们会怎么实现

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}
//目录
void menu()
{printf("***************************\n");printf("*****  1.add  2.sub  ******\n");printf("*****  3.mul  4.div  ******\n");printf("*****  0.exit        ******\n");printf("***************************\n");
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret = %d\n", ret);break;case 2:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}

有没有感觉上面这段代码很冗杂需要这么多次case
接下来我用函数指针数组再来实现一下

3.2代码例子(函数指针数组实现计算器)

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}//...void menu()
{printf("***************************\n");printf("*****  1.add  2.sub  ******\n");printf("*****  3.mul  4.div  ******\n");printf("*****  0.exit        ******\n");printf("***************************\n");
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;//函数指针数组的使用 - 转移表int (* pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};//                            0     1    2    3    4do{menu();printf("请选择:>");scanf("%d", &input);if (input >= 1 && input <= 4){printf("请输入两个操作数:");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else if(input == 0){printf("退出计算器\n");}else{printf("选择错误,重新选择\n");}} while (input);return 0;
}

这里我们将函数储存在数组里面,直接通过下标调用是不是感觉很方便

指向函数指针数组的指针

这里给大家表演一个套娃,是不是感觉很绕口,这里我们不过多解释只讲定义(用处不多);

1.如何定义

void test(const char* str)
{printf("%s\n", str);
}int main()
{void (*pf)(const char*) = test;//pf是函数指针变量void (*pfArr[10])(const char*);//pfArr是存放函数指针的数组void (* (*p) [10])(const char*) = &pfArr;//p指向函数指针数组的指针return 0;
}

2.类型和指向内容

去掉p就是类型
去掉*p就是指向内容

回调函数

在这里插入图片描述

这个意思就是有函数A,B,main方法调用函数A将函数B地址作为参数传到函数A,A中利用函数地址调用函数B
这个我会在下一篇博客讲解qsort时举例讲解,

一个小知识点如何找到指针和数组的类型和(指向)存储内容

*类型都是去掉名字
指针指向的内容是去掉(指针名字)
数组存储内容是去掉名字和【】(中括号)

更多指针相关内容请听下回讲解,看到这里了,不妨给博主给个三连,要是想持续收听,也可以关注博主, 让我们一起变得更强吧,大家加油!!!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/73706.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【前端知识】React 基础巩固(三十九)——React-Router的基本使用

React 基础巩固(三十九)——React-Router的基本使用 一、Router的基本使用 Router中包含了对路径改变的监听&#xff0c;并且会将相应的路径传递给子组件。 Router包括两个API&#xff1a; BrowserRouter使用history模式 HashRouter使用hash模式&#xff08;路径后面带有#号…

APP自动化测试-Python+Appium+Pytest+Allure框架实战封装(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 pytest只是单独的…

无人驾驶实战-第五课(动态环境感知与3D检测算法)

激光雷达的分类&#xff1a; 机械式Lidar&#xff1a;TOF、N个独立激光单元、旋转产生360度视场 MEMS式Lidar&#xff1a;不旋转 激光雷达的输出是点云&#xff0c;点云数据特点&#xff1a; 简单&#xff1a;x y z i &#xff08;i为信号强度&#xff09; 稀疏&#xff1a;7%&…

【工具】自动搜索Research网站的学术会议排名

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] Research.com是一个可以搜索学术会议网站的影响因子的网站。 好用是好用&#xff0c;但有一个缺点&#xff1a;得手动选择类目。有这么多类目&#xff0c;一个个手动选也太累了。 所以做了一个自动搜索的小工具&a…

HTTP杂谈之Referer和Origin请求头再探

一 关于Referer和Origin的汇总 1) 知识是凌乱的,各位看官看个热闹即可2) 内容不断更新1、理解有盲区,需要及时纠正2、内容交叉有重复,需要适当删减3、扩展视野3) 以下内容都与Referer和Origin请求头有关联 nginx防盗链 HTTP杂谈之Referrer-Policy响应头 iframe标签referre…

新手入门Jenkins自动化部署入门详细教程

1. 背景 在实际开发中&#xff0c;我们经常要一边开发一边测试&#xff0c;当然这里说的测试并不是程序员对自己代码的单元测试&#xff0c;而是同组程序员将代码提交后&#xff0c;由测试人员测试&#xff1b; 或者前后端分离后&#xff0c;经常会修改接口&#xff0c;然后重新…

vue element el-upload附件上传、在线预览、下载当前预览文件

上传 在线预览&#xff08;iframe&#xff09;&#xff1a; payload&#xff1a; response&#xff1a; 全部代码&#xff1a; <template><div><el-table :data"tableData" border style"width: 100%"><el-table-column prop"d…

.Net6 Core Web API 配置 log4net + MySQL

目录 一、导入NuGet 包 二、添加配置文件 log4net.config 三、创建MySQL表格 四、Program全局配置 五、帮助类编写 六、效果展示 小编没有使用依赖注入的方式。 一、导入NuGet 包 ---- log4net 基础包 ---- Microsoft.Extensions.Logging.Log4Net…

天线辐射机制

电磁场如何从源中产生并最终脱离天线辐射到自由空间中去的呢&#xff1f;让我们首先来研究一下一些基本的辐射源。 1、单线Single Wire 导线是一种电荷运动产生电流特性的材料&#xff0c;假设用qv&#xff08;库仑/m3&#xff09;表示的一个电体积电荷密度均匀分布在一个横截…

ansible控制主机和受控主机之间免密及提权案例

目录 案例描述 环境准备 案例一--免密远程控制主机 效果展示&#xff1a; 解决方案 1.添加主机 2.通过ssh-key生成密钥对 3.生成ssh-copy-id 4.验证 案例二-----免密普通用户提权 效果展示 解决方案 1.使用普通用户&#xff0c;与案例一 一样&#xff0c;进行发送密钥…

pytest 自定义HOOK函数

除了系统提过的HOOK函数外&#xff0c;也可以通过自定义HOOK的方式实现想要的功能。 首先创建一个py文件&#xff0c;里面定义自己的HOOK函数&#xff0c;主要pytest里面的hook函数必须以pytest开头。 #myhook.pydef pytest_myhook(user):"""自定义HOOK函数&q…

【JavaEE初阶】Servlet(四) Cookie Session

文章目录 1. Cookie && Session1.1 Cookie && Session1.2 Servlet会话管理操作 1. Cookie && Session 1.1 Cookie && Session Cookie是什么? Cookie是浏览器提供的持久化存储数据的机制.Cookie从哪里来? Cookie从服务器返回给浏览器. 服务…

中小企业如何做好MES管理系统实施建设

中小企业在生产制造领域面临着诸多挑战&#xff0c;包括提升产品竞争力、规范生产制造等。为了应对这些挑战&#xff0c;越来越多的中小企业开始实施MES生产管理系统。然而&#xff0c;由于企业规模小、资源实力不足等原因&#xff0c;很多企业在实施MES管理系统时存在一定的困…

opencv rtsp 硬件解码

讨论使用opencv的reader 硬件解码的方案有太多种&#xff0c;如果使用ffmpeg硬件解码是最方便的&#xff0c;不方便的是把解码过后的GPU 拉到 CPU 上&#xff0c;再使用opencv的Mat 从cpu 上上载到gpu上&#xff0c;是不是多了两个过程&#xff0c;应该是直接从GPU mat 直接去…

腾讯测试大佬分享4个关于 Python 函数(方法)的冷知识

关于参数标识 不知道大家在工作中有没有遇到一种情况&#xff0c;你的同事 A 写了一个方法给你调用&#xff0c;然后你调用时不知道该传什么参数&#xff0c;然后这个同事 A 还很 cao dan 的居然不加班&#xff01;你一脸茫然的看着这个方法&#xff0c;当你尝试传进去一个 ab…

使用pg_prewarm缓存PostgreSQL数据库表

pg_prewarm pg_prewarm 直接利用系统缓存的代码,对操作系统发出异步prefetch请求&#xff0c;在应用中&#xff0c;尤其在OLAP的情况下&#xff0c;对于大表的分析等等是非常耗费查询的时间的&#xff0c;而即使我们使用select table的方式&#xff0c;这张表也并不可能将所有…

后端整理(MySql)

1 事务 1.1 事务ACID原则 原子性&#xff08;Atomicity&#xff09; 事务的原子性指的是事务的操作&#xff0c;要么全部成功&#xff0c;要么全部失败回滚 一致性&#xff08;Consistency&#xff09; 事务的一致性是指事务必须使数据库从一个一致状态转变成另一个一致性…

hugging face下载数据集

开始直接执行这个&#xff0c;下载下来的图片打不开 git clone https://huggingface.co/datasets/diffusers/dog-example 解决办法&#xff1a; 安装git lfs 1. curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash 2. sudo apt…

会议OA系统会议管理模块开发思路(layui搭建)

目录 一.为什么要进行开发 1.开发目的 2.项目流程 A.发起会议请求过程 1.首先实现我们的多选下拉框功能&#xff01; 2.时间组件功能&#xff0c;并且提交我们新增加的会议内容 3.在进行发起会议编码时遇到的问题&#xff0c;BUG 3.1.有点时候js访问不到路径 3.2在增加…

Ctfshow web入门 JWT篇 web345-web350 详细题解 全

CTFshow JWT web345 先看题目&#xff0c;提示admin。 抓个包看看看。 好吧我不装了&#xff0c;其实我知道是JWT。直接开做。 在jwt.io转换后&#xff0c;发现不存在第三部分的签证&#xff0c;也就不需要知道密钥。 全称是JSON Web Token。 通俗地说&#xff0c;JWT的本质…