带你深层了解c语言指针

news2024/11/15 17:32:20

在这里插入图片描述

前言

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏: 🍔🍟🌯 c语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:介绍c语言中有关指针更深层的知识.
金句分享:

✨今天所有的混乱与芜杂,努力与精进,✨
✨都将在进步中变得更加清晰.✨

目录

  • 前言
  • 一、字符指针
  • 二、指针数组
    • 1.1 指针数组的定义
    • 2.2 使用指针数组模拟二维数组
  • 三、数组指针
  • 四、一维数组的传参
    • 4.1 整形一维数组传参:
    • 4.2 一维指针数组传参
  • 五、二维数组传参
    • 二级指针作为参数

一、字符指针

我们可以定义一个字符指针,指向一个字符变量,并通过指针来修改这个字符变量.

#include <stdio.h>
int main()
{
	char a = 'x';
	char* p = &a;
	*p = 'y';//通过指针来修改
	printf("%c\n", *p);
	printf("%c", a);
	return 0;
}

那么字符指针还可以怎么用呢?

下面这段代码的运行结果是什么呢?

int main()
{
	char* s1 =  "aabbccdd" ;
	for (int i = 0; i < 3; i++)
	{
		*(s1 + i) = '0';
	}
	printf("%s", s1);
	return 0;
}

在这里插入图片描述
答案:

运行错误,原因是"aabbccdd"是常量字符串,它们存放在"常量区"(里面的东西是只读的),是不允许被修改的.

分析:

这段代码是将常量字符串存放进s1指针里面了吗?
显然不可能,想放也放不进去呀.
正确理解是:将该常量字符串的首元素放进了s1,即s1指向该字符串的首元素.

const关键字在后续的库函数模拟中会详细介绍,这里可以理解为被修饰的字符指针指向的内容,使其不能被修改.

使用字符指针打印字符串

int main()
{
	const char* s1 =  "aabbccdd" ;
	printf("%s", s1);
	return 0;
}

如果对上面的代码还是不理解可以看一下,下面的这一道曾经的笔试题:

#include <stdio.h>
int main()
{
	char str1[] = "你好,初阶牛!.";
	char str2[] = "你好,初阶牛!";
	const char* str3 = "你好,初阶牛!";
	const char* str4 = "你好,初阶牛!";
	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是两个的字符数组,两个数组是相互独立的,它们在创建时,会各自向内存申请空间,所以地址必然不一样,只不过是在内存中两块不同内存区域存放着相同的内容罢了.
由于字符串常量是不能修改的,在内存中这类常量并没有必要保存两份,他们存储在内存中的常量区(只读型)所以str3和str4指向的是同一块空间.

图解:
在这里插入图片描述

二、指针数组

1.1 指针数组的定义

指针数组数组还是指针?
答案是数组.
我们知道,

整形数组:存放整形的数组.
字符数组,:存放字符的数组.
浮点型数组:存放浮点型数据的数组,

	int arr1[] = { 1,2,3,4,5 };//整形数组
	char arrr2[] = "abcdef";//字符型数组
	double arr3[] = { 3.4,5.8,1.9 };//浮点型数组

那么用于存放指针数组自然被称为指针数组了.
那么下面哪个是指针数组?

	int* arr1[10];
	int(*arr2)[10];

由于" * “'(星号)的优先级要低于”[ ]"(中括号)

arr1指针数组: arr1先与[10]先结合,故arr1是指针数组
arr2数组指针: 由于括号的优先级更高,所以(*arr2)是指针,指向的对象是数组,故arr2是数组指针.

常见的指针数组:

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

2.2 使用指针数组模拟二维数组

#include <stdio.h>
int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };
	int* arr[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", *(arr[i] + j));
		}
		printf("\n");
	}
	return 0;
}

arr1数组,arr2数组,arr3数组在内存中都有自己独立的内存空间,
我们将这三个一维数组的首元素地址放在一个新的指针的数组(arr)中,通过指针数组来访问这三个一维数组,效果就如同二维数组一样,但并不是真正的二维数组.
图解:
在这里插入图片描述
内存中存储的图:
在这里插入图片描述

三、数组指针

指向数组指针被称为数组指针.

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
	//指针p就是数组指针,指向arr数组
	return 0;
}

数组指针对于一维数组作用并不是很大,数组指针一般用于二维数组.
在二维数组传参时可以使用数组指针接收.

对于初学者,数组指针指针数组经常容易搞混.
试着分析下面这些代码的含义吧!

int arr[5];//1
int *arr1[10];//2
int (*arr2)[10];//3
int (*arr3[10])[5];//4

在这里插入图片描述

答案:

1.整形数组:一个包含5个整形元素的整形数组
2.指针数组:数组中包含十个元素,每个元素都是一个整形指针(int*).
3.数组指针:指向一个拥有十个元素的整形指针.
4.数组指针数组:arr3先与[10]结合,说明arr3是一个数组,
其次剩下的int(*)[5]是arr3数组中的元素,显然是数组的每个元素都是一个数组指针.故arr3是存放了10个指向有5个整形元素数组的数组指针数组.

四、一维数组的传参

4.1 整形一维数组传参:

整形一维数组传过来的arr的是数组首元素地址,即一个整形的地址.
整形变量的地址用一级整形指针接收或者同样用数组接收都行.

void test1(int arr[])//使用不指定具体大小的一维数组接收
{}
void test2(int arr[10])//使用具体大小的一维数组接收
{}
void test3(int *arr)//使用指针接收
{}

具体实现:

#include <stdio.h>
//使用同样一维数组接收
void test1(int arr[],int sz)//arr1
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
//与test1一样的道理,用指定大小接收当然也没问题
void test2(int arr[5],int sz)//arr1
{}
//使用指针接收
void test3(int* arr,int sz)//arr1
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr+i));//等价于printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	test1(arr1,sz);
	test2(arr1,sz);
	test3(arr1,sz);
	return 0;
}

4.2 一维指针数组传参

这三种有一种是错误的.

void test4(int *arr[20])
{}
void test5(int* arr)
{}
void test6(int **arr)
{}

一维指针数组的数组名表示首元素的地址,而首元素是一个整形指针,一级(整形)指针的地址可以用二级指针接收.也可以用相同类型的一维指针数组接收.

#include <stdio.h>
//用整形指针数组接收整形指针数组
void test4(int* arr[3])
{
	for (int i = 0; i < 3; i++)//决定访问整形指针的个数
	{
		for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数
		{
			printf("%d ", *(arr[i] + j));
		///等价于printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
//错误,不能使用
void test5(int* arr)
{

}
//用二级指针接收一维指针数组
void test6(int** arr)
{
	for (int i = 0; i < 3; i++)//决定访问整形指针的个数
	{
		for (int j = 0; j < 4; j++)//决定使用整形指针向后访问整形元素的个数
		{
			printf("%d ", *(*(arr+i) + j));//这种一般不好理解
			//等价于	printf("%d ", *(arr[i] + j));
			//等价于	printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };
	int* arr[3] = { arr1,arr2,arr3 };
	test4(arr);
	test5(arr);
	test6(arr);
}

五、二维数组传参

给定一个二维整形数组:
二维数组的数组名代表首元素地址,即第一行的地址(int*[ ]);

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

下面七种传参方式,哪些是正确的.

void test1(int arr[2][4])
void test2(int arr[][])
void test3(int arr[][4])
void test4(int *arr)
void test5(int* arr[4])
void test6(int (*arr)[4])
void test7(int **arr)

在这里插入图片描述

正确传参方式:test1()、test3()和test6()

#include <stdio.h>
void test1(int arr[2][4])
{
	int i = 0, j = 0;
	printf("test1\n");
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
void test3(int arr[][4])
{
	int i = 0, j = 0;
	printf("test2\n");
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
void test6(int(*arr)[4])//ok?
{
	int i = 0, j = 0;
	printf("test3\n");
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
int main()
{
	int arr[2][4] = { {1,2,3,4 },{5,6,7,8} };
	test1(arr);
	test3(arr);
	test6(arr);
}

二级指针作为参数

二级指针可以接收一级指针的地址.

#include <stdio.h>
void print1(int** aa)
{
	printf("%d\n", **aa);
}
void print2(char** pp)
{
	printf("%c\n", **pp);
}
int main()
{
	int a = 5;
	int* aa = &a;
	char* p = "abcdef";
	char** pp = &p;
	print1(&aa);
	print2(&p);
	print2(pp);
	return 0;
}

指针进阶的内容比较多,本篇先讲到这里.
最后希望这篇文章对大家有些帮助.
感觉支持!!!💗💗💗

在这里插入图片描述

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

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

相关文章

麻省理工学院,Web3 人才辈出

2 月 22 日&#xff0c;NFT 交易平台 Blur 创始人公开身份&#xff0c;曾就读于麻省理工学院计算机系。除了 NFT 交易平台&#xff0c;在公链、交易所、VC、媒体、Layer2 等 Web3 和 Crypto 的多个赛道&#xff0c;麻省理工学院&#xff08;MIT&#xff09;的毕业生和教授们均有…

HDMI协议介绍(二)--DataIsland Packets

前言 前文简单介绍了HDMI传输的三个周期&#xff1a;控制周期、DataIsland周期和Video周期。DataIsland传输音频和辅助数据&#xff0c;这些数据以Packet的形式传输。本文简单介绍DataIsland Packet。 目录 前言 数据岛概述 数据岛包的构造 Packet Header Paket Body Dat…

从0开始学python -40

Python3 面向对象-3 类属性与方法 类的私有属性 __private_attrs &#xff1a;两个下划线开头&#xff0c;声明该属性为私有&#xff0c;不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs 。 类的方法 在类的内部&#xff0c;使用 def 关键字…

Linux驱动——设备模型

目录 一、起源 二、新方案 2.1 sysfs: 2.2 uevent 三、代码中自动mknod 四、实例 一、起源 仅devfs&#xff0c;导致开发不方便以及一些功能难以支持&#xff1a;&#xff08;硬编&#xff09; 1. 热插拔&#xff08;插上usb设备就立马能安装驱动&#xff09; 2. 不支持…

蓝桥杯2015年第六届真题-奇怪的数列C++

题目&#xff1a;从X星截获一份电码&#xff0c;是一些数字&#xff0c;如下&#xff1a;13111331131321131113122113....YY博士经彻夜研究&#xff0c;发现了规律&#xff1a;第一行的数字随便是什么&#xff0c;以后每一行都是对上一行“读出来”比如第2行&#xff0c;是对第…

hadoop网站流量日志数据统计

系统背景介绍 数据提供企业决策能力 网站日志的数据分析------》受欢迎程度如何评价你这个网站的受欢迎程度 用数据 下载的人多了 观看的人多了 通过Hadoop 对某个网站产生的日志数据流量进行统计分析 得出该网站的访问流量 继而做出决策我们可以采用flume收集nginx的success文…

AcWing算法提高课-2.1.1池塘计数

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 题目传送门点这里 题目描述 农夫约翰有一片 N∗M 的矩形土地。 最近&#xff0c;由于降雨的原因&#xff0c;部分土地被水淹没了。 现在用一个字符矩阵来表示他的土地。 每个单元格内&…

EasyRecovery16最新免费版电脑数据恢复软件功能介绍

EasyRecovery是一款支持Windows/Mac平台进行恢复图片的专业工具&#xff0c;尤其是各种流行单反相机RAW格式文件&#xff0c;以及超大型视频文件等&#xff0c;推荐摄影爱好者使用。适用于主流相机、无人机、PC、存储卡、USB 闪存驱动器等&#xff0c;由于删除、损坏或意外格式…

【项目精选】医院管理住院系统的研究与实现(源码+论文+视频)

点击下载源码 本系统主要分为六大模块&#xff0c;分别是医生管理模块、病人管理模块、病床管理模块、收费管理模块、统计分析模块和系统功能模块 &#xff0c;医生、病人和医院的管理人员都可以通过此系统寻找出自己所需要的信息。 1.1 背景 医院管理住院系统是当今大部分现代…

Nginx安装及介绍

前言&#xff1a;传统结构上(如下图所示)我们只会部署一台服务器用来跑服务&#xff0c;在并发量小&#xff0c;用户访问少的情况下基本够用但随着用户访问的越来越多&#xff0c;并发量慢慢增多了&#xff0c;这时候一台服务器已经不能满足我们了&#xff0c;需要我们增加服务…

华为OD机试用Python实现 -【天然蓄水库 or 天然蓄水池】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲天然蓄水库 or 天然蓄水池题目描述输入描述输出描述说明示例一输入输出说明示例二输入输出说明示例三输入输出说明Python 代码实现算法思路华为OD机试300题大纲 参加华为

Android 分区和内存监控

Andorid之所以是分区&#xff0c;是因为各自有对应的功能和用途的考量&#xff0c;可以进行单独读写和格式化。Android 设备包含两类分区&#xff1a;一类是启动分区&#xff0c;对启动过程至关重要。一类是用户分区&#xff0c;用于存储与启动无关的信息。启动分区boot 分区一…

HTML综合案例练习

一、展示简历内容 可以首先看一下我们的效果&#xff0c;之后再思考怎么实现 总的来说&#xff0c;这个练习不算难。 这里关于这个简历的代码编写我们不说太多&#xff0c;只注意以下几个内容即可&#xff1a; 注意及时查看我们的代码是否符合预期&#xff0c;即一段一段测 …

@ModelAttribute注释,接收用户不能做出修改的值,定义好值不变化

举例&#xff1a;有时候我们做修改的时候&#xff0c;比如用户的生日&#xff0c;和姓名用身份证验证之后就默认了你的资料&#xff0c;后期在修改个人资料的时候是不允许修改的&#xff0c;只能修改兴趣等等......1.设置实体类User&#xff0c;定义好属性&#xff0c;以及get,…

进程优先级(Linux)

目录 优先级VS权限 基本概念 查看系统进程 几个重要信息 PRI and NI PRI vs NI top命令 上限&#xff1a; 详细步骤 下限&#xff1a; 其他概念 优先级VS权限 权限&#xff1a;能or不能 优先级&#xff1a;已经能&#xff0c;但是谁先谁后的问题&#xff08;CPU资源有…

(十七)操作系统-进程同步、互斥

文章目录一、知识总览二、进程同步三、进程互斥1. 临界资源2. 互斥3. 对临界资源的互斥访问&#xff0c;在逻辑上分为四个部分4. 为了实现对临界资源的互斥访问&#xff0c;同时保证系统整体性能&#xff0c;需要遵循的原则五、总结一、知识总览 二、进程同步 同步亦称直接制约…

Kubernetes学习笔记-pod与集群节点的自动伸缩20230225

前言pod手动横向扩展&#xff1a;通过ReplicationController、ReplicaSet、Deployment等可伸缩资源的replicas字段&#xff0c;来手动实现pod中应用的横向扩展。pod纵向扩展&#xff1a;通过增加pod容器的资源请求和限制&#xff08;pod创建时&#xff09;pod自动横向扩展&…

Allegro如何快速把视图居中显示操作指导

Allegro如何快速把视图居中显示操作指导 用Allegro进行PCB设计的时候,为了方便检查和设计,时常需要将视图居中显示。一般地,会使用鼠标的中键进行放大和缩小,或者使用Zoom in和Zoom out来调整视图 Allegro还支持快速将视图居中 具体操作如下 点击View

Flume三种组件的选择对比

文章目录1.source2.channel3.sink1.source Source: 数据源:通过source组件可以指定让Flume读取哪里的数据&#xff0c;然后将数据传递给后面的 channel Flume内置支持读取很多种数据源&#xff0c;基于文件、基于目录、基于TCP\UDP端口、基于HTTP、Kafka的 等等、当然了&#x…

基于Windows下离线安装当前最新Arduino ESP32 SDK(2.0.7)固件开发包

基于Windows下离线安装当前最新Arduino ESP32 SDK&#xff08;2.0.7&#xff09;固件开发包✨写这篇的文章的初衷&#xff0c;是由于在前几天想通过离线一键安装包方式实现升级安装&#xff0c;结果发现解压后&#xff0c;可以找到开发板&#xff0c;但是无法上传代码&#xff…