『C语言进阶』指针进阶(一)

news2024/11/24 13:51:27

在这里插入图片描述
🔥博客主页 小羊失眠啦
🔖系列专栏 C语言
🌥️每日语录无论你怎么选,都难免会有遗憾。
❤️感谢大家点赞👍收藏⭐评论✍️


在这里插入图片描述

前言

在C语言初阶中,我们对指针有了一定的了解,指针是个变量,是用来存放地址的,指针的大小是固定的4/8个字节,指针是有类型的,指针的类型决定了指针的±整数的步长以及指针的运算,小羊最近已经开学,博客可以正常更新了,好啦,接下来让我们再进一步的了解指针!!


一、字符指针

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

例1

#include<stdio.h>
int main()
{
	char ch = 'x';
	char* p = &ch;//pc是字符指针
	ch = 'a';
	*p = 'a';
	return 0;
}
//ch是字符变量,可以直接改变ch的值,
//pc这时是字符指针,存放的是ch的地址,也可以通过指针来改变变量ch的值。

例2

#include<stdio.h>
int main()
{
	char arr[] = "abcdefg";//创建数组,用字符串来初始化
	char* p = "abcdefg";//常量字符串,"abcdefg"存放在"常量区",只读,不允许被修改
	//指针变量p存放的是字符串首元素的地址
	*p='a';//erro 这种情况会报错,pa不能改变为w
}

例3:这段代码运行结果是什么?

int main()
{
	char* s = "abcdefg";
	for (int i = 0; i < 4; i++)
	{
		*(s + i) = '0';
	}
	printf("%s\n", s);
	return 0;
}

结果:

运行错误,原因是"abcdefg"是常量字符串,它们存放在"常量区",只读,不允许被修改,所以一般写成const char* s="abcdefg"

笔试题

int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	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
str3和str4里面存放的地址是同一个,都是h的地址,所以是一样的


二、指针数组

2.1 认识指针数组

在C语言初阶里的初识指针中,铁汁们对指针数组也有一定的了解,指针数组是一个存放指针的数组。

整形数组--存放整形的数组
字符数组--存放字符的数组
指针数组--存放指针的数组

#include<stdio.h>
int main()
{
	int arr1[3];  //存放了三个整形的数组 int int int
	int* arr2[3]; //存放了三个整形指针的数组 int* int* int*
	char* arr3[3];//存放了三个一级字符指针的数组 char* char* char*
	char** arr4[3];//存放了三个二级字符指针的数组 char** char** char**
	return 0;
}

牛刀小试

int main()
{
	char* arr[] = { "abcd","efgh","igkl" };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
		printf("%s ", arr[i]);
	return 0;
}

在这里插入图片描述

