【C语言期末不挂科——指针进阶篇】【上】

C语言进阶篇【上】

文章目录

  • C语言进阶篇【上】
      • 字符指针
      • 数组指针
      • 数组传参和指针传参
          •   数组传参
          •   一级指针传参
          •   二级指针传参


前言:

  我们在指针初阶篇学习了:

1、指针就是个变量,用来存放地址,地址唯一标识一块空间。
2、指针的大小是固定的4/8个字节(32位平台/64位平台)
3、指针是有类型,指针的类型决定指针±整数的步长,指针解引用操作时候的权限。
4、指针的运算。

  快要期末了,祝各位小伙伴们期末考试顺利,那么话不多说,进入我们今天的主题!


字符指针

  指针的类型里面我们知道有一种指针 类型为 字符指针(char *),经过初阶的学习我们已经能用:

#include<stdio.h>int main()
{char ch = 'a';char *p = &ch;*p = 'b';return 0;
}

  一般我们在存储字符串的时候,我们都会把字符串放进字符数组里面,其实还有一种方式可以用来存储字符串:

#include<stdio.h>int main()
{char ch[10] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0' };const char *p = "abcdef";//常量字符串printf("%s",p);return 0;
}

  我们可以用字符指针来接收字符串,有人可能要问了:“指针接收?那是把字符串存储到指针里吗?”,实则不然,想一下,如果是在x86的环境下,指针只有4个字节大小,而这个字符串已经超出4字节的范围。

  还记得我们在C语言中是如何打印字符串的吗?

printf("%s\n",ch);//上面的例子

  我们当时不知道为什么这样写,现在看来,我们是将数组首元素地址传入到printf函数里,然后printf函数通过寻址来访问字符数组从而打印字符串。

  其实我们指针也是如此,指针并不是存储了字符串,而是存储了字符串首元素的地址,这样就能通过寻址打印了:

在这里插入图片描述

  注意:被双引号引用的字符串已经变成了常量,所以要加const,在C语言内存布局里面有个叫做代码区的区域,是专门用来存储常量以及代码的地方。

我们来看下面这道题:(请问输出结果是什么?)

#include <stdio.h>int main()
{char str1[] = "hello world";char str2[] = "hello world";const char *str3 = "hello world";const char *str4 = "hello world";if(str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

在这里插入图片描述

  怎么样,你相对了没有?下面我们来仔细分析是如何将打印的。

  首先我们来看str1[]与str2[],我们知道,数组名代表首元素地址,而这是两个数组,str1与str2那么他们两个的地址一定是不同的,所以第一次if判断的时候他们是不相等的。

在这里插入图片描述

  而str3与str4为什么又是相同的呢?首先,与前两个不同,str3与str4并没有单独开一个空间存储字符串,实际上,常量字符串存储在代码区里时,如果你的常量是出现过的,编译器不会在生成另一份相同的常量,而是直接取同一块常量的首地址给你的指针,所以str3与str4是相同的。

在这里插入图片描述


数组指针

  在学习数组指针之前,我们先复习一下上节课所讲的指针数组:

  如果想不明白也可以类比,整形数组——是存放整形的数组字符数组——是存放字符的数组,那么指针数组——是存放指针的数组

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

  当然也可以用指针数组来模拟二维数组:

#include <stdio.h>int main()
{int arr1[] = { 1, 2, 3, 4, 5 };int arr2[] = { 2, 3, 4, 5, 6 };int arr3[] = { 3, 4, 5, 6, 7 };int *arr[3] = { arr1, arr2, arr3 };//指针数组int i = 0;for(i = 0 ; i < 3 ; i++)//打印每一列{int j = 0;for(j = 0 ; j < 5 ; j++)//打印每一行{printf("%d ",arr[i][j]);	}	printf("\n");}return 0;
}

在这里插入图片描述


  话不多说,进入正题,数组指针是什么?

  同样的,我们可以先来类比:

整形指针——指向整型变量的指针,存放整形变量地址的指针变量
字符指针——指向字符变量的指针,存放字符变量地址的指针变量
数组指针——指向数组的指针,存放的是数组的地址的指针变量

  下面哪个是数组指针?

int *p1[10];
int (*p2)[10];

  在C语言中’[]‘的优先级是要比’*'高的,所以第一个语句,p1是先与[]结合,所以是数组,而数组的类型是int *整形指针类型。

  我们再来看第二个,*p2被括号括起来了,那么他的优先级就要比[]高,所以p2就是指针类型,还记得之前说的吗?int *p,*说明了p是指针,而int说明了p指向的是整形,那么去掉(*p2)剩下的int [10],也就是数组类型,所以第二条语句是一个数组指针。

在这里插入图片描述


  我们知道了什么是数组指针,但是我们该如何给数组指针赋值呢?这个时候就需要再次研究一下我们的数组名了。

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

在这里插入图片描述

  我们前面说过,数组名表示首元素地址,但是我们有两个例外:

1、sizeof(数组名),这里的数组名不是首元素地址,数组名表示整个数组,其计算的是数组的整个大小,单位为字节。
2、&数组名 这里的数组名也表示整个数组,&所取出的是数组的整个地址。

  除此之外,所有情况的数组名均表示首元素地址。

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

在这里插入图片描述
  我们可以看到,1 2和3 4运行结果相同,都是相差四个字节,虽然5的地址和1与3的地址相同,但是6的地址却与1 3 5 相差了40个字节,而我们数组的大小刚好是40个字节。这也能说明&数组名是取整个数组的地址。

  那么我们就可以写出数组指针了:

#include<stdio.h>int main()
{int arr[10] = { 0 };int (*p)[] = &arr;//&arr是整个数组的地址return 0;
}

在这里插入图片描述

  我们也可以通过编译来看数组指针的类型:

在这里插入图片描述
  上面已经声明了数组指针,那么我们到底该怎么用数组指针呢?

#include<stdio.h>int main()
{int arr[10] = { 1, 2, 3, 4 ,5, 6, 7, 8, 9, 10 };int (*p)[10] = &arr;int len = sizeof(arr)/sizeof(int);int i = 0;for(i = 0 ; i < len ; i++){printf("%d ", *((*p) + i));//数组指针使用方式}return 0;
}

在这里插入图片描述

  数组指针使用时首先对p进行解引用找到这个数组首元素地址,然后首元素加上偏移量之后再解引用,得到具体的值。当然还可以这样写:

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

  实际上数组指针并不是以上情况下使用的,一般我们数组指针用来对二维数组传参,因为二维数组传参,形参是指针形式。例如:

#include<stdio.h>void Print(int (*p)[5], int r, int c)//这里数组指针指向的就是二维数组
{int i = 0;for(i = 0 ; i < r ; i++)//每一行{int j = 0;for(j = 0 ; j < c ; j++)//每一列{printf("%d ", *(*(p + i) + j));//*(p+i)表示找到二维数组的i行首元素地址,+j表示这一行的第j个元素的地址,最后在最最外面解引用,最后得到这个元素}printf("\n");}return;
}int main()
{int arr[3][5] = { 1,2,3,4,5 ,2,3,4,5,6 , 3,4,5,6,7 };Print(arr, 3, 5);return 0;
}

在这里插入图片描述

  虽然在main函数里我们是使用二维数组数组名来传参,但是我们知道,数组名表示首元素地址,这个时候使用数组指针就比较符合场景了。

  虽然看起来指针数组不如直接将二维数组传入来的实在,但是其实我们直接传数组的时候,编译器还是会将数组转化为上面写的那种形式。


数组传参和指针传参

  数组传参

  既然说到了数组指针问题,上面函数参数可能会让人捉摸不透,变来变去,我们来总结一下:

我们看下面一段代码:(我们可以先思考下面函数部分是不是都是对的?)

//一维数组各种传参
#include<stdio.h>void test(int arr[]);//y or n?void test(int arr[10]);//y or n?void test(int *arr);//y or n?void Test2(int *arr[20]);//y or n?void Test2(int **arr);//y or n?int main()
{int arr[10] = {0};int *arr2[20] = {0};test(arr);Test2(arr2);return 0;
}

  我们依次看每个函数:
我们先来看第一个函数:

  也就是说以上五个一维数组传参的形式全部都是正确的,怎么样,你理解了吗?那么二维数组的传参会是怎样呢?来看看下面哪些是正确的二维数组传参形式:

void test(int arr[3][5]);//y or n?void test(int arr[][]);//y or n?void test(int arr[][5]);//y or n?void test(int *arr);//y or n?void test(int *arr[5]);//y or n?void test(int (*arr)[5]);//y or n?void test(int **arr);//y or n?int main()
{int arr[3][5] = {0};test(arr);return 0;
}

在这里插入图片描述

  所以,二级指针传参,如果用数组传参只能将行省略,如果用指针传参,只能使用数组指针的形式传参。


  一级指针传参

  既然我们说到了传参,而数组又与指针密不可分,我们顺带来分析一下,指针的传参方式又是什么?

  我们来看下面代码:

#include<stdio.h>void Test(int *p)
{return;
}int main()
{int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int a;return 0;
}

  思考一下,我们有哪些方式可以传参一级指针呢?你可以把你想的全部写下来,与我写的对比,看看有什么不同:

  1、可以传整形变量的地址。
  2、可以传一级指针。
  3、可以传一维数组的首元素。

#include<stdio.h>void test(int *p)
{return;
}int main()
{int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int a = 1;int *p = &a;test(arr);test(&a);test(p); return 0;
}

在这里插入图片描述

  我们可以看到是可以通过编译的,可能还存在其他形式的一级指针传参,但是这三个是我们最常用的方式。


  二级指针传参
#include<stdio.h>void test(int **p)
{return;
}int main()
{//哪些传参方式? return 0;
}

  同样,我们思考一下有哪些方式可以给二级指针传参呢?

  1、一级指针变量的地址
  2、二级指针
  3、指针数组的数组名

#include<stdio.h>void test(int **p)
{return;
}int main()
{int n = 10;int *p = &n;int **pp = &p;int *arr[10];test(&p);//二级指针就是取一级指针的地址 test(pp);//传入二级指针 test(arr); //函数名表示首元素地址,而指针数组的每个元素都是指针,所以这个数组名就是二级指针 return 0;
}

  当然,二级以上的指针和二级指针类似,类比就行。


  这就是今天的内容了,如果对各位有帮助还望能三连支持~~

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

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

相关文章

渗透测试学习day4

文章目录 靶机&#xff1a;SequelTask1Task2Task3Task4Task5Task6Task7Task8 靶机&#xff1a;CrocodileTask1Task2Task3Task4Task5Task6Task7Task8Task9Task10 靶机&#xff1a;Sequel 考察sql注入 Task1 问题&#xff1a;在扫描过程中&#xff0c;我们发现哪个端口为 MySQL …

自动化测试 —— requests和selenium模块!

一、requests基于POST请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #1.requests的GET与POST用法的区别&#xff1a; GET请求: (HTTP默认的请求方法就是GET) * 没有请求体 * 数据必须在1K之内&#xff01; * GET请求数据会暴露在浏览器…

信号类型(通信)——高斯最小频率键控(GMSK)

系列文章目录 《信号类型&#xff08;通信&#xff09;——仿真》 《信号类型&#xff08;通信&#xff09;——QAM调制信号》 《信号类型&#xff08;通信&#xff09;——QPSK、OQPSK、IJF_OQPSK调制信号》 《信号类型&#xff08;通信&#xff09;——最小频移键控&…

在PyCharm中配置PyQt5环境

在PyCharm中配置PyQt5环境 文章目录 1.安装第三方库2.PyQt5设计器3.PyUIC转换工具 &#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1f339;꧔ꦿ&#x1…

2023年AI报告:AI提供新一轮C端创新

今天分享的是AI系列深度研究报告&#xff1a;《2023年AI报告&#xff1a;AI提供新一轮C端创新》。 &#xff08;报告出品方&#xff1a;中泰电子&#xff09; 报告共计&#xff1a;54页 手机&#xff1a;销量处于底部&#xff0c;华为引领复苏  华为手机销量强势回归&…

Ruoyi-Vue或者Ruoyi-Cloud登录进去之后的第一个页面如何修改(即如何去掉首页或者如何修改首页)

其实大家如果看过最近的码云上的issues 就能知道这个问题的答案了。 我这里给出一下链接&#xff1a;https://gitee.com/y_project/RuoYi-Vue/issues/I60JIY 开始 第一步&#xff0c;把router/index.js里面关于首页的路由给注释掉或者删除掉&#xff0c;如图&#xff1a; 第…

分享:身份证阅读器在ARM Linux系统调用libwlt2bmp.so解码库实现身份证头像解码

头像解码库&#xff1a;libwlt2bmp.so 照片文件名&#xff1a;photo.bmp 原始身份证相片数据&#xff1a;574C66007E00320000F........&#xff08;此处省略&#xff09; 调用身份证阅读器Linux开发包&#xff0c;然后调用libwlt2bmp.so解码库文件&#xff0c;传入身份证原始…

如何熟练使用vim工具?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

软件系统安全漏洞检测应该怎么做?靠谱的软件安全检测公司推荐

软件系统安全漏洞检测是指通过对软件系统进行全面的、系统化的评估&#xff0c;发现和解决其中可能存在的安全漏洞和隐患。这些安全漏洞可能会被不法分子利用&#xff0c;引发数据泄露、系统瘫痪、信息被篡改等安全问题&#xff0c;给企业造成严重的经济和声誉损失。那么软件系…

zabbix分布式监控平台从IPV4切换到IPV6之监控主机切换

现在有一套监控了海量服务器的zabbix分布式监控平台需整体在线从IPV4切换到IPV6&#xff0c;不能影响其原有的定制监控及视图。本文讲解了切换的第一步--监控主机切换。 一、zabbix分布式监控平台平台架构 本套zabbix分布式监控平台是一个多代理服务器分布式部署的典型传统架构…

Node——Node.js简介

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;它能够让JavaScript脚本运行在服务端&#xff0c;这使得JavaScript成为与PHP、Python等服务端语言平起平坐的脚本语言。 1、认识Node.js Node.js是当今网站开发中非常流行的一种技术&#xff0c;它以简单易…

开放远程访问MySQL的权限

访问远程数据库时&#xff0c;产生Access denied for user ‘root‘‘xxx.xxx.xxx.xxx‘ (using password: YES)异常的解决办法 一. 异常现象 我编写了一个SpringBoot项目&#xff0c;项目中连接的数据库服务器地址是192.168.87.107&#xff0c;然后打包生成了对应的jar包&am…

Microsoft Remote Desktop高效、安全、稳定的远程办公解决方案

在今天的数字化时代&#xff0c;Remote Desktop远程办公已成为许多人的日常生活。无论你是因为工作需要&#xff0c;还是因为在家中需要访问公司服务器&#xff0c;微软远程连接软件都是一个理想的选择。 微软远程连接软件Remote Desktop是一款高效、安全、稳定的远程办公解决…

苹果手机照片恢复,这3个方法收藏好了吗?

如今&#xff0c;我们越来越喜欢用手机拍照来记录生活的点点滴滴。对于很多人来说&#xff0c;手机中的照片是他们珍贵的记忆和情感。如果这些照片丢失了&#xff0c;会给他们带来很大的困扰。那么&#xff0c;如何恢复苹果手机照片呢&#xff1f;本文将为您介绍有关苹果手机照…

内网穿透的应用-如何部署Tale博客并结合cpolar内网穿透发布个人站点到公网访问

Linux系统部署Tale个人博客并发布到公网访问 文章目录 Linux系统部署Tale个人博客并发布到公网访问前言1. Tale网站搭建1.1 检查本地环境1.2 部署Tale个人博客系统1.3 启动Tale服务1.4 访问博客地址 2. Linux安装Cpolar内网穿透3. 创建Tale博客公网地址4. 使用公网地址访问Tale…

阿里云效一键部署前后端

静态站点到OSS 阿里云-云效&#xff0c;阿里云企业级一站式 DevOps&#xff0c;可以免费使用&#xff08;会限制人数、流水线数量等&#xff0c;个人项目够用了&#xff09;。相关文章 CI 持续集成 - 阿里云云效 OSS 是对象存储的意思&#xff0c;一般一个项目对应一个 Bucke…

深度学习手势检测与识别算法 - opencv python 计算机竞赛

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…

STM32 外部中断配置与中断函数设计

单片机学习 目录 文章目录 一、外部中断配置步骤 1.1配置RCC 1.2配置GPIO 1.3配置AFIO 1.4配置EXTI 1.5配置NVIC 二、中断函数设计 总结 一、外部中断配置步骤 第一步&#xff1a;配置RCC&#xff0c;把涉及外设的时钟打开。第二步&#xff1a;配置GPIO&#xff0c;选择…

样品实验K-KAT348羧酸铋催化剂TDS说明书

样品实验K-KAT348羧酸铋催化剂TDS说明书 50克 100克 200克

STM32_11(SPI)

一、SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司开发的一种通用数据总线四根通信线&#xff1a;SCK&#xff08;Serial Clock&#xff09;、MOSI&#xff08;Master Output Slave Input&#xff09;、MISO&#xff08;Master Input Slav…