c【语言】了解指针,爱上指针(4)

news2024/11/16 19:50:31

了解指针,爱上指针(4)

  • 字符指针变量
  • 数组指针变量
  • 二维数组传参的本质
  • 函数指针变量
  • typedef关键字
  • 函数指针数组
  • 转移表

字符指针变量

如整型指针变量一样,它是指针变量的其中一个类型:char*
一般,我们是这样使用字符指针的:

int main()
{
char ch='w';
char* c=&ch;//字符指针变量c指向&ch
*c='z';
return 0;
}
int main()
{
char ch[]="adsd";
char* c=ch;//字符指针变量c指向ch
for(int i=0;i<4;i++)
{
	*c='z';
	c++;
}
return 0;
}

通过字符指针变量修改字符或者字符数组,这两种情况下,都能够正常的通过字符指针变量来修改、访问并输出指向的地址下的值,但是有一种情况例外:

int main()
{
char*p="abcdef";
return 0;
}

这段代码让指针p指向一段字符串,如果我们通过指针p来输出字符或者字符串,是可以输出的:

在这里插入图片描述
但是当我们想要通过指针p来修改字符串的值时,程序将会崩溃:
在这里插入图片描述
这是因为:将字符串赋值给指针变量实际上是让指针指向字符串首字符的地址

当我们需要输出字符串的首元素时,只需要解引用指针p就行,如上文输出字符’a’一样
当我们需要输出整个字符的时候,不需要解引用,因为指针p指向的就是字符串首元素的地址,知道了字符串首元素的地址,就能成功访问字符。

为什么用指针指向一个字符串时,不能通过指针变量来修改字符串的值呢?我们回想一下,当我们用数组来存放字符串时,修改字符串是被允许的,这又是为什么呢?
原因是:

  • 数组存放字符串,实际上是开辟了一块内存空间,将字符串拷贝了一份到这块空间中
  • 指针指向的字符串,实际上是一个常量字符串,对常量字符串进行修改是不被允许的。(在c/c++中,常量字符串是放在只读数据区的)。

下面,给大家来个小测试:

#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");//1
 else
 printf("str1 and str2 are not same\n");//2
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");//3
 else
 printf("str3 and str4 are not same\n");//4
 
 return 0;
}

这段代码输出第2、3两句。原因:第一个判断的是两个数组的首地址是否相同,第二个判断的是两个字符指针变量指向的地址是否相等

  1. 数组存储字符串是单独开辟一块连续的空间用来放数据,所以一个数组开辟一块空间,两个数组就开辟两块空间,既然不是同一块空间,那么它们的地址也就不一样。
  2. 字符指针变量存储的是字符串首字符的地址,被存储的字符串叫“常量字符串”,是不会被修改的,因为它被放在“只读数据区”,既然它不会被修改那么在用到它的时候,只需要将首字符的地址传过去就行了,不需要开辟单独的空间,同时我们回归指针变量的本质“用来存放地址的变量”,所以,str3==str4为真,输出第三句。

数组指针变量

数组指针变量:是指针变量,用来存放的是数组的地址。

声明数组指针变量:

int (*p)[4];
//它的类型是:int(*)[4];
//[4]表示指向的数组中存放四个数据
//int 表示指向的数组是一个整型数组
//因为下标引用符[]的优先级高于*,所以要加括号。否则就变成看指针数组了

数组指针变量的初始化:

int main()
{
int a[4]={1,2,3,4};
int(*p)[4]=&a;
return 0;
}

既然是存放数组地址的指针变量,那么初始化的值就是数组的地址。通过调试我们发现&a与p的类型是一样的
在这里插入图片描述

二维数组传参的本质

本质:二维数组传参实际上传递的是第一个元素的地址

二维数组我们可以理解为:“一个存放一维数组的数组”,二维数组的每一个元素是一个一维数组:

int a[3][3]={0};

在这里插入图片描述
二维数组中存放了三个一维数组“a[0]”、“a[1]”、“a[2]”,每个一维数组中存放三个元素。

当我们调用函数并传递二维数组:

void Prin(int a[3][3],int r,int c)
{
	int i=0;
	for(i=0;i<r;i++)
	{
		int j=0;
		for(j=0;j<c;j++)
		{
			printf("%d ",a[i][j]);
		}
	}
}

int main()
{
int a[3][3]={0};
Prin(a,3,3);
return 0;
}

