拿捏指针(二)---对指针的进阶认识(中级)

news2024/12/24 8:37:37

文章目录

  • 字符指针
  • 指针数组
  • 数组指针
    • 数组指针的定义
    • &数组名与数组名的区别
    • 数组指针的使用
  • 数组参数、指针参数
    • 一维数组传参
    • 二维数组传参
    • 一级指针传参
    • 二级指针传参

字符指针

我们知道,在指针的类型中有一种指针类型叫字符指针char * 。
字符指针的一般使用方法为:

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

代码中,将字符变量ch的地址存放在了字符指针p中。

其实,字符指针还有另一种使用方式:

#include<stdio.h>
int main()
{
	char* p = "hello csdn.";
	printf("%c\n", *p);//打印字符'h'
	printf("%s\n", p);//打印字符串"hello csdn."
	return 0;
}

代码中,字符指针p中存放的并非字符串"hello csdn.",字符指针p中存放的是字符串"hello csdn.“的首元素地址,即字符’h’的地址。
所以,当对字符指针p进行解引用操作并以字符的形式打印时只能打印字符’h’。我们知道,打印一个字符串只需要提供字符串的首元素地址即可,既然字符指针p中存放的是字符串的首元素地址,那么我们只要提供p(字符串首地址)并以字符串的形式打印,便可以打印字符串"hello csdn.”。
注意:代码中的字符串"hello csdn."是一个常量字符串。

这里有一道题目,可以帮助我们更好的理解字符指针和常量字符串:

#include <stdio.h>
int main()
{
	char str1[] = "hello csdn.";
	char str2[] = "hello csdn.";
	char *str3 = "hello csdn.";
	char *str4 = "hello csdn.";
	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 and str2 are not same
str3 and str4 are same

解释:

题目中str1和str2是两个字符数组,比较str1和str2时,相当于比较数组str1和数组str2的首元素地址,而str1与str2是两个不同的字符数组,创建数组str1和数组str2是会开辟两块不同的空间,它们的首元素地址当然不同。

在这里插入图片描述
而str3和str4是两个字符指针,它们指向的都是常量字符串"hello csdn."的首元素地址,所以str3和str4指向的是同一个地方。

在这里插入图片描述

注意:常量字符串与普通字符串最大的区别是,常量字符串是不可被修改的字符串,既然不能被修改,那么在内存中没有必要存放两个一模一样的字符串,所以在内存中相同的常量字符串只有一个。

指针数组

我们已经知道了整型数组、字符数组等。整型数组是用于存放整型的数组,字符数组是用于存放字符的数组。

int arr1[4];
char arr2[5];

数组arr1包含4个元素,每个元素的类型是整型;数组arr2包含5个元素,每个元素的类型是字符型。

在这里插入图片描述

指针数组也是数组,是用于存放指针的数组。

int* arr3[5];

数组arr3包含5个元素,每个元素是一个一级整型指针。

在这里插入图片描述

以此类推:

char* arr4[10];//数组arr4包含10个元素,每个元素是一个一级字符型指针。
char** arr5[5];//数组arr5包含5个元素,每个元素是一个二级字符型指针。

数组arr4包含10个元素,每个元素是一个一级字符型指针;数组arr5包含5个元素,每个元素是一个二级字符型指针。

数组指针

数组指针的定义

我们已经知道了,整型指针是指向整型的指针,字符指针是指向字符的指针,那么数组指针应该就是指向数组的指针了。
整型指针和字符指针,在使用时只需取出某整型/字符型的数据的地址,并将地址存入整型/字符型指针即可。

#include<stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;//取出a的地址存入整型指针中
	char ch = 'w';
	char* pc = &ch;//取出ch的地址存入字符型指针中
	return 0;
}

数组指针也是一样,我们只需取出数组的地址,并将其存入数组指针即可。

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

那么数组指针的指针类型是如何写出来的呢?

首先我们应该知道的是:

  1. [ ]的优先级要高于 * 。
  2. 一个变量除去了变量名,便是它的变量类型。

比如:

int a = 10;//除去变量名a,变量类型为int
char ch = 'w';//除去变量名ch,变量类型为char
int* p = NULL;//除去变量名p,变量类型为int*

数组也可以这样理解:

int arr[10] = { 0 };//除去变量名arr,变量类型为int [10]
int arr[3][4] = { 0 };//除去变量名arr,变量类型为int [3][4]
int* arr[10] = { 0 };//除去变量名arr,变量类型为int* [10]

数组的变量类型说明了数组的元素个数和每个元素的类型:

在这里插入图片描述

3.一个指针变量除去了变量名和 * ,便是指针指向的内容的类型。

