C指针之舞——指针探秘之旅

博客主页:折枝寄北-CSDN博客

专栏内容:C语言学习专栏icon-default.png?t=O83Ahttps://blog.csdn.net/2303_80170533/category_12794764.html?spm=1001.2014.3001.5482

指针基础学习 在之前的博客文章中,简单总结了指针的基础概念

我们知道了指针的概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4. 指针的运算。

今天对指针进行扩充学习并加深理解

1. 字符指针

1.1字符指针定义及其格式

在指针的类型中我们知道有一种指针类型为字符指针 char* ;

字符指针使用格式如下:

//一般使用情况:
int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}


//第二种使用情况:
int main()
{
    const char* pstr = "hello world.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

代码 const char* pstr = "hello world.";很容易让人误以为是把字符串 hello world 放到字符指针 pstr 里了,但是本质是把字符串 hello world. 首字符的地址放到了pstr中。

1.2字符指针应用示例

知道字符指针存储的是字符串的字符首元素地址,我们通过一道题目来进行验证。

#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;
}

这里最终输出的是:

这里str3和str4指向的是一个同一个常量字符串(指向同一块地址区域)     C/C++会把常量字符串存储到单独的一个内存区域,当成两个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

2. 指针数组

2.1指针数组定义及其格式

定义:指针数组是一个存放指针的数组。

指针数组使用格式如下:

int main()
{
	const char* arr[4] = { "hello","world","come","back" };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

2.2指针数组应用示例

示例:打印数组元素

int main()
{
	const char* arr[4] = { "hello","world","come","back" };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int arr4[] = { 4,5,6,7,8 };

	int* arr[4] = { arr1,arr2,arr3,arr4 };
	int i = 0;
	for(i = 0; i < 4; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", arr[i][j]);
			printf("%d ", *(arr[i]+j));//arr[i]+j是地址,*()--->进行解引用

		}
		printf("\n");
	}
	return 0;
}

代码结果为:

通过一个简图来理解如何存放指针数组

3. 数组指针

3.1数组指针定义及其格式

字符指针---存放字符地址的指针---char*

整型指针---存放整型地址的指针---int*

浮点型指针---存放浮点型地址的指针---float*  double*

定义:存放指向数组地址的指针,指向数组的指针。

数组指针使用格式如下:

int main()
{
	//字符指针
	char ch = 'w';
	char* pc = &ch;
	//整型指针
	int num = 10;
	int* pi = &num;

	//数组指针
	int arr[10] = {0};
	//pa就是一个数组指针
	int (*pa)[10] = &arr;//数组指针的形式
	return 0;
}

3.2数组指针应用示例

示例1:打印数组(用数组指针)

void print1(int arr[3][4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

void print2(int(*p)[4], 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]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print1(arr, 3, 4);
	print2(arr, 3, 4);
	return 0;
}

代码结果如下:

用一个简图来理解如何访问数组:

3.3数组名和&数组名有何不同

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr);

	return 0;
}

运行上面的代码,得出的结果一致,但是实质是不一样的。

对代码进行修改(加一),观察有何不同

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);

	printf("%p\n", &arr);
	printf("%p\n", &arr+1);
	return 0;
}

观察代码运行结果我们可以得出:

printf("%p\n", arr);  int*
printf("%p\n", arr+1);  地址加4

printf("%p\n", &arr[0]);  int*
printf("%p\n", &arr[0]+1);  地址加4

printf("%p\n", &arr);  int(*)[10]
printf("%p\n", &arr+1);  地址加40

综上可得:

数组名--数组首元素的地址

&数组名--是数组的地址

二者在值的大小上是一致的,但是二者的意义不同

完整代码片段及解释:


int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//&arr取出的是数组的地址,只有数组的地址才需要数组来接收
	int(*p)[10] = &arr;                            
	
	//数组名-数组首元素的地址
	//&数组名-是数组的地址
	//二者从值的大小来看是一致的,但是二者的意义不一样。
	printf("%p\n", arr);
	printf("%p\n", arr+1);//+4


	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);//+4


	printf("%p\n", &arr);//int(* )[10]
	printf("%p\n", &arr+1);//+40,跳过了整个数组
 
	char arr[5];
	char(*pa)[5] = &arr;
	
 return 0;
}

指针数组是一个存放指针的数组。 这里我们再复习一下,下面所提及的都是什么意思?

1. int arr[5];//整型数组,数组有5个元素