在之前,我们学过一维数组的传参,一维数组的传参实际上传递的是首元素的地址,二维数组也是如此,只不二维数组的首元素是一个一维数组,传递的是第一个一维数组的地址。
在这里插入图片描述
三个既然是数组,那么就有它的类型,三个一维数组的类型是“int[3]”,一维数组地址的类型是"int(*)[3]",我们又知道,二维数组传参的本质“传递的是首元素的地址”,既然是地址,那就可以写成指针的形式,所以上面的代码还可以写成这样子:

void Prin(int(*a)[3],int r,int c)
{
	int i=0;
	for(i=0;i<r;i++)
	{
		int j=0;
		for(j=0;j<c;j++)
		{
			printf("%d ",*(*(a+i)+j));
		}
	}
}

int main()
{
int a[3][3]={0};
Prin(a,3,3);
return 0;
}
为什么a[3][3]可以写成*(*(a+i)+j)?
我们可以这样理解:
我们知道数组名就是数组首元素的地址,因为是二维数组,所以首元素地址+i可以理解为第几行。
再对a+i解引用就得到了那一行的一维数组的首元素地址,让这个地址+j,就表示,访问这个数组的第j个元素的地址,解引用就得到相应的值

在这里插入图片描述

总结

二维数组传参实际上传递的是首元素的地址
⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

函数指针变量

函数指针:用来存放函数地址的指针。

变量有地址,数组有地址,函数当然也有地址。
在这里插入图片描述
如果我们要把函数的地址存放起来,就要用到函数指针。

函数指针变量与数组指针变量非常类似:

void test(int a)
{
    printf("%d",a);
}

int main()
{
    void(*p)(int a)=test;//也可以写成int(*p)(int a)=&test,因为函数名就是函数的地址
    //int(*p)(这个括号里可以只写参数的类型,不写参数)比如int(*p)(int)
	p(1);//函数指针的使用,也可以写成(*p)(1)
    return 0;
}

函数指针解析:

用上面的代码举例
void(*p)(int a);
void表示指向函数的返回类型
p表示函数指针变量名
(int a)指向函数的参数

关于函数指针两段有趣的代码
代码1:

(*(void (*)())0)();

这段代码运行起来,程序会崩掉。

关于这段代码可以这么理解:将0将转成void(*)()类型,转换后可以看出(*0)(),此时0就是一个函数指针类型,把0用p代替(*p)(),就相当于调用p处的函数,而p又等于0,所以就是调用0地址处的函数。

代码2:

void (*signal(int , void(*)(int)))(int);

关于这段代码可以这么理解:这其实是一个函数指针的声明,函数指针类型是”void()(int)“,变量名也是一个函数signal(int,void(*)(int)),这里接收两个参数,一个是int类型的,一个是void()(int)类型的。

typedef关键字

typedef关键字是用来为类型重命名的,可以将复杂的类型简单化。

typedef struct student 
{
	int age;
}stu;
int main()
{
stu s1;
return 0;
}

在这里将struct student类型重命名为为stu

typedef unsigned int unit;

这里将无符号整型重命名成了unit

typedef int(*p)(int);
//使用重命名后的类型
假设有一个int test1(int x)函数
p test=test1;
test1(1);

这里将一个int(*)(int)类型重命名为p
关于数组指针指针数组函数指针的重命名,都需要把新的名字放在*号的左边

typedef int*p_t;

这里将int*类型重命名为p_t

这里再提一嘴前面讲到的有趣的代码。
学了typedef,可以把代码2简写成这样:

typedef void(*pfun_t)(int);
pfun_t signal(int,pfun_t);

函数指针数组

函数指针数组:是数组,用来存放函数的指针(地址)

int Add(int x,int y)
{
    return x+y;
}

int sub(int x,int y)
{
    return x - y;
}

int main()
{
    int(*p[2])(int, int) = {Add,sub};//函数指针数组的数组名是p,存放的元素类型是"int(*)(int,int)",存放了两个这样的元素。
    for (int i = 0; i < 2; i++)
    {
        printf("%d",p[i](4,3));
    }
    return 0;
}

数组我们知道是”里面存放的元素的类型都是相同的“,函数指针数组也是。

函数指针数组存放的是函数的地址,要求:函数指针变量的类型相同。

转移表

void menu()
{
    printf("*************************************************\n");
    printf("*************************************************\n");
    printf("*******1.Add    2.Sub    3.Div      4.Mul********\n");
    printf("********************0.退出***********************\n");
    printf("*************************************************\n");
}

int Add(int x, int y)
{
    return x + y;
}

int Sub(int x, int y)
{
    return x - y;
}

int Div(int x, int y)
{
    return x / y;
}

int mul(int x, int y)
{
    return x * y;
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    int (*p[5])(int x, int y) = { 0,Add,Sub,Div,mul };
    do
    {
        printf("请选择:");
        scanf("%d",&input);
        if (input>=1&&input<=4)
        {
            printf("请输入两个操作数:");
            scanf("%d %d", &x, &y);

            ret = (*p[input])(x, y);
            printf("%d\n", ret);
        }
        else if (input == 0)
        {
            printf("退出计算器\n");
        }
        else
        {
            printf("请重新输入\n");
        }


    } while(input);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通胀担忧仍存,美联储降息预期或又推迟

KlipC报道&#xff1a;周三&#xff0c;美联储公布4月30日至5月1日政策会议纪要&#xff0c;会议纪要显示美联储对通胀仍感到担忧&#xff0c;将更长时间维持利率不变&#xff0c;必要时进一步收紧政策。 尽管在前不久公布的4月CPI数据显示通胀有所缓解&#xff0c;但是被认为…

网站如何建设

#### 环境准备 - 安装Java Development Kit (JDK)&#xff1a;这是Java开发的基础&#xff0c;用于编译Java源代码。 - 安装Web服务器&#xff1a;常用的有Tomcat、Jetty、GlassFish或JBoss。它们负责将Java应用程序部署到Web上&#xff0c;并处理来自客户端的请求。 - 安装…

flutter开发实战-美颜前后对比图效果实现

flutter开发实战-美颜前后对比图效果实现 最近使用代码中遇到了图片前后对比&#xff0c;这里使用的是CustomClipper来实现 一、CustomClipper 我们实现CustomClipper子类来实现美颜后的图片裁剪功能 getClip()是用于获取剪裁区域的接口&#xff0c;由于图片大小是6060&am…

一张SSL证书如何同时保护多个域名及其子域名?

在互联网时代&#xff0c;数据安全和隐私保护变得至关重要&#xff0c;而SSL证书作为确保网站安全的重要工具&#xff0c;其重要性不言而喻。本文将详细探讨一种特殊的SSL证书——多域名通配符SSL证书&#xff0c;它为网站管理员提供了一种高效、经济的方式来保护多个域名及其子…

第一后裔加速器推荐 第一后裔免费加速器用哪个

知名游戏开发商NEXON对于许多老玩家来说都不会陌生&#xff0c;它旗下的泡泡堂和DNF可谓是一代人的青春。就在最近NEXON又为玩家们带来了最新作品《第一后裔》&#xff0c;该款游戏为搭建在虚幻5引擎上的一款多人联机射击掉宝类游戏&#xff0c;一上线就受到了许多游戏玩家的关…

高速公路定向广播(声光一体) HT-600D

1、产品概述&#xff1a; HT-600D声光一体平面波IP定向广播是北京恒星科通创新性研发产品&#xff0c;采用公司自主研发的平面波传声技术&#xff0c;该产品具有高声压、强指向性、高清晰度等特点&#xff0c;采用定向声传声技术将声音聚集到正前方定向传输,周边声压级明显降低…

【EXCEL_VBA_实战】两组数据比对是否一致(字符串数组)

工作背景&#xff1a;比对两组数据是否一致&#xff08;位置非一一对应&#xff09; 思路构建&#xff1a;两组数据转换为两组字符串数组&#xff0c;比对所包含元素是否相同 问题点&#xff1a;A数组的第一个元素不一定与B数组的第一个元素对应&#xff0c;此时无法通过公式…

数据仓库和数据挖掘基础

文章目录 1. 数据仓库基础知识1.1 数据仓库的基本特性1.2 数据仓库的数据模式1.3 数据仓库的体系结构 2. 数据挖掘基础知识2.1 数据挖掘的分类2.2 数据挖掘技术2.3 数据挖掘的应用过程 传统数据库在联机事务处理(OLTP)中获得了较大的成功&#xff0c;但是对管理人员的决策分析要…

SAP---成本中心采购跟消耗性采购的区别

1.常规库存采购业务的说明&#xff1a; 1.从业务层面分析&#xff0c;企业的常规库存物料采购是&#xff1a; 采购部门下采购订单后&#xff0c;供应商送货&#xff0c;当货物到厂后&#xff0c;由库管员执行收货操作&#xff0c;先将货物收到仓库中&#xff0c;再由各个需求…

tomcat jdbc连接池的默认配置配置方案

MySQL 5.0 以后针对超长时间数据库连接做了一个处理&#xff0c;即一个数据库连接在无任何操作情况下过了 8 个小时后(MySQL 服务器默认的超时时间是 8 小时)&#xff0c;MySQL 会自动把这个连接关闭。在数据库连接池中的 connections 如果空闲超过 8 小时&#xff0c;MySQL 将…

肌肤暗沉与胶原蛋白:解锁透亮肌肤的秘密

&#x1f338;亲爱的小仙女们&#xff0c;今天我们来聊聊肌肤暗沉与胶原蛋白之间的神秘联系。你是不是也曾为肌肤的暗沉而烦恼&#xff1f;其实&#xff0c;很多时候&#xff0c;肌肤的暗沉不仅仅是外部因素造成的&#xff0c;更与肌肤内部的胶原蛋白含量密切相关。&#x1f31…

element ui 下拉框Select 选择器 上下箭头旋转方向样式错乱——>优化方案

目录 前言1、问题复现2、预期效果3、input框样式修改解析4、修改方案 &#x1f680;写在最后 前言 测试A&#xff1a;那啥&#xff01;抠图仔&#xff0c;样式怎么点着点着就出问题了。 前端&#xff1a;啥&#xff1f;css样式错乱了&#xff1f;你是不是有缓存啊&#xff01…

智慧仓储新动力:EasyCVR+AI视频智能监管系统方案助力仓储安全高效管理

一、背景 随着物流行业的快速发展和智能化水平的提升&#xff0c;智慧仓储视频智能监管系统已成为现代仓储管理的重要组成部分。本系统通过综合运用物联网、视频分析、边缘计算等技术手段&#xff0c;实现对仓储环境的全面监控、智能分析和高效管理。 TSINGSEE青犀视频汇聚Ea…

李斌阻击马斯克,也不放过李想

市场唯一不变的就是变化。 当年特斯拉开放专利&#xff0c;引起了国内电动车的创业潮&#xff0c;蔚来比小鹏、理想早几个月成立&#xff0c;也是造车新势力中首家实现交付的品牌。 但时过境迁&#xff0c;现在已经不是蔚来领衔“蔚小理”的时代了&#xff0c;理想是其中销量…

Java对象的内存分配机制

下面以一段代码为示例&#xff1a; public class Person{int age;String name; } class Test{public static void main(String[]args){Person A new Person();A.age10;A.name"张三";System.out.println(A.age);System.out.println(A.name);} }

【话题】你眼中的IT行业现状与未来趋势

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 引言一、IT行业的现状1.1 云计算与大数据1.2 人工智能与机器学习1.3 物联网与5G通信1.4 区块链技术 二、IT行业未来发展趋势2.1 边缘计算与智能设备2.2 深度学习与自然语…

SBC3568启动升级,灵活更换动画logo

今天小智将会带着大家体验如何在openharmony sdk内替换开机logo和动态动画。 1. 更换开机logo 开机logo分为uboot阶段【logo.bmp】和kernel阶段【logo_kernel.bmp】的logo两个文件&#xff0c;对图片的要求是&#xff1a;必须为bmp格式&#xff0c;8或者24位深&#xff0c;且…

小程序-收货地址管理模块实现

页面结构代码&#xff1a; address-form.vue --->新建地址和修改地址页面 <template><view class"content"><form><!-- 表单内容 --><view class"form-item"><text class"label">收货人</text>…

青少年 CTF 练习平台:Misc(一)

前言 当然&#xff0c;我可以更详细地介绍一下青少年CTF练习平台。 青少年CTF练习平台是一个专为青少年设计的网络安全竞赛和训练平台。该平台由思而听&#xff08;山东&#xff09;网络科技有限公司与克拉玛依市思而听网络科技有限公司共同建设&#xff0c;自2018年创建以来…

IDEA连接MySQL后如何管理数据库

上一节讲解了IDEA如何连接MySQL数据库管理系统&#xff0c;接下来我们就可以在IDEA里使用MySQL来管理数据库了。那么如果我们现在还没有创建需要的数据库怎么办&#xff1f;本节就来教大家如何在IDEA连接MySQL后管理数据库(创建/修改/删除数据库、创建/修改/删除表、插入/更新/…