arr是首元素地址

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 arr4[4] = { 4,5,6,7 };
	int* arr[4] = { arr1,arr2,arr3,arr4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", *(arr[i] + j));
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述
结果分析:

经过调试内存可得:
	int arr1[4] = { 1,2,3,4 };              //arr1的地址是:0x0000001C526FF7F8
	int arr2[4] = { 2,3,4,5 };              //arr2的地址是:0x0000001C526FF828
	int arr3[4] = { 3,4,5,6 };              //arr3的地址是:0x0000001C526FF858
	int arr4[4] = { 4,5,6,7 };              //arr4的地址是:0x0000001C526FF888
由此可见,arr1,arr2,arr3,arr4数组内存中有独立的内存空间,并不是连续的四个内存空间,
我们将这四个一维数组的首元素的地址放在指针数组arr中,通过指针数组来访问这四个一维数组,效果和二维数组是一样的,但是并不是真正的二维数组!!

三、数组指针

3.1 数组指针的定义

定义:指向数组指针被称为数组指针
由于指针数组和数组指针对于初学者很容易混肴,所以我们先通过类比的方法来认识数组指针。

字符指针–指向字符变量的指针,即存放字符变量地址的指针变量。
整型指针–指向整型变量的指针。即存放整型变量地址的指针变量
数组指针–指向数组变量的指针。即存放数组变量地址的指针变量

#include<stdio.h>
int main()
{
	int a = 2;
	int* p = &a; 
	int arr[10]; 
	int* arr[10];  //arr先和[10]结合,所以arr是一个数组,里面存的都是指针变量,即为指针数组
	int(*arr)[10];//arr先和*结合所以arr是一个指针变量,又指向数组,即为指针数组
	return 0;
}

解释:p先和*结合,说明p是一个指针变量,然后指向一个大小为10个整型的数组(指向的类型 int [10])。所以p是一个指针,指向一个数组,叫数组指针。

注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

3.2 &数组名VS数组名

  • 通常情况下,数组名是首元素地址
  • sizeof(数组名):计算的是整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组
  • &数组名:取出的是数组的地址,&数组名,数组名表示整个数组
#include<stdio.h>
int main()
{
	int arr[5] = { 0,1,2,3,4 };
	printf("  arr=%p\n", arr);
	printf("arr+1=%p\n", arr + 1);
	printf("\n");
	printf("  &arr[0]=%p\n", &arr[0]);
	printf("&arr[0]+1=%p\n", &arr[0] + 1);
	printf("\n");
	printf("  &arr=%p\n", &arr);
	printf("&arr+1=%p\n", &arr + 1);
	return 0;
}

在这里插入图片描述

3.3 数组指针的使用

数组指针指向的是数组,那数组指针中存放的数组的地址

#include<stdio.h>
int main()
{
	int arr[5];
	int(*p)[5]=&arr;//p的类型是int(*)[5],存放的是存放int类型的数组
	int* pp[5];//指针数组,pp是数组,存放的是int*类型
	int* (*ppp) = &pp;//ppp的类型是int*(*)[5],存放的是存放int*类型的数组
	return 0;
}

3.3.1 打印数组元素

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, i = 0;
	int* p = arr;
	int(*pp)[10] = &arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *((*pp) + i));
	}
	return 0;
}

3.3.2 打印二维数组元素

#include<stdio.h>

void Print1(int arr[3][5], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

void Print2(int(*p)[5], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int 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} };
	Print1(arr, 3, 5);
	printf("\n");
	Print2(arr, 3, 5);
	return 0;
}

3.4 总结:

在这里插入图片描述
数组名arr,表示首元素的地址,二维数组的首元素是二维数组的第一行所以这时传递的arr,其实相当于第一行的地址,是一维数组的地址可以数组指针来接收。

补充:

一维数组传参,形参的部分可以是数组,也可以是指针
二维数组传参,形参的部分可以是数组,也可以是指针

注意:形参写成数组形式是为了让人更容易理解,本质上是指针


四、数组传参和指针传参

4.1 一维数组传参

数组传参的时候,形参可以写成同样数组形式,若是用数组作形参,[ ]里面的值可以省略,也可以随意赋值。传过来的arr是数组首元素地址,所以也可以用一级整形指针来接受。

#include<stdio.h>
void test1(int arr[]);//正确
void test2(int arr[10]);//正确
void test3(int* arr);//正确
int main()
{
	int arr1[10] = { 0 };
}

实战演练

#include<stdio.h>

void test1(int arr[],int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void test2(int arr[10],int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void test3(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr+i));
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	test1(arr, sz);
	printf("\n");
	test2(arr, sz);
	printf("\n");
	test3(arr, sz);
	return 0;
}

4.2 二维数组传参

void test(int arr[3][5])//ok?
{}
OK
void test(int arr[][])//ok?
{}
NO  二维数组传参,函数形参的设计只能省略行数,不能省略列数
二位数组传参,传的是二维数组第一行的地址,而不是第一行第一个元素的地址
void test(int arr[][5])//ok?
{}
OK
void test(int* arr)//ok?
{}
NO
void test(int* arr[5])//ok?
{}
NO  这是一个存放int* 类型的数组
void test(int(*arr)[5])//ok?
{}
OK  这是一个存放数组的一级指针
void test(int** arr)//ok?
{}
NO
int main()
{
    int arr[3][5] = { 0 };
    test(arr);
}

4.3 一维指针传参

void test(int* p)
{}

int main()
{
	int n = 10;
	test(&n);

	int* p = &n;
	test(p);

	int arr[5] = { 0 };
	test(arr);
	return 0;
}