2. int *par1[10];//指针数组,数组10个元素,每个元素是int*类型

3. int (*parr2)[10];//数组指针,该指针指向一个数组,数组有10个元素,每个元素是int类型

4.int(*parr3[10])[5];//parr3是数组,数组有10个元素,数组每个元素的类型是int(*)[5]的数组指针类型

简图理解4.

4. 数组传参和指针传参

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.1 一维数组传参

#include<stdio.h>
void test(int arr[])//ok(√)
{}

void test(int arr[10])//ok(√)
{}

void test(int* arr)//ok(√)
{}

void test2(int *arr2[20])//ok(√)
{}

void test2(int **arr)//ok(√)
{}

int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };

	test(arr);
	test2(arr2);

	return 0;
}

4.2二维数组传参

void test(int arr[3][5])//ok(√)
{}

void test(int arr[][])//行可以省略,列不可以省略不写(×)
{}

void test(int arr[][5])ok(√)
{}

//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。

void test(int *arr)//(×)
{}
void test(int* arr[5])//(×)
{}

void test(int (*arr)[5])//(×)
{}

void test(int **arr)//(×)
{}

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

4.3一级指针传参

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

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

例如:

void test(int *p)

{}

int a=0;

int *p=&a;

int arr[10];

test(arr);

test(&a);

test(p);

4.4二级指针传参

#include<stdio.h>
void test(int** ptr)
{
	printf("num=%d\n", **ptr);
}

int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);

	return 0;
}

当函数的参数为二级指针的时候,可以接收什么参数?

可以接受:二级指针,指针数组的数组名.....

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:腾讯云自媒体同步曝光计划 - 腾讯云开发者社区-腾讯云

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB绘制克莱因瓶

MATLAB绘制克莱因瓶 clc;close all;clear all;warning off;% clear all rand(seed, 100); randn(seed, 100); format long g;% Parameters u_range linspace(0, 2*pi, 100); v_range linspace(0, pi, 50); [U, V] meshgrid(u_range, v_range);% Parametric equations for t…

Spring Cloud微服务下如何配置I8n

什么是I8n 国际化&#xff08;I18n&#xff09;指的是设计和开发产品的过程&#xff0c;使得它们能够适应多种语言和文化环境&#xff0c;而不需要进行大量的代码更改。这通常涉及到创建一个基础版本的产品&#xff0c;然后通过配置和资源文件来添加对不同语言和地区的支持。 这…

将分割标签数据从JSON格式转换为YOLOv8的TXT格式

AnyLabeling是一款突破性的开源图像标注工具。 一、主要功能与特点 融合传统标注工具优点&#xff1a;AnyLabeling结合了LabelImg和Labelme等传统标注软件的优点&#xff0c;提供了多边形、矩形、圆形、线条和点等多种标注形式&#xff0c;满足不同场景的需求。强大的AI自动标…

【graphics】图形绘制 C++

众所周知&#xff0c;周知所众&#xff0c;图形绘制对于竞赛学僧毫无用处&#xff0c;所以这个文章&#xff0c;专门对相关人员教学&#xff08;成长中的码农、高中僧、大学僧&#xff09;。 他人经验教学参考https://blog.csdn.net/qq_46107892/article/details/133386358?o…

Javaweb梳理17——HTMLCSS简介

Javaweb梳理17——HTML&CSS简介 17 HTML&CSS简介17.1 HTML介绍17.2 快速入门17.3 基础标签17.3 .1 标题标签17.3.2 hr标签17.3.3 字体标签17.3.4 换行17.3.8 案例17.3.9 图片、音频、视频标签17.3.10 超链接标签17.3.11 列表标签17.3.12 表格标签17.3.11 布局标签17.3.…

637. 二叉树的层平均值【 力扣(LeetCode) 】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 637. 二叉树的层平均值 一、题目描述 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 二、测试用例 示例 1&a…

selenium元素定位---元素点击交互异常解决方法

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、异常原因 在编写ui自动化时&#xff0c;执行报错元素无法点击&#xff1a;ElementClickInterceptedException 具体报错&#xff1a;selenium.common.exc…

ARM64环境部署EFK8.15.3收集K8S集群容器日志

环境规划 主机IP系统部署方式ES版本CPU架构用户名密码192.168.1.225Ubuntu 22.04.4 LTSdockerelasticsearch:8.15.3ARM64elasticllodyi4TMmZD ES集群部署 创建持久化目录(所有节点) mkdir -p /data/es/{data,certs,logs,plugins} mkdir -p /data/es/certs/{ca,es01}服务器…

搭建MC服务器

局域网中玩MC&#xff0c;直接自己创建房间开启局域网就可以了。如果想开一个24小时不关机的服务器呢&#xff1f;其实最开始我是想在windows云服务器&#xff0c;图形化界面运行一个开启局域网即可。可能是云服务器上没有显卡&#xff0c;还是其他什么原因&#xff0c;游戏打开…

数据结构-二叉搜索树(Java语言)

目录 1.概念 2.查找search 3.插入insert ​编辑4.删除remove&#xff08;难点&#xff09; 5.性能分析 1.概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树 : 1.若它的左子树不为空&#xff0c;则左子树上所有节点的值都…

时代变迁对传统机器人等方向课程的巨大撕裂

2020年之后&#xff0c;全面转型新质课程规划&#xff0c;传统课程规划全部转为经验。 农耕-代表性生产关系-封建分配制度主要生产力-人力工业-代表性生产关系-资本分配制度工业分为机械时代&#xff0c;电气时代&#xff0c;信息时代&#xff1b;主要生产力-人力转为人脑&…

【Pikachu】PHP反序列化RCE实战

痛是你活着的证明 1.PHP反序列化概述 在理解 PHP 中 serialize() 和 unserialize() 这两个函数的工作原理之前&#xff0c;我们需要先了解它们各自的功能及其潜在的安全隐患。接下来&#xff0c;我会对相关概念做更详细的扩展解释。 1. 序列化 serialize() 序列化&#xff…

Stable Diffusion概要讲解

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

免费开源!DBdoctor推出开源版系统诊断工具systool

​前言 在开发和运维过程中&#xff0c;经常会遇到难以定位的应用问题&#xff0c;我们通常需要借助Linux系统资源监控工具来辅助诊断。然而&#xff0c;系统的IO、网络、CPU使用率以及文件句柄等信息通常需要通过多个独立的命令工具来获取。在没有部署如Prometheus这样的综合…

在openi平台 基于华为顶级深度计算平台 openmind 动手实践

大家可能一直疑问&#xff0c;到底大模型在哪里有用。 本人从事的大模型有几个方向的业务。 基于生成式语言模型的海事航行警告结构化解析。 基于生成式语言模型的航空航行警告结构化解析。 基于生成式生物序列&#xff08;蛋白质、有机物、rna、dna、mrna&#xff09;的多模态…

FPGA开发流程

注&#xff1a;开发板&#xff1a;小梅哥的ACX720。本实验可直接运行在小梅哥的ACX720开发板上&#xff0c;后续的实验都可直接运行在小梅哥的ACX720上。 一、打开VIVADO并创建工程 1、双击VIVADO图标&#xff0c;打开vivado。 2、打开vivado界面打&#xff0c;点击有 Create …

【深度学习】wsl-ubuntu深度学习基本配置

配置pip镜像源 这里注意一点&#xff0c;你换了源之后就最好不要开代理了&#xff0c;要不然搞不好下载失败&#xff0c;pip和conda都是 ## 配置中科大镜像 pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple# 配置阿里源 pip config set global…

基于Cnn神经网络虫害预测

【摘 要】鉴于农业病虫害经济损失的预测具有较强的复杂性和非线性特性&#xff0c;设计了一种新型的GRNN预测模型&#xff0c;对农业病虫害经济损失进行预测。该模型基于人工神经网络捕捉非线性变化独特的优越性&#xff0c;在神经网络技术和江苏省气象局提供的数据的基础上&am…

【AI图像生成网站Golang】项目介绍

AI图像生成网站 目录 一、项目介绍 二、雪花算法 三、JWT认证与令牌桶算法 四、项目架构 五、图床上传与图像生成API搭建 六、项目测试与调试(等待更新) 简介 本教程将手把手教你如何从零开始构建一个简单的AI图像生成网站。网站主要包含用户注册、图像生成、分类管理等…

单片机学习笔记 4. 蜂鸣器滴~滴~滴~

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯 目录 0、实现的功能 1、Keil工程 1-1 蜂鸣器工作原理 1-2 三极管工作原理 1-3 蜂鸣器原理图 2、代码实现 0、实现的功能 使蜂鸣器滴~滴~滴~ 1…