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

news2024/12/28 19:44:01

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");
 	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");
 
 	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);//1
	printf("%p\n", arr + 1);//2
	
	printf("%p\n", &arr[0]);//3
	printf("%p\n", &arr[0] + 1);//4
	
	printf("%p\n", &arr);//5
	printf("%p\n", &arr + 1);//6
	return 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.coloradmin.cn/o/1271507.html

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

相关文章

Android自动化测试中使用ADB进行网络状态管理!

技术分享&#xff1a;使用ADB进行Android网络状态管理 Android自动化测试中的网络状态切换是提高测试覆盖率、捕获潜在问题的关键步骤之一&#xff0c;本文将介绍 如何使用ADB检测和管理Android设备的网络状态。 自动化测试中的网络状态切换变得尤为重要。 网络状态查询 adb s…

esp32-s3部署yolox_nano进行目标检测

ESP32-S3部署yolox_nano进行目标检测 一、生成模型部署项目01 环境02 配置TVM包03 模型量化3.1预处理3.2 量化 04 生成项目 二、烧录程序 手上的是ESP32-S3-WROOM-1 N8R8芯片&#xff0c;整个链路跑通了&#xff0c;但是识别速度太慢了&#xff0c;20秒一张图&#xff0c;所以暂…

C语言线性表的链式存储(详解)

线性表的链式存储 线性表的顺序存储&#xff1a;用一块连续的内存空间 线性表的链式存储&#xff1a;不连续的内存空间 链表是由一系列的节点组成&#xff0c;每个节点包含两个域&#xff0c;一个是数据域&#xff0c;一个是指针域 链表的插入和删除原理 单项链表框架的搭建 …

深度学习毕设项目 医学大数据分析 - 心血管疾病分析

# 1 前言 &#x1f6a9; 基于大数据的心血管疾病分析 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 1 课题背景 本项目的任务是利用患者的检查结果预测心血管疾病(CVD)的存在与否。 2 数据…

老师怎么分配学生座位

学生座位分配是教育过程中的一个重要环节&#xff0c;对于学生的学习效果、课堂氛围以及师生互动都有一定的影响。那么&#xff0c;老师应该如何分配学生的座位呢&#xff1f; 了解每个学生的个性特点和学习习惯。不同的学生有不同的性格和特点&#xff0c;老师需要充分了解每个…

MAMP Pro v6.8.1(PHP/MySQL开发环境)

MAMP Pro是一款专为Mac用户设计的全功能本地服务器软件&#xff0c;可以将电脑变成一个完整的Web开发环境。无论个人开发者、网站管理员还是团队协作&#xff0c;MAMP Pro都提供了强大的工具和便捷的管理方式&#xff0c;能够更加高效地构建和测试网站。 MAMP Pro的基本功能包括…

Appium+Pytest实现app并发测试

前言 这个功能已经写完很长时间了&#xff0c;一直没有发出来&#xff0c;今天先把代码发出来吧&#xff0c;有一些代码是参考网上写的&#xff0c;具体的代码说明今天暂时先不发了&#xff0c;代码解释的太详细还得我花点时间^_^, 毕竟想让每个人都能看明白也不容易&#xff…

uniapp 使用web-view外接三方

来源 前阵子有个需求是需要在原有的项目上加入一个电子签名的功能&#xff0c;为了兼容性和复用性后面解决方法是将这个电子签名写在一个新的项目中&#xff0c;然后原有的项目使用web-view接入这个电子签名项目&#xff1b; 最近又有一个需求&#xff0c;是需要接入第三方的…

LeetCode(43)快乐数【哈希表】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 快乐数 1.题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也…

干销售找不到客户怎么办?

干销售找不到客户怎么办&#xff1f; 销售找不到客户&#xff1f;别担心&#xff0c;我们有解决方案&#xff01; 当你面临销售找不到客户的困境时&#xff0c;不要轻易放弃。相反&#xff0c;你可以采取一些策略来帮助你找到潜在的客户。以下是一些建议&#xff0c;它们将有…

什么是API? (应用程序编程接口)

我们经常听到 API 这个专业名称。那么什么是 API 呢&#xff1f; 定义 API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的函数&#xff0c;或指软件系统不同组成部分衔接的约定。目的是提供应用程序与开发人员基于某软…

47、Flink 的指标报告介绍(graphite、influxdb、prometheus、statsd和datalog)及示例(jmx和slf4j示例)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

C/C++ Zlib库封装MyZip压缩类

Zlib是一个开源的数据压缩库&#xff0c;提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly和Mark Adler开发&#xff0c;旨在成为一个高效、轻量级的压缩库&#xff0c;其被广泛应用于许多领域&#xff0c;包括网络通信、文件压缩、数据库系统等。其压缩算法是…

《如何戒掉坏习惯》精华摘抄

“劣质货币会驱逐优良货币。”这就是格雷沙姆法则&#xff08;劣币驱逐良币 法则&#xff09;。它是指一旦对劣质货币&#xff08;假币&#xff09;的流通放任不管&#xff0c;人们就不会 去使用优良货币&#xff08;真正的货币&#xff09;&#xff0c;从而导致优良货币从市场…

深入探索Maven:优雅构建Java项目的新方式(二)

Meven高级 1&#xff0c;属性1.1 属性1.1.1 问题分析1.1.2 解决步骤步骤1:父工程中定义属性步骤2:修改依赖的version 1.2 配置文件加载属性步骤1:父工程定义属性步骤2:jdbc.properties文件中引用属性步骤3:设置maven过滤文件范围步骤4:测试是否生效 1.3 版本管理 2&#xff0c;…

todesk连接ubuntu显示当前系统并无桌面环境,或无显示器,无法显示远程桌面,您需要自行安装X11桌面环境,或者使用终端文件功能

ToDesk远程遇到的问题如上图&#xff0c;换向日葵直接黑屏&#xff1b; 问题原因 截止发文时间&#xff0c;Todesk只支持X11协议&#xff0c;没有适配最新的Wayland协议&#xff0c;所以我们需要把窗口系统调整为X11才可以。 解决方法 修改配置文件&#xff0c;关闭wayland su…

Hadoop入门学习笔记

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 这里写目录标题 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.1.1. 修改虚拟机子网IP和网关1.1.2. 安装…

AI PC专题:AI PC深入变革PC产业

今天分享的是AI系列深度研究报告&#xff1a;《AI PC专题&#xff1a;AI PC深入变革PC产业》。 &#xff08;报告出品方&#xff1a;西南证券研究发展中心&#xff09; 报告共计&#xff1a;30页 AI PC将深入变革PC产业  从出货量看&#xff0c;PC整体呈现周期性的特征。2…

智慧灯杆网关:引领城市智慧照明的未来

智慧灯杆网关&#xff0c;作为城市智慧照明系统的核心组件&#xff0c;正逐渐成为各大城市发展的关键所在。它的出现使得城市照明管理更加智能、高效&#xff0c;为未来城市的可持续发展奠定了坚实的基础。 智慧灯杆网关是一种集网络通信、数据处理、远程控制等功能于一体的设备…

系统部署安装-Centos7-Cassandra

文章目录 介绍安装在线下载安装启动普通启动注册服务 介绍 Apache Cassandra是一个高度可扩展的高性能分布式数据库&#xff0c;旨在处理许多商用服务器上的大量数据&#xff0c;提供高可用性而没有单点故障。 安装 在线下载 &#xff08;1&#xff09;使用weget下载最新的…