比如:

int a = 10;
int* p = &a;//除去变量名(p)和*,便是P指向的内容(a)的类型->int
char ch = 'w';
char* pc = &ch;//除去变量名(pc)和*,便是pc指向的内容(ch)的类型->char

接下来我们就可以来写数组指针的指针类型了:

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

首先p是一个指针,所以p必须要先与 * 结合,而[ ]的优先级要高于 * ,所以我们要加上( )以便让p与 * 先结合。
指针p指向的内容,即数组类型是int [10],所以数组指针p就变成了int(*p)[10]。
去掉变量名p后,便是该数组指针的变量类型int( * )[10]。

在这里插入图片描述

如果我们不加( ),那么就变成了int *p[10],因为[ ]的优先级要高于 * ,所以p先与[ ]结合,这时p就变成了一个数组,而数组中每个元素的的类型是int * ,这就是前面说到的指针数组。

在这里插入图片描述

&数组名与数组名的区别

对于一个数组的数组名,它什么时候代表数组首元素的地址,什么时候又代表整个数组的地址,这一直是很多人的疑惑。在这里我给出大家准确的答案:

数组名代表整个数组的地址的情况其实只有两种:

1.&数组名。
2.数组名单独放在sizeof内部,即sizeof(数组名)。

除此之外,所有的数组名都是数组首元素地址。

比如:

int arr[5] = { 1, 2, 3, 4, 5 };

对于该数组arr,只有以下两种情况数组名代表整个数组的地址:

	&arr;
	sizeof(arr);//arr单独放在sizeof内部

除此之外,所以的arr都代表数组首元素地址,即1的地址。

将其与指针联系起来:

#include<stdio.h>
int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };
	int* p1 = arr;//数组首元素的地址
	int(*p2)[5] = &arr;//数组的地址
	printf("%p\n", p1);
	printf("%p\n", p2);

	printf("%p\n", p1+1);
	printf("%p\n", p2+1);
	return 0;
}

因为代码中的arr是数组首元素地址,所以要用int * 的指针接收。而&arr是整个数组的地址,所以要用数组指针进行接收。
虽然一个是数组首元素地址,一个是数组的地址,但是它们存放的都是数组的起始位置的地址,所以将p1和p2以地址的形式打印出来发现它们的值一样。
数组首元素地址和数组的地址的区别在于,数组首元素地址+1只能跳过一个元素指向下一个元素,而数组的地址+1能跳过整个数组指向数组后面的内存空间。

在这里插入图片描述

数组指针的使用

数组指针有一个简单的使用案例,那就是打印二维数组:

#include<stdio.h>
void print(int(*p)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//行数
	{
		int j = 0;
		for (j = 0; j < col; j++)//列数
		{
			printf("%d ", *(*(p + 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 } };
	print(arr, 3, 5);//传入二维数组名,即二维数组首元素地址,即二维数组第一行的地址
	return 0;
}

在这里我们打印一个三行五列的二维数组。传参时我们传入二维数组的数组名,明确打印的起始位置;传入行数和列数,明确打印的数据范围。
通过上面对&数组名和数组名的认识,我们知道了这里传入的数组名代表的是二维数组的首元素地址,而二维数组的首元素第一行的元素,即传入的是一维数组的地址,所以我们必须用数组指针进行接收。
打印时,通过表达式 * (*(p+i)+j ) 锁定打印目标:

在这里插入图片描述

数组参数、指针参数

一维数组传参

#include<stdio.h>
void test1(int arr[10])//数组接收
{}
void test1(int *arr)//指针接收
{}
void test2(int *arr[20])//数组接收
{}
void test2(int **arr)//指针接收
{}
int main()
{
	int arr1[10] = { 0 };//整型数组
	int *arr2[20] = { 0 };//整型指针数组
	test1(arr1);
	test2(arr2);
}

整型数组:
当向函数传入整型数组的数组名时,我们有以下几种参数可供接收:

1.数组传参数组接收,我们传入的是整型数组,那我们就用整型数组接收。
2.传入的数组名本质上是数组首元素地址,所以我们可以用指针接收。数组的元素类型是整型,我们接收整型元素的地址用int * 的指针即可。
整型指针数组:
当向函数传入整型指针数组的数组名时,我们有以下几种参数可供接收:

1.数组传参数组接收,我们传入的是整型指针数组,那我们就用整型指针数组接收。
2.指针接收,数组的元素是int * 类型的,我们接收int * 类型元素的地址用二级指针int ** 即可。

注意:一维数组传参,函数形参设计时[ ]内的数字可省略。

二维数组传参

#include<stdio.h>
void test(int arr[][5])//数组接收
{}
void test(int(*arr)[5])//指针接收
{}
int main()
{
	int arr[3][5] = { 0 };//二维数组
	test(arr);
}

当向函数传入二维数组的数组名时,我们有以下几种参数可供接收:

1.二维数组传参二维数组接收。
2.指针接收,二维数组的首元素是二维数组第一行的地址,即一维数组的地址,我们用数组指针接收即可。

注意:二维数组传参,函数形参的设计只能省略第一个[ ]内的数字。

一级指针传参

#include<stdio.h>
void print(int* p, int sz)//一级指针接收
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;//一级指针
	print(p, sz);
	return 0;
}

当我们传入的参数为一级指针时,我们可以用一级指针的形参对其进行接收,那么当函数形参为一级指针的时候,我们可以传入什么样的参数呢?

#include<stdio.h>
void test(int* p)
{}
int main()
{
	int a = 10;
	test(&a);//可以传入变量的地址
	int* p = &a;
	test(p);//可以传入一级指针
	int arr[10] = { 0 };
	test(arr);//可以传入一维数组名
	//...
	return 0;
}

总而言之,只要传入的表达式最终的类型是一级指针类型即可传入。

二级指针传参

#include<stdio.h>
void test(int** p)//二级指针接收
{}
int main()
{
	int a = 10;
	int* pa = &a;
	int** paa = &pa;
	test(paa);//二级指针
	return 0;
}

当我们传入的参数为二级指针时,我们可以用二级指针的形参对其进行接收,那么当函数形参为二级指针的时候,我们可以传入什么样的参数呢?

#include<stdio.h>
void test(int** p)
{}
int main()
{
	int a = 10;
	int* pa = &a;
	test(&pa);//可以传入一级指针的地址
	int** paa = &pa;
	test(paa);//可以传入二级指针
	int* arr[10];
	test(arr);//可以传入一级指针数组的数组名
	//...
	return 0;
}

总而言之,只要传入的表达式最终的类型是二级指针类型即可传入。

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

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

相关文章

Pytest自动化测试的三种运行方式

目录 1、主函数模式 2、命令行模式 3、通过读取pytest ini配置文件运行 &#xff08;最主要运用的方式&#xff09; 总结&#xff1a; Pytest 运行方式共有三种&#xff1a; 1、主函数模式 运行所有 pytest.main() 指定模块 pytest.main([-vs],,./testcase/test_day1.py)…

组合逻辑电路设计---多路选择器

目录 1、多路选择器简介 2、硬件设计 3、实验任务 4、程序设计 4.1、模块设计 4.2、绘制波形图 4.3、编写代码 &#xff08;1&#xff09;assign 中条件运算符&#xff08;三目运算符&#xff09;实现方法&#xff1a; &#xff08;2&#xff09;always 语句块中使用 …

逍遥自在学C语言 | 指针的基础用法

前言 在C语言中&#xff0c;指针是一项重要的概念&#xff0c;它允许我们直接访问和操作内存地址。 可以说&#xff0c;指针是C语言一大优势。用得好&#xff0c;你写程序如同赵子龙百万军中取上将首级&#xff1b;用得不好&#xff0c;则各种问题层出不穷&#xff0c;有种双…

.gitignore 忽略文件和目录

1. .gitignore 简介2. .gitignore 注释3. / 开头或结尾的忽略4. glob 模式匹配忽略5. .gitignore 全局忽略6. 忽略已提交到远程仓库的内容7. 使用各种框架下的忽略规则 1. .gitignore 简介 .gitignore 文件的作用就是告诉 git 哪些文件不需要添加到版本管理中&#xff08;定义…

Python如何制作图标点选验证码

本文讲解如何使用python中的opencv库来制作图标点选验证码 图标点选验证码制作起来非常简单,你只需要准备两部分数据集,数据集数量都不用很多,背景图我选择了20个左右,大小为(300, 500)左右,图标我抓取了100多个,图标大小为(40,40)左右,图标由不同大小的透明度构成…

html实现好看的个人介绍,个人主页模板1(附源码)

文章目录 1.设计来源1.1 主界面1.2 关于我界面1.3 自我介绍界面1.4 项目演示界面1.5 个人成就界面1.6 联系我界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/13125310…

01-Maven 安装

一. 下载 apache官网下载 Maven&#xff1a;Maven – Download Apache Maven &#xff0c;根据需要下载不同压缩包。 二. 安装和配置 因为是压缩包不是可执行文件&#xff0c;直接将压缩包进行解压即可&#xff0c;最好放在无中文目录下解压。 1. 配置maven本地仓库 打开解压…

自然语言处理从入门到应用——静态词向量预训练模型:神经网络语言模型(Neural Network Language Model)

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 《自然语言处理从入门到应用——自然语言处理的语言模型&#xff08;Language Model&#xff0c;LM&#xff09;》中介绍了语言模型的基本概念&#xff0c;以及经典的基于离散符号表示的N元语言模型&#xff08;N-gram…

每日一道算法---数组中出现次数超过一半的数字

数组中出现次数超过一半的数字 1.题目2.思路3.代码 1.题目 链接: 数组中出现次数超过一半的数字 2.思路 【解题思路1】&#xff1a; 思路一&#xff1a;数组排序后&#xff0c;如果符合条件的数存在&#xff0c;则一定是数组中间那个数。这种方法虽然容易理解&#xff0c;但…

gcov的使用

什么是代码覆盖率&#xff1f; 代码覆盖率是对整个测试过程中被执行的代码的衡量&#xff0c;它能测量源代码中的哪些语句在测试中被执行&#xff0c;哪些语句尚未被执行。 代码覆盖率的指标种类 代码覆盖率工具通常使用一个或多个标准来确定你的代码在被自动化测试后是否得…

SQL注入第一章节

SQL注入第一章节 1.1 什么是SQL注入 SQL 注入&#xff08;Injection&#xff09; 概述 SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情…

合并表格的指定列按序号排序

这里有一个Excel需求&#xff1a; 如下图所示&#xff0c;需要在序号那一列自动排序下去。 但是是合并的行&#xff0c;而且合并的行数还是不确定的&#xff0c;那怎么给他自动排序下去呢&#xff1f; 解决方法可供参考&#xff1a;使用筛选和COUNT函数完成。 1.第一步筛选 首…

Collection集合

Collection集合面试题 导学 这次课程主要涉及到的是List和Map相关的面试题&#xff0c;比较高频就是 ArrayList LinkedList HashMap ConcurrentHashMap ArrayList底层实现是数组LinkedList底层实现是双向链表HashMap的底层实现使用了众多数据结构&#xff0c;包含了数组、…

5.MySQL索引事务

文章目录 &#x1f43e;1. 索引&#x1f43e;&#x1f490;1.1 概念&#x1f490;&#x1f338;1.2 作用与缺点&#x1f338;&#x1f337;1.2.1作用&#x1f337;&#x1f340;1.2.2缺点&#x1f340; &#x1f339;1.3 使用场景&#x1f339;&#x1f33b;1.4 使用&#x1f3…

阿里云服务器官网

阿里云服务器官网&#xff1a;https://www.aliyun.com/product/ecs 阿里云服务器分为云服务器ECS、轻量应用服务器、GPU云服务器等&#xff0c;云服务器ECS是阿里云明星级产品&#xff0c;专业级云服务器&#xff0c;如下图&#xff1a; 阿里云服务器ECS 阿里云服务器网分享阿…

【论文阅读】Learing to summarize from human feedback

前言 更多关于大模型的文章可见&#xff1a;ShiyuNee/Awesome-Large-Language-Models: Papers about large language models (github.com) 该仓库持续更新 Abs 通过训练模型来向着人类偏好优化可以显著提高摘要质量。 Method High-level methodology 从一个在摘要数据集上…

剑指offer(C++)-JZ3:数组中重复的数字(算法-排序)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的&#xff0c;但不知道有几…

攻防世界-web-Web_python_template_injection

题目描述&#xff1a;只有一句话&#xff0c;翻译出来时python模板注入 1. 思路分析 1.1 什么是python模板注入&#xff1f; 做这道题之前我也不知道什么是python模板注入&#xff0c;问了下chatgpt&#xff0c;回答是这样的&#xff1a; 回答很简洁&#xff0c;举个例子&…

AUTOSAR 架构下的SPI模块的理解

一、SPI模块 1、模块简介 SPI处理程序/驱动程序为单片SPI [串行外设接口]处理程序/驱动程序提供功能和API。该软件模块包括处理和驱动功能。这种单片SPI处理器/驱动器的主要目标是充分利用每个微控制器的功能&#xff0c;并根据静态配置实现优化&#xff0c;以尽可能地满足ECU…

医疗虚拟仿真和虚拟现实有什么区别?哪个更好?

随着我们在仿真教育中越来越多地使用新技术&#xff0c;区分虚拟模式的类型很重要。虚拟仿真是一个统称&#xff0c;用来概括术语来描述各种基于仿真的体验&#xff0c;从基于屏幕的平台到沉浸式虚拟现实。然而&#xff0c;各虚拟平台在保真度、沉浸感和临场感的水平上有很大差…