4.4 二维指针传参

void test(int** p)
{}
 
int main()
{
	int n = 10;
    int* p=&n;
	test(&p);
 
	int** pp = &p;
	test(pp);
 
	int* arr[5] = { 0 };
	test(arr);
	return 0;
}

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有问题可以在评论区留言,小羊一定认真认真修改,以后写出更好的文章。
在这里插入图片描述

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

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

相关文章

交叉编译嵌入式linux平台的gdb工具

目录 前期准备&#xff1a; 开始编译&#xff1a; 配置编译环境&#xff1a; 配置交叉编译工具链&#xff1a; 创建交叉编译产物的目录&#xff1a; termcap&#xff1a; ncurses&#xff1a; gmp&#xff1a; gdb&#xff1a; 编译产物&#xff1a; 前期准备&#x…

IT设备监控软件有哪些功能

IT设备监控软件通常可以实现以下功能&#xff1a;  设备状态监控&#xff1a;可以实时监测IT设备的运行状态&#xff0c;如设备的温度、湿度、风扇转速等&#xff0c;以及设备的开机、关机、重启等事件。  性能指标监控&#xff1a;可以监测IT设备的各项性能指标&#xff0…

rtthread下spi device架构MCP25625驱动

1.CAN驱动架构 由于采用了RTT的spi device架构&#xff0c;不能再随心所遇的编写CAN驱动 了&#xff0c;之前内核虽然采用了RTT内核&#xff0c;但是驱动并没有严格严格按RTT推荐的架构来做&#xff0c;这次不同了&#xff0c;上次是因为4个MCP25625挂在了4路独立的SPI总线上&…

前端(十六)——Web应用的安全性研究

&#x1f642;博主&#xff1a;小猫娃来啦 &#x1f642;文章核心&#xff1a;Web应用的安全性研究 文章目录 概述常见前端安全漏洞XSS&#xff08;跨站脚本攻击&#xff09;CSRF&#xff08;跨站请求伪造&#xff09; 点击劫持安全性验证与授权用户身份验证授权与权限管理 安全…

“搭载超快闪充、续航自由、天玑8200性能” iQOO Z8系列发布

近日&#xff0c;“天玑 8200 性能小超人”iQOO Z8系列正式发布&#xff0c;包括iQOO Z8和iQOO Z8x两款产品&#xff0c;首销售价1199元起。 “天玑 8200 性能小超人”iQOO Z8倾力打造“最佳千元性能机”&#xff1a;搭载具备巅峰性能的天玑 8200 &#xff0c;携手满血版LPDDR…

熵 | 无线通信知识

文章目录 一、信息论&#xff08;熵、联合熵、条件熵&#xff09;二、Bernoulli熵三、联合熵和条件熵四、互信息五、相对熵(KL距离)常需要的不等式公式 一、信息论&#xff08;熵、联合熵、条件熵&#xff09; 熵定义&#xff1a; H ( X ) E [ − l o g 2 p ( x ) ] − ∑ x…

【算法|链表】移除链表元素

算法|链表-移除链表元素 关于链表的介绍以及相关实现操作&#xff0c;见单链表&#xff0c;双链表 leetcode 203 移除链表元素 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1…

Unity3D之动态生成指定数量带间隔的地面

文章目录 准备代码实现实现效果 准备 空物体生成脚本地面预制体 代码实现 using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine;public class CreateGround : MonoBehaviour {[SerializeField]public i…

多功能透明屏,在智能家居领域中,有哪些功能特点?显示、连接

多功能透明屏是一种新型的显示技术&#xff0c;它能够在透明的表面上显示图像和视频&#xff0c;并且具有多种功能。 这种屏幕可以应用于各种领域&#xff0c;如商业广告、智能家居、教育等&#xff0c;为用户提供更加便捷和多样化的体验。 首先&#xff0c;多功能透明屏可以…

NIFI关于Parameter Contexts的使用

说明 nifi版本&#xff1a;1.23.2&#xff08;docker镜像&#xff09; 作用 Parameter Contexts&#xff08;参数上下文&#xff09;&#xff1a;参数上下文由 NiFi 实例全局定义/访问。访问策略可以应用于参数上下文&#xff0c;以确定哪些用户可以创建它们。创建后&#x…

CRM系统中的工作流管理及其重要性

工作流是CRM系统中较为常见的功能&#xff0c;它可以有效减少重复工作、提高销售效率。如果您想深入了解&#xff0c;本文就来详细说说&#xff0c;CRM工作流是什么&#xff1f;工作流的作用&#xff1f; 什么是CRM工作流&#xff1f; CRM工作流是指在CRM系统中&#xff0c;根…

Python编程的八大魔法库

Python是一门广受欢迎的编程语言&#xff0c;其生态系统丰富多彩&#xff0c;拥有许多令人惊叹的依赖库&#xff0c;可以帮助程序员们在各种领域中创造出令人瞠目结舌的应用。在这篇文章中&#xff0c;我们将探讨Python编程的十大神奇依赖库&#xff0c;它们像魔法一样&#xf…

3.运行项目

克隆项目 使用安装的git克隆vue2版本的若依项目&#xff0c;博主使用的版本是3.8.6. git clone https://gitee.com/y_project/RuoYi-Vue.git目录结构如下图所示&#xff0c;其中ruoyi-ui是前端的内容&#xff0c;其它均为后端的内容。 配置mysql数据库 在数据库里新建一个…

实体门店如何变“流量”为“存量”门店数字化积分体系如何设计?

随着各行各业特别是实体商家对于数字化工具的广泛使用&#xff0c;积分系统对于实体客户的留存、转化相较于其他营销手段&#xff0c;拥有更好的数据反馈。简单而言&#xff0c;积分指的是在某个平台上流通的虚拟数值&#xff0c;可以辅助平台提升活动运营效果&#xff0c;提升…

Python 操作 Word

上次给大家介绍了 Python 如何操作 Excel &#xff0c;是不是感觉还挺有趣的&#xff0c;今天为大家再介绍下&#xff0c;用 Python 如何操作 Word &#xff0c;这个可能跟数据处理关系不大&#xff0c;用的也不多&#xff0c;不过可以先了解下都能实现什么功能&#xff0c;以备…

阿里巴巴国际站测评补单有哪些优势,需要哪些技术要求

阿里巴巴国际站买家复购等销售指标就成为了判断你这个产品是否能大卖的第一标准。判断后的潜力大就给你更多的流量&#xff0c;潜力小则排名靠后。所以补单的目的就是通过GMV等重要指标的提升&#xff0c;增加系统对你产品的潜力预判&#xff0c;使你的链接在赛马机制中优先&am…

文件夹高效管理,轻松实现多次复制!

在日常工作和生活中&#xff0c;我们经常需要复制某些文件夹。但是&#xff0c;传统的复制方法往往需要多次操作&#xff0c;不仅耗时还容易出错。为了解决这个问题&#xff0c;我们为您提供一种高效管理文件夹的方法&#xff0c;让您轻松实现多次复制&#xff01; 首先我们要…

【论文+代码】1706.Transformer简易学习笔记

Transformer 论文: 1706.attention is all you need! 唐宇迪解读transformer&#xff1a;transformer2021年前&#xff0c;从NLP活到CV的过程 综述&#xff1a;2110.Transformers in Vision: A Survey 代码讲解1: Transformer 模型详解及代码实现 - 进击的程序猿 - 知乎 代码讲…

从零开始探索C语言(六)----数组

文章目录 1. 数组初识1.1 数组声明1.2 数组初始化1.3 数组元素的访问1.4 获取数组长度1.5 数组名 2. 多维数组3. 形参数组4.函数返回数组5. 指向数组的指针6. 静态数组和动态数组6.2 静态数组6.2 动态数组 1. 数组初识 C 语言支持数组数据结构&#xff0c;它可以存储一个固定大…

【C++杂货铺】探索list的底层实现

文章目录 一、list的介绍及使用1.1 list的介绍1.2 list的使用1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity&#xff08;容量相关&#xff09;1.2.4 list element access&#xff08;元素访问&#xff09;1.2.5 list modifiers&#xff08;链表修改&#xff0…