c语言:摆脱对指针的恐惧【4】

  在上一期指针我们讲到了二级指针是的作用是存放一级指针的地址,还讲了指针数组是一个可以存放若干个指针变量的数组,这里我们再复习一下,下面指针数组是什么意思?

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

上面三行代码都分别定义了一个数组,第一行代码的arr1数组存放了10个int类型的指针变量 ,第二行代码的arr2数组存放了4个字符类型的指针变量,第三行代码的arr3数组存放了三个二级字符类型的变量,复习完上一期学过的内容,我们马上开始这一期内容的学习吧。

数组指针

数组指针是指针还是数组呢,答案是,它是一个指针,一个指向数组的指针。

我们已经熟悉:  

整形指针: int * pint; 能够指向整形数据的指针。

浮点型指针: float * pf ; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?

解释:

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

&数组名vs数组名 

假设我们定义一个数组:

int arr[10];

我们知道,arr表示这个数组的首元素地址,通常用于函数调用时的传址调用和把它赋给指针变量,以便指针可以访问该数组,那&arr是什么意思呢,也是首元素的地址吗?我们来看一段代码:

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

运行结果如下:

 

从次代码的运行结果来看它们似乎是一样的,难道它们真的一样吗?再看一段代码:

 

在不进行任何运算时,它们的结果是一样的,然而在进行加一也就是向前走一步时,它们的结果却不一样,且整整相差了 40个字节,所以我们也能发现,虽然arr和&arr虽然值时一样的,但是它们表示的一样却是不同的。

实际上: &arr 表示的是 数组的地址 ,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址 +1 ,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是 40。

数组指针的使用

那么数组指针是怎么使用的呢?

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
看代码:
#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;int j = 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;int j = 0;for(i=0; i<row; i++){for(j=0; j<col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};print_arr1(arr, 3, 5);//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收print_arr2(arr, 3, 5);return 0;
}

我们想让程序输出arr中的所有元素,常见的方法是创建一个函数,用一个数组来接收,完成打印,而在我们学到数组指针优以后我们发现数组指针也可以完成二维数组的接收和打印,所以两个函数打印的是相同的内容,让我们运行程序试试:

结果完全相同,学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

数组指针、指针参数

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

一维数组传参

#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?
{}
int main()
{int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);
}

上面的几种函数参数设计都可以吗,是的,我们可以用数组接收数组首元素地址,也可以用指针接收数组首元素地址,我们可以用指针数组来接收指针数组首元素的地址,也可以用二级指针来接收指针数组首元素的地址,因为指针数组的首元素是一个一级指针,而一级指针的地址就必须用二级指针来储存,所以以上test函数的参数设计都是可以的。

二位数组传参

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);return 0;
}

思考:当一个函数的参数部分为指针时,函数能接收什么样的参数?

比如:

void test1(int *p)
{}
//test1函数能接收什么参数?
void test2(char* p)
{}
//test2函数能接收什么参数?

答案是:test1可以接收一个int类型数组的首元素地址,也可以接收一个int类型变量的地址,同样,test2可以接收一个char类型数组的首元素地址,也可以接收一个char类型变量的地址。

二级指针传参

#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;
}

思考:当函数的参数为二级指针的时候,可以接收什么参数?

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;
}

答案是:当函数的参数为二级指针时,可以接收一个一级指针的地址,也可以接收一个指针数组的首元素地址。

函数指针

首先看一段代码:
#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

输出的结果:

当我们把一个函数名test和&test分别打印时。

输出的是两个地址,这两个地址是 test 函数的地址。 那我们的函数的地址要想保存起来,怎么保存? 下面我们
看代码:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能存储地址,就要求pfun1或pfun2是指针,那哪个是指针呢?答案是:

pfun1 可以存放。 pfun1 先和 * 结合,说明 pfun1 是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
未完待续。。。。。。

 

 

 

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

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

相关文章

Python中动态调用C#的dll动态链接库中方法

在Python中调用C#的dll库_哔哩哔哩_bilibili 环境准备&#xff1a; 安装 pythonnet pip install pythonnet在Python中调用C#动态链接库&#xff08;DLL&#xff09;&#xff0c;可以使用pythonnet库&#xff0c;它允许直接使用 .NET 的程序集。以下是一个示例&#xff0c;…

C++ 写的_string类,兼容std::string, MFC CString和 C# 的string

代码例子&#xff1a; using namespace lf; int main() { CString s1 _t("http://www.csdn.net"); _string s2 s1; CString s3 s2; _pcn(s1); _pcn(s2); _pcn(s3); return 0; } 输出&#xff1a; _Str.h /***************************************…

在使用LabVIEW控制多个串口设备进行数据读取时,读取时间过长

在使用LabVIEW控制多个串口设备进行数据读取时&#xff0c;如果发现数据更新时间超过5秒&#xff0c;可以从以下几个方面进行分析和解决&#xff1a; 1. 串口配置与通信参数 确保每个串口的通信参数&#xff08;波特率、数据位、停止位、校验位等&#xff09;配置正确&#x…

【数据结构】二叉树的功能实现

文章目录 关于二叉树的创建如何创建二叉树实现二叉树的前、中、后序遍历层序遍历 关于二叉树的创建 在笔者的上一篇文章中堆进行了一个详细介绍&#xff0c;而二叉树是以堆为基础进行创建&#xff0c;它与堆的显著不同是 堆像是一个线性结构&#xff0c;堆的结构往往是一个数…

springboot项目,@Test写法 @Before @After

某文件示例 package cn.xxx.crm.boss;import cn.xxxx.crm.manager.mq.rabbit.AliyunCredentialsProvider; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; im…

2024电工杯数学建模B题Python代码+结果表数据教学

2024电工杯B题保姆级分析完整思路代码数据教学 B题题目&#xff1a;大学生平衡膳食食谱的优化设计及评价 以下仅展示部分&#xff0c;完整版看文末的文章 import pandas as pd df1 pd.read_excel(附件1&#xff1a;1名男大学生的一日食谱.xlsx) df1# 获取所有工作表名称 e…

XSS漏洞:pikachu靶场中的XSS通关

目录 1、反射型XSS&#xff08;get&#xff09; 2、反射性XSS&#xff08;POST&#xff09; 3、存储型XSS 4、DOM型XSS 5、DOM型XSS-X 6、XSS之盲打 7、XSS之过滤 8、XSS之htmlspecialchars 9、XSS之href输出 10、XSS之js输出 最近在学习XSS漏洞&#xff0c;这里使用…

与WAF的“相爱相杀”的RASP

用什么来保护Web应用的安全&#xff1f; 猜想大部分安全从业者都会回答&#xff1a;“WAF&#xff08;Web Application Firewall,应用程序防火墙&#xff09;。”不过RASP&#xff08;Runtime Application Self-Protection&#xff0c;应用运行时自我保护&#xff09;横空出世…

LeetCode198:打家劫舍

题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房屋存…

【LeetCode】【5】最长回文子串

文章目录 [toc]题目描述样例输入输出与解释样例1样例2 提示Python实现动态规划 个人主页&#xff1a;丷从心 系列专栏&#xff1a;LeetCode 刷题指南&#xff1a;LeetCode刷题指南 题目描述 给一个字符串s&#xff0c;找到s中最长的回文子串 样例输入输出与解释 样例1 输入…

打造专业级网页排版:全方位解析专业字体家族font-family实践与全球知名字体库导览

CSS中的字体家族&#xff08;font-family&#xff09;属性用于指定文本所使用的字体系列。它允许开发者选择一种或多种字体作为备选&#xff0c;确保在浏览器中以最佳可用字体显示文本。本文将深度解析专业级网页排版中字体家族&#xff08;font-family&#xff09;设置的实践技…

嵌入式实时操作系统笔记2:UCOS基础知识_UC/OS-III移植(STM32F4)_编写简单的UC/OS-III任务例程(失败.....)

今日学习嵌入式实时操作系统RTOS&#xff1a;UC/OS-III实时操作系统 本文只是个人学习笔记备忘用&#xff0c;附图、描述等 部分都是对网上资料的整合...... 文章主要研究如何将UC/OS-III 移植到 STM32 F407VET6上&#xff0c;提供测试工程下载 &#xff08;2024.5.21 文章未…

明天(周六)下午!武汉Linux爱好者线下沙龙,我们在华中科技大学等你!

2024 年 5月 25 日&#xff08;周六&#xff09;下午&#xff0c;我们将在「武汉市洪山区」 珞喻路 1037 号华中科技大学南五楼 613 室举办武汉 Linux 爱好者线下沙龙&#xff08;WHLUG&#xff09;&#xff0c;欢迎广大 Linux 爱好者来到现场&#xff0c;与我们一同交流技术&a…

【Spring】SSM介绍_SSM整合

1、SSM介绍 1.1简介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一种流行的Java Web应用程序框架组合&#xff0c;它将Spring框架的核心特性、SpringMVC作为Web层框架和MyBatis作为数据访问层框架结合在一起。这种整合方式提供了从数据访问到业务逻辑处理再…

构建智能化的语言培训教育技术架构:挑战与机遇

随着全球化的发展和人们对语言学习需求的增长&#xff0c;语言培训教育行业正面临着越来越多的挑战和机遇。在这个背景下&#xff0c;构建智能化的语言培训教育技术架构成为提升服务质量和效率的重要手段。本文将探讨语言培训教育行业的技术架构设计与实践。 一、智能化教学平台…

接口响应断言

目录 接口断言介绍接口断言方式介绍响应状态码断言 课程目标 掌握什么是接口断言。了解接口断言的多种方式。掌握如何对响应状态码完成断言。 思考 这两段代码是完整的接口自动化测试代码吗&#xff1f; …省略… when().get(“https://httpbin.ceshiren.com/get?namead&…

Golang | Leetcode Golang题解之第109题有序链表转换二叉搜索树

题目&#xff1a; 题解&#xff1a; var globalHead *ListNodefunc sortedListToBST(head *ListNode) *TreeNode {globalHead headlength : getLength(head)return buildTree(0, length - 1) }func getLength(head *ListNode) int {ret : 0for ; head ! nil; head head.Next…

AI视频智能分析技术赋能营业厅:智慧化管理与效率新突破

一、方案背景 随着信息技术的快速发展&#xff0c;图像和视频分析技术已广泛应用于各行各业&#xff0c;特别是在营业厅场景中&#xff0c;该技术能够有效提升服务质量、优化客户体验&#xff0c;并提高安全保障水平。TSINGSEE青犀智慧营业厅视频管理方案旨在探讨视频监控和视…

爬虫基础1

一、爬虫的基本概念 1.什么是爬虫&#xff1f; 请求网站并提取数据的自动化程序 2.爬虫的分类 2.1 通用爬虫&#xff08;大而全&#xff09; 功能强大&#xff0c;采集面广&#xff0c;通常用于搜索引擎&#xff1a;百度&#xff0c;360&#xff0c;谷歌 2.2 聚焦爬虫&#x…

Linux 如何用上次的checkpoint文件dist_train.sh 接着训练【mmdetection】

在Linux环境下&#xff0c;如果你想要用上一次的checkpoint文件继续训练&#xff0c;你可以在你的dist_train.sh脚本中设置--resume_from参数。这个参数指定了checkpoint文件的路径&#xff0c;训练会从该文件的状态继续进行。 例如&#xff0c;如果你的checkpoint文件名为las…