【C语言】指针(二)

news2024/11/23 21:52:16

目录

一、传值调用和传址调用

二、数组名的理解

三、通过指针访问数组

四、一维数组传参的本质

五、指针数组

六、指针数组模拟实现二维数组


一、传值调用和传址调用

指针可以用在哪里呢?我们看下面一段代码:

#include <stdio.h>

void Swap(int x, int y)
{
	int z = x;
	x = y;
	y = z;
}
int main()
{
	int a = 10;
	int b = 20;

	printf("交换前:a=%d b=%d\n", a, b);
	Swap(a, b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

运行结果如下:

我们发现,并没有完成交换,调试发现:

出现这样的原因:a和b是实参(是真实传递给函数的),x和y是形参,当实参ab传给形参xy时候,形参把实参数据临时拷贝了一份,x,y创建自己的独立空间,因为有自己独立的空间,跟a,b无关,所以修改形参不会影响实参。这种交换方式也叫传值调用。

所以说:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

措施:可以将a,b的地址传给Swap函数,Swap函数里通过地址间接的操作main函数中的a和b,达到交换的效果。也就是传址调用。

代码如下:

#include <stdio.h>

void Swap(int* pa, int* pb)//用指针变量来接收
{
	int z = *pa;
	*pa = *pb;
	*pb = z;
}
int main()
{
	int a = 10;
	int b = 20;

	printf("交换前:a=%d b=%d\n", a, b);
	Swap(&a, &b);//传地址

	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量。

  • 未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。
  • 如果函数内部要修改主调函数中的变量的值,就需要传址调用。

二、数组名的理解

数组名就是数组首元素(第一个元素)的地址。

代码如下:

但有两个例外:

  1. sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
  2. &数组名,这里的数组名表示整个数组,所以&数组名取出的是整个数组的地址

除此之外,任何地方使用数组名,数组名都表示首元素的地址。

可能会有疑问,为什么首元素地址(arr)跟整个数组地址(&arr)相同呢?

代码如下:

从值的角度来讲,地址就是一个编号,它们打印出来的地址一模一样,但是意义却不相同,操作arr和&arr带来的结果也是不一样的。

如下代码:

可以看出,&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是首元素的地址,类型是int*,+1就是跳过一个整型(元素)。
但是&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。

也可以这么理解:

arr与&arr都是指针,指针有两个要素:

  • 第一个是地址值,也就指向的位置,打印出来的就是地址值,arr与&arr的地址值是一样的。
  • 第二个是类型(所指向的数据类型),arr指向数组第一个元素,&arr指向数组arr,arr+1后的地址值会偏移一个元素的长度,&arr+1后的地址值会偏移一整个数组的长度,所以arr与&arr类型是不一样的。

三、通过指针访问数组

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	int* p = arr;//p存放的数组首元素的地址

	for (i = 0; i < sz; i++)
	{
		scanf("%d", p + i);
		//scanf("%d", arr+i);//也可以这样写
	}
	//输出
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
		//输出可以写成:
		// *(p+i) <==> arr[i] <==> *(arr+i) <==> *(i+arr) <==> i[arr]
	}
	return 0;
}

可以发现,数组的下标引用操作符([ ])效果相当于解引用操作符(*)

其实数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

四、一维数组传参的本质

之前我们都是在函数外部计算数组的元素个数,那可以把函数传给一个函数后,在函数内部求数组的元素个数吗?

我们来看一段代码:

#include <stdio.h>

void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	
	test(arr);
	return 0;
}

运行结果如下:

发现sz1计算错误,函数内部没有正确获得数组的元素个数。

原因:

数组名是数组首元素地址,传递的数组名,本质上数组传参传递的是数组首元素地址。

所以函数的形参部分理论上应该用指针来接收首元素的地址。那么在函数内部写sizeof(arr)计算的是一个地址的大小(单位字节),而不是数组的大小(单位字节)。

正因为函数的参数部分本质是指针,所以在函数内部是没办法求数组元素个数的。

代码如下:

void test(int arr[10]) //==>arr[]  参数写成数组形式,本质上还是指针
{
printf("%d\n", sizeof(arr)); //计算⼀个指针变量的⼤⼩
}

void test(int* arr) //参数写成指针形式
{
printf("%d\n", sizeof(arr));
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}

上面数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组。

形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略掉数组大小的。

总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

五、指针数组

数组是一组相同类型元素的集合。数组有整形数组,字符数组.....

指针数组:存放指针(地址)的数组。

int* arr1[6];//存放整型指针的数组
char* arr2[10];//存放字符指针的数组

如下图:

指针数组的每个元素是地址,又可以指向一块区域。

六、指针数组模拟实现二维数组

代码如下:

#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*的,可以存放在parr数组中
	int* parr[3] = { arr1, arr2, arr3 };
	
	int i = 0;
	int j = 0;

	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
			//parr[i][j]<==>*(parr[i]+j)<==>*(*(parr+i)+j)
		}
		printf("\n");
	}
	return 0;
}

parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。

上述的代码模拟出二维数组的效果,本质上其实不是二维数组。

因为二维数组在内存中是连续存放的,而这三个数组在内存可不一定连续存放。

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

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

相关文章

【Week Y9】yolo-v8网络结构的主要模块学习

文章目录 一、...\ultralytics-main\ultralytics\nn\modules\conv.py&#xff1a;&#xff08;1&#xff09;__all__说明&#xff1a;&#xff08;2&#xff09;autopad()说明&#xff1a;&#xff08;3&#xff09;conv()说明&#xff1a;&#xff08;4&#xff09;Focus()说明…

ue引擎游戏开发笔记(40)——行为树的建立:丰富ai行动

1.需求分析&#xff1a; 敌人的ai行为随着开发的不断更新&#xff0c;会不断增加&#xff0c;如果每次都进入c中编写会很不方便&#xff0c;也无法凸显ue引擎中行为树的优势作用&#xff0c;因此有必要将敌人的ai行为&#xff0c;全部转到行为树中实现。 2.操作实现&#xff1…

R实验 基础(二)

实验目的&#xff1a; 掌握向量的几种类型&#xff1a;数值向量、逻辑向量、字符向量、复数向量&#xff1b;掌握生成向量几个的函数使用和向量的下标运算&#xff1b;掌握因子的定义和相关函数的使用。 实验内容&#xff1a; R语言中&#xff0c;数值向量用得非常多。生成数…

深入解析Wireshark1:从捕获到分析,一网打尽数据包之旅

目录 1 认识 Wireshark 1.1 选择网卡界面 1.2 捕获数据包界面 1.3 常用按钮功能介绍 1.4 数据包列表信息 1.5 数据包详细信息 2 数据包案例分析 Frame: 物理层的数据帧概况 Ethernet II: 数据链路层以太网帧头部信息 Internet Protocol Version 4 (IPv4): 互联网层IP…

【Python】图形用户界面设计

1、设计并编写一个窗口程序,该窗口只有一个按钮,当用户单击时可在后台输出hello world. import tkinter as tk def on_button_click():print("hello world") # 创建主窗口 root tk.Tk() root.title("Hello World Button") # 设置窗口大小 root.geometry…

2005-2022年全国及各省绿色信贷水平测算数据(含原始数据+计算过程+计算结果)

2005-2022年全国及各省绿色信贷水平测算数据&#xff08;含原始数据计算过程计算结果&#xff09; 1、时间&#xff1a;2005-2022年 2、来源&#xff1a;工业统计年鉴、统计年鉴、其中2017年采用插值法填补 3、范围&#xff1a;31省 4、方法说明&#xff1a;选取各省六大高…

每日一题13:Pandas:方法链

一、每日一题 &#xff1b;&#xff1a;&#xff1a; 解答&#xff1a; import pandas as pddef findHeavyAnimals(animals: pd.DataFrame) -> pd.DataFrame:heavy_animals animals[animals[weight] > 100].sort_values(byweight, ascendingFalse)result heavy_anim…

代码随想录--链表--反转链表

题目 题意&#xff1a;反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 思路 如果再定义一个新的链表&#xff0c;实现链表元素的反转&#xff0c;其实这是对内存空间的浪费。 其实只需要改变链表的next指针的…

Java项目:基于ssm框架实现的家政服务网站管理系统分前后台(B/S架构+源码+数据库+毕业论文+答辩PPT)

一、项目简介 本项目是一套基于ssm框架实现的家政服务网站管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 二、技术实现 jdk版本&#xff1a;1.…

Qt+C++串口调试工具

程序示例精选 QtC串口调试工具 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《QtC串口调试工具》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推荐首选。 …

JWT生成token工具类实现

JWT简介 JWT定义 JWT全称为Json web token&#xff0c;也就是 Json 格式的 web token JWT数据结构 1.JWT由三段字符串组成&#xff0c;中间用.分隔 Project_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzE2MzcwMTM0LCJpYXQiOjE3MTU3NjUzMzQsImp0aSI6IjllO…

关于DOCKER启动后如何添加新的端口映射

前段时间在用docker部署服务的时候发现&#xff0c;容器已经启动&#xff0c;但是需要新的端口映射&#xff08;即容器在启动的时候只进行了部分的端口映射&#xff09;&#xff0c;经过查询资料后发现现在网上有2种方法&#xff0c;一中是修改json文件。另一种是将已经运行的容…

QT:QML中读取文件(QDesktopServices和QFile)

目录 一.介绍 二.QDesktopServices: 1.添加头文件 2.声明函数 3.操作 4.注册 5.qml调用 三.QFile&#xff1a; 1.添加头文件 2.声明函数 3.读取指定文件名的文件内容 4.注册 5.qml中调用 四.效果展示&#xff1a; 1.QDesktopServices&#xff1a;上方按钮点击打开…

纯血鸿蒙APP实战开发——Web获取相机拍照图片案例

介绍 本示例介绍如何在HTML页面中拉起原生相机进行拍照&#xff0c;并获取返回的图片。 效果预览图 使用说明 点击HTML页面中的选择文件按钮&#xff0c;拉起原生相机进行拍照。完成拍照后&#xff0c;将图片在HTML的img标签中显示。 实现思路 添加Web组件&#xff0c;设置…

一看就会的AOP事务

文章目录 AOPAOP简介AOP简介和作用AOP的应用场景为什么要学习AOP AOP入门案例思路分析代码实现AOP中的核心概念 AOP工作流程AOP工作流程AOP核心概念在测试类中验证代理对象 AOP切入点表达式语法格式通配符书写技巧 AOP通知类型AOP通知分类AOP通知详解 AOP案例案例-测量业务层接…

太极图形学——高级数据结构——稠密

太极是一个面向数据的编程语言 在并行计算的框架下&#xff0c;在计算上花费的时间反而是少数&#xff0c;大量的时间都花在了数据获取&#xff08;也就是访问内存&#xff09;上面&#xff0c;这一点在之前的games103课程上也有简单的提及 cpu的计算能力非常强大&#xff0c…

Qwen学习笔记3:Qwen模型调用外部API实现模型增强(openai的形式)

前言 本文记录了使用本地部署的Qwen模型&#xff0c;调用外部API实现模型的功能增强&#xff0c;非常的易用&#xff0c;大家用于开发自己的应用&#xff0c;只需要作简单的修改就可以进行使用了。 本文的代码来源视频教程&#xff1a; Qwen大模型变强了&#xff0c;通过API…

【SQL】SQL常见面试题总结(2)

目录 1、增删改操作1.1、插入记录&#xff08;一&#xff09;1.2、插入记录&#xff08;二&#xff09;1.3、插入记录&#xff08;三&#xff09;1.4、更新记录&#xff08;一&#xff09;1.5、更新记录&#xff08;二&#xff09;1.6、删除记录&#xff08;一&#xff09;1.7、…

【文末附gpt升级方案】腾讯混元文生图大模型开源:中文原生Sora同款DiT架构引领新潮流

在人工智能与计算机视觉技术迅猛发展的今天&#xff0c;腾讯再次引领行业潮流&#xff0c;宣布其旗下的混元文生图大模型全面升级并对外开源。这次开源的模型不仅具备强大的文生图能力&#xff0c;更采用了业内首个中文原生的Sora同款DiT架构&#xff0c;为中文世界的视觉生成领…

NodeJS V8引擎内存和垃圾回收器

关于max_old_space_size max_old_space_size参数用于指定V8引擎的老生代内存的最大大小。通过增加max_old_space_size参数的值&#xff0c;我们可以提供更多的内存给V8引擎&#xff0c;从而提高应用程序的性能和稳定性。 既然提到了老生代&#xff0c;就不得不提下什么是垃圾&…