【重生之我要苦学C语言】深入理解指针4

深入理解指针4

字符指针变量

指针指向字符变量

char ch = 'w';
char* p = &ch;

指针指向字符数组

char arr[10] = "abcdef";
char* p = arr;
printf("%s\n", arr);
printf("%s\n", p);

结果是一样的

也可以写成:

char* p = "abcdef";//常量字符串
//将字符串首字符a的地址赋值给p

字符数组可以存放字符串,字符数组的内容可以修改
常量字符串和数组是非常相似的,也是在一个连续的空间中存放了多个字符,但是常量字符串的内容不能修改

*p='w';//错误的,常量字符串不能被修改

因此可以写为:

const char* p = "abcdef";

看一道题:

int main(){char strl[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("strl and str2 are same\n");//1elseprintf("strl and str2 are not same\n"); //2if (str3 == str4)printf("str3 and str4 are same\n"); //3elseprintf("str3 and str4 are not same\n");//4return 0;
}

问:打印结果
答案为:2 3

str3和str4完全相同,为了节省内存,str4并不会被创建

在这里插入图片描述

str1,str2为数组名,是数组首元素的地址,两个数组创建的是不同的空间,首元素地址不同,故str1!=str2
str3,str4比较的是两个指针变量中存放的地址,所以str3==str4

数组指针变量

存放的是数组的地址——指向数组的指针

&arr——数组的地址

int arr[5] = { 0 };
int(*p)[5] = &arr;
//p为数组指针变量
//int: p指向的数组的元素类型
char arr[8];
char(*pc)[8] = &arr;
char* arr[8];
char*(*pc)[8] = &arr;//
//pc数组指针变量

int arr[6] = { 1,2,3,4,5,6 };
int*(*p)[6] = &arr;

为例:
因为:

  *p = *&arr = arr;

所以求数组长度:

printf("%zd\n", sizeof(arr));

或者:

printf("%zd\n", sizeof(*p));

输出数组:

for (i = 0;i < 6;i++) {printf("%d ", arr[i]);
}

或者:

for (i = 0;i < 6;i++) {printf("%d ", (*p)[i]);
}

但最简单的方法还是取出数组首元素的地址:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {int arr[6] = { 1,2,3,4,5,6 };int* p = arr;int i = 0;for (i = 0;i < 6;i++) {printf("%d ", p[i]);}return 0;
}

二维数组传参本质

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Print(int arr[3][5], int r, int c) {//形参写成数组形式int i = 0, j = 0;for (i = 0;i < r;i++) {for (j = 0;j < c;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} };//  1  2  3  4  5//  2  3  4  5  6//  3  4  5  6  7//写一个函数打印arr数组Print(arr, 3, 5);//实参传数组名return 0;
}

二维数组每一行都是一个一维数组,所以二维数组可以理解为一维数组的数组,也就是说二维数组的每个元素其实是个一维数组

Print(arr, 3, 5);

arr是二维数组的数组名,数组名表示数组首元素的地址(就是二维数组第一行的地址)

根据前面学过的一维数组的传参本质,可知,二维数组的形参可以写为:

void Print(int (*arr)[5], int r, int c) {
 p[i][j]=*(*(p+i)+j)*(p+i)——>第i+1行数组名

函数指针变量

printf("%p\n", &Add);//函数的地址
printf("%p\n", Add);//函数名也是函数的地址
int (*pf)(int x, int y ) = &Add;
int (*pf)(int x, int y ) = Add;
//pf就是函数指针变量
//形参的名字不会被使用,也可以省略
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
//pf=Add
int r=(*pf)(a, b);
int r = Add(a, b);
int r=pf(a, b);
//一样

指针可以指向任何内存中的对象,变量、数组、函数

辨析两个语句:

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

可以分开来看:

void(*)()函数指针类型
(void(*)())0强制类型转换,0变成了地址,0的位置是一个函数
*(void(*)())0解引用
(*(void(*)())0)()函数调用
  • 将0这个整数强制转换成函数指针类型
  • 调用0地址处的函数
2
void(*signal(int, void(*)(int)))(int);
signal函数名
(int, void(*)(int))函数的两个参数
void(*)(int)指针类型
void(*……)(int);函数指针类型——>函数返回类型是一个函数指针类型,signal(int, void(*)(int))为函数名(参数,参数)

上面的代码是一个函数的声明
函数的名字是signal
signal函数的参数有两个,第一个是int类型,第二个是函数指针类型void(*)(int),这个函数指针指向的函数有一个int参数,返回类型是void
signal函数的返回值类型也是一个函数指针类型,这个函数指针指向的函数有一个int参数,返回类型是void

typedef关键字

typedef 是用来类型重命名的,可以将复杂的类型简单化

typedef unsigned int uint;//将unsigned int重命名为uint

指针类型重命名

typedef int* ptr_t;
//指针类型int*重命名为 ptr_t:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef int* ptr_t;
int main() {int* p1, p2;ptr_t p3, p4;return 0;
}

p2不是int*类型

在这里插入图片描述
应变为:

int* p1,* p2;

数组指针类型重命名

typedef int(*parr_t)[5]; //新的类型名必须在*的右边
//数组指针类型int(*)[5],需要重命名为parr_t

函数指针类型的重命名

 typedef void(*pf_t)(int);//新的类型名必须在*的右边//将 void(*)(int)类型重命名为 pf_t

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

将这段代码简化:

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

函数指针数组

 类比:指针数组char* arr1[5];//字符指针数组int* arr2[6];//整形指针数组

函数指针数组是数组,里面存放的都是函数的地址

int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) { return x - y; 
}
int main() {int (*pf1)(int, int) = Add;int (*pf2)(int, int) = Sub;//参数和返回类型一样int (*parr[4])(int ,int) = {Add,Sub};

在这里插入图片描述

调用:

int r = parr[0](100, 200);//300
int r = parr[1](100, 200);//-100

转移表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add    2.sub ***\n");printf("*** 3.mul    4.div ***\n");printf("***     0.exit     ***\n");printf("**********************\n");
}
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; 
}
//写一个简易的计算器
//1.实现加减乘除
//2.不需要退出程序可以继续计算
int main() {int input = 0;do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;switch (input) {case 1:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Add(a, b);printf("%d\n", r);break;case 2:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Sub(a, b);printf("%d\n", r);break;case 3:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Mul(a, b);printf("%d\n", r);break;case 4:printf("请输入两个操作数");scanf("%d%d", &a, &b);r = Div(a, b);printf("%d\n", r);break;case 0:printf("退出计算器");break;default:printf("选择错误,重新选择");break;}} while (input);return 0;
}

如果想增加计算器的功能,那么代码就会越来愈长,越来越冗余
这时就可以使用函数指针数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu() {printf("**********************\n");printf("*** 1.add    2.sub ***\n");printf("*** 3.mul    4.div ***\n");printf("***     0.exit     ***\n");printf("**********************\n");
}
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 input = 0;//转移表int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//整型双目运算//指针类型和指针指向参数的返回类型要一样do{menu();printf("请选择");scanf("%d",&input);int a = 0, b = 0, r = 0;if (input == 0) {printf("退出计算器");}else if (input >= 1 && input <= 4) {printf("请输入两个操作数");scanf("%d%d", &a, &b);int r=pfArr[input](a, b);printf("%d\n", r);}else printf("选择错误,重新选择");} while (input);return 0;
}

END……

午后读一本书。晚上在杏花树下
喝酒,聊天,直到月色和露水清凉。
在梦中,行至岩凤尾蕨茂盛的空空山谷,
鸟声清脆,一起在树下疲累而眠。
醒来时,我尚年少,你未老。
——庆山

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

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

相关文章

Freertos学习日志(1)-基础知识

目录 1.什么是Freertos&#xff1f; 2.为什么要学习RTOS&#xff1f; 3.Freertos多任务处理的原理 1.什么是Freertos&#xff1f; RTOS&#xff0c;即&#xff08;Real Time Operating System 实时操作系统&#xff09;&#xff0c;是一种体积小巧、确定性强的计算机操作系统…

勒索软件通过易受攻击的 Cyber​​Panel 实例攻击网络托管服务器

一个威胁行为者&#xff08;或可能多个&#xff09;使用 PSAUX 和其他勒索软件攻击了大约 22,000 个易受攻击的 Cyber​​Panel 实例以及运行该实例的服务器上的加密文件。 PSAUX 赎金记录&#xff08;来源&#xff1a;LeakIX&#xff09; Cyber​​Panel 漏洞 Cyber​​Pane…

基于vue3和elementPlus的el-tree组件,实现树结构穿梭框,支持数据回显和懒加载

一、功能 功能描述 数据双向穿梭&#xff1a;支持从左侧向右侧转移数据&#xff0c;以及从右侧向左侧转移数据。懒加载支持&#xff1a;支持懒加载数据&#xff0c;适用于大数据量的情况。多种展示形式&#xff1a;右侧列表支持以树形结构或列表形式展示。全选与反选&#xf…

Linux入门-基础指令和权限

1.压缩打包 1.1压缩是什么 压缩是通过特定的算法&#xff0c;使文件减小体积&#xff0c;从而达到节省空间的目的。 1.2.为什么要压缩 a.压缩将文件大小减小&#xff0c;在本地可能不太明显&#xff0c;但是在网络传输中&#xff0c;减小了网络传输的成本。 b.将多个文件压…

WPF中如何解决DataGrid的Header没有多余的一行

将最后一行设置DataGridTemplateColumn Width"*" 使其自适应

Qt/C++地图雷达扫描/动态扇形区域/标记线实时移动/轮船货轮动态轨迹/雷达模拟/跟随地图缩放

一、前言说明 地图雷达扫描的需求场景也不少&#xff0c;很多人的做法是直接搞个覆盖层widget&#xff0c;在widget上绘制雷达&#xff0c;优缺点很明显&#xff0c;优点是性能高&#xff0c;毕竟直接在widget上绘制性能明显比js中绘制要高&#xff0c;缺点是要么动态计算经纬…

Java | Leetcode Java题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; class Solution {int[] pre;int total;public Solution(int[] w) {pre new int[w.length];pre[0] w[0];for (int i 1; i < w.length; i) {pre[i] pre[i - 1] w[i];}total Arrays.stream(w).sum();}public int pickIndex() {int x …

uni-app自定义弹窗

1、项目根目录components目录下创建/modal/modal.vue文件 2、modal.vue文件内容 vue2版本&#xff1a; <template><view class"modal-container"><view class"bg" tap"maskClose"></view><view class"box&quo…

Python小游戏20——超级玛丽

首先&#xff0c;你需要确保你的Python环境中安装了pygame库。如果还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; bash pip install pygame 运行效果展示 代码展示 python import pygame import sys # 初始化pygame pygame.init() # 设置屏幕尺寸 screen_width …

木马病毒相关知识

1、 木马的定义 相当于一个远控程序&#xff08;一个控制端[hack]、一个被控端[受害端]&#xff09; 在计算机系统中&#xff0c;“特洛伊木马”指系统中被植入的、人为设计的程序&#xff0c;目的包括通过网终远程控制其他用户的计算机系统&#xff0c;窃取信息资料&#xff0…

Apache POI(java操作Miscrosoft Office)

Apache POI 1.1 介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下&#xff0c;POI 都是用于操作 Excel 文件。 Apache POI 的应用场景&a…

【Docker】安装registry本地镜像库,开启Https功能

下载镜像 docker pull registry:2 需要启动https功能&#xff0c;就要生成服务端的自签名的证书和私钥&#xff0c;以及在docker客户端安装这个经过签名的证书。 第一步&#xff1a;生成公私钥信息&#xff0c;第二步&#xff0c;制作证书签名申请文件&#xff0c; 第三步&…

Python generator 生成杨辉三角

一、题目描述 先看来自于 廖雪峰老师的一道 Python 练习题 杨辉三角定义如下&#xff1a; 1/ \1 1/ \ / \1 2 1/ \ / \ / \1 3 3 1/ \ / \ / \ / \1 4 6 4 1/ \ / \ / \ / \ / \ 1 5 10 10 5 1把每一行看做一个list&#xff0c;试写一个generato…

【06】A-Maven项目SVN设置忽略文件

做Web项目开发时&#xff0c;运用的是Maven管理工具对项目进行管理&#xff0c;在项目构建的过程中自动生成了很多不需要SVN进行管理的文件&#xff0c;SVN在对源码进行版本管理时&#xff0c;需要将其忽略&#xff0c;本文给出了具体解决方案。 SVN设置忽略Maven项目中自动生成…

AVL树的插入和删除分析(图解和代码)

文章目录 1. AVL树1.1 AVL树的概念1.2 AVL树节点的定义1.3AVL树的插入1.4 AVL树的删除查找要删除的节点判断要删除节点的类型从下往上调节平衡因子真正删除节点整体代码 1.5 AVL树的性能分析 1. AVL树 1.1 AVL树的概念 二叉搜索树虽然能够缩短查找的效率,但是如果数据有序或者…

MySQL-基础汇总

MySQL-基础汇总 数据库对于任何一个从事后台开发的人说都是永远躲不掉的&#xff0c;任何系统或程序离开了数据的支持都变的毫无意义。而管理数据的工具——数据库就显得尤为重要。本章节我们的核心就是 MySQL&#xff0c;相信很多小伙伴跟我一样&#xff0c;也沉浸在增、删、…

一条sql语句是怎么执行的?

一、问题 InnoDB存储引擎&#xff0c;执行了下列语句&#xff1a; UPDATE user SET name "小明" WHERE id1002; 其中id是主键&#xff0c;这条SQL语句的执行过程是怎样的&#xff1f; 二、答案 首先客户端与MySQL连接器进行连接&#xff0c;然后分析器经过词法…

MySQL数据库迁移到DM8数据库

1. 达梦新建zsaqks库 2. 打开DM数据迁移工具 3. 新建工程 4. 迁移 - 右击 - 新建迁移 下一步 5. 选择迁移方式 6. MySQL数据源 请输入MySQL数据库信息 7. DM数据库目的 请输入达梦数据库信息 8. 迁移选项 保持对象名大小写(勾选) 9. 指定模式 指定是从数据源复制对象。 10.…

Qt 练习做一个登录界面

练习做一个登录界面 效果 UI图 UI代码 <?xml version"1.0" encoding"UTF-8"?> <ui version"4.0"><class>Dialog</class><widget class"QDialog" name"Dialog"><property name"ge…

minikube 的 Kubernetes 入门教程--(五)

本文记录 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。 这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。 在深入这些步骤之前&#xff0c;先分享来自kubernetes.io教程。 链接>>使用持久卷部署 WordPress 和 MySQL | Kubernetes 获…