当前位置: 首页 > news >正文

【C语言系列】深入理解指针(2)

一、数组名的理解

上一篇文章中我们写过一个这样的代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

这里使用&arr[0] 的方式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,而且是数组首元素的地址,观察下面代码:

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

运行结果如下图:
在这里插入图片描述
通过运行代码我们可以知道**&arr[0] 拿到了数组第一个元素的地址和数组名拿到的地址是一样的。(即数组名就是数组首元素的地址)**
见下方代码:

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

运行结果如下图:
在这里插入图片描述
观察上述代码,不难看出如果数组名是首元素的地址那么打印出来应该是4或者8。
那么数组名到底是不是首元素的地址呢?出现这种情况又怎样理解呢?
答案是肯定的,数组名就是数组首元素的地址,但是有两种情况下例外:

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

除此之外,所有的数组名都是数组首元素的地址。
那么问题又来了,那arr和&arr有啥区别呢?

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

运行结果如图:
在这里插入图片描述
这里我们发现&arr[0]和&arr[0]+1相差4个字节, arr和arr+1相差4个字节,是因为&arr[0]和arr都是首元素的地址,+1就是跳过一个元素。但是&arr和&arr+1相差40个字节,这就是因为 &arr是整个数组的地址,+1操作是跳过整个数组的地址。

二、使用指针访问数组

#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
//打印数组内容
int*p = arr;//int*p = &arr[0];
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
//给数组输入10个值
//for(i = 0;i < sz;i++)
//{
//scanf("%d",&arr[i]);
//}
for(i = 0;i < sz;i++)
{
scanf("%d",p++);
}
//输出
for(i = 0;i < sz;i++)
{
printf("%d",*p);
p++;
//这里可以合起来写成:printf("%d",*(p + i));
}
return 0;
}

观察上述代码后,我们来捋捋可以等价替换的式子,如下图所示:
在这里插入图片描述

三、一维数组传参的本质

我们之前都是在函数外部计算数组的元素个数,现在我们把数组传给⼀个函数后,在函数内部求数组的元素个数,代码如下:

#include <stdio.h>
void test(int arr[])//这里括号中可以替换为int*arr
{
int sz2 = sizeof(arr)/sizeof(arr[0]);
printf("sz2 = %d\n",sz2);//1
}
int main()
{
int arr[10] = {0};
int sz1 =sizeof(arr)/sizeof(arr[0]);//10
printf("sz1 = %d\n",sz1);
test(arr);//本质上arr是指针
//这里的arr没有单独放在sizeof内部也没有&,所以arr是数组首元素的地址。
return 0;
}

运行结果如下图:
在这里插入图片描述
观察上述代码和运行结果可得:在函数内部是没有正确获得数组的元素个数。数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参传递的是数组首元素的地址,所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。sizeof(arr) 计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。正因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
总结:1.一维数组传参的时候,传过去的是数组首元素的地址。
2.形参的部分可以写成指针的形式,也可以写成数组的形式,但本质上都是指针,写成数组的形式是为了方便理解。

四、冒泡排序

排序算法:
1.冒泡排序
2.选择排序
3.插入排序
4.快速排序
5.堆排序
6.希尔排序

冒泡排序的思想:
1.两两相邻的元素进行比较,如果不满足顺序就交换,满足顺序就找下一对。
在这里插入图片描述
代码如下:

#include <stdio.h>
void bubble_sort(int arr[],int sz)
{
int i = 0;
//趟数
for(i = 0;i < sz-1;i++)
{
//一趟冒泡排序
int j = 0;
for(j = 0;j < sz-1-i;j++)
{
if(arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void printf_arr(int arr[],int sz)
{
int i = 0;
for(i = 0;i < sz;i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {9,8,7,6,5,4,3,2,1,0};//降序
//实现排序,排成升序
int sz = sizeof(arr) / sizeof(arr[0]);
printf_arr(arr,sz);
bubble_sort(arr,sz);
printf_arr(arr,sz);
return 0;
}

运行结果如图:
在这里插入图片描述
这个代码还可以优化一下,代码如下:

#include <stdio.h>
int count = 0;
void bubble_sort(int*arr,int sz)
{
int i = 0;//趟数
for(i = 0;i < sz;i++)
{
//一趟冒泡排序
int flag = 1;
int j = 0;
for(j = 0;j < sz-1-i;j++)
{
count++;
if(*(arr + j) > *(arr + j +1))
{
int tmp = *(arr +j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = tmp;
flag = 0;//不是有序
}
}
if(flag == 1)
{
break;
}
}
}
int main()
{int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);int i = 0;for(i=0; i<sz; i++){printf("%d ", arr[i]);}return 0;
}

运行结果如下图:
在这里插入图片描述

五、二级指针

一级指针变量也是变量,是变量就有地址,那一级指针变量的地址存放在哪⾥?存放在⼆级指针中。
二级指针的介绍如下图:
在这里插入图片描述

#include <stdio.h>
int main()
{
int a = 10;
int*p = &a;
int**pp = &p;
**pp = 20;//两次解引用才能拿到初始变量a,依次解引用只能拿到一级指针p的地址。
printf("%d\n",a);
return 0;
}

运行结果如下图:
在这里插入图片描述

六、指针数组

指针数组是指针还是数组?
指针数组是存放指针的数组即它本质上是数组。
指针数组的每一个元素都是用来存放地址(指针)的,指针数组的每一个元素是地址,又可以指向一块区域。
在这里插入图片描述

七、指针数组模拟二维数组

代码如下:

#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]);
}
printf("\n");
}
return 0;
}

运行结果如下图:
在这里插入图片描述
在这里插入图片描述
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型一维数组中的元素。
注:上述虽然模拟出二维数组,但是每一行的地址是不连续的,不算真正上的二为数组。

相关文章:

【C语言系列】深入理解指针(2)

一、数组名的理解 上一篇文章中我们写过一个这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0];这里使用&arr[0] 的方式拿到了数组第⼀个元素的地址&#xff0c;但是其实数组名本来就是地址&#xff0c;而且是数组首元素的地址&#xff…...

与 Spring Boot 的无缝集成:ShardingSphere 快速集成实践

ShardingSphere 是一个轻量级的开源分布式数据库中间件&#xff0c;它支持分库分表、分布式事务、读写分离等功能。它能够与各种应用框架进行集成&#xff0c;其中与 Spring Boot 的集成非常流行&#xff0c;因为它能够帮助开发者在 Spring Boot 项目中快速实现高性能的分布式数…...

【QT】窗口/界面置于最前端显示,且激活该窗口

目录 0.环境 1.问题描述 2.具体实现 0.环境 windows11 qt 1.问题描述 我有一个窗口QMainWindow&#xff08;也适用于QWidget或QDialog&#xff09;&#xff0c;想让其在显示的时候置于最前面&#xff0c;且激活成为当前活动窗口 2.具体实现 mainWindow->show();mainWind…...

DOL-288 多功能电子计时器说明书

新买一个计时器&#xff0c;它的用法不太直观&#xff0c;所以把说明书留在这里&#xff0c;以便以后查询。 DOL-288 多功能电子计时器说明书 1.功能说明&#xff1a; 正计时功能&#xff0c;计时上限为23小时59分59秒倒计时功能&#xff0c;计时上限为23小时59分59秒&#…...

14 常用的负载均衡算法

基于nginx的代理 1. 轮询算法 例如我们在nginx服务器中代理了3台服务器&#xff0c;再每次客户端发起请求的时候按照顺序请求挨次的发送到代理的三台服务器上。该算法比较适合每台服务器性能差不多的场景&#xff0c;如果部分服务器性能比较差&#xff0c;可能会造成性能好的…...

方法建议ChatGPT提示词分享

方法建议 ChatGPT能够根据您的具体需求提供针对性的建议&#xff0c;帮助您选择最合适的研究方法。通过清晰的提示&#xff0c;ChatGPT可以精准地为您提供最契合的研究方案。此外&#xff0c;它还能协助您将这些方法灵活地应用于新的研究环境&#xff0c;提出创新的技术解决方案…...

如何提高自动化测试覆盖率和效率

用ChatGPT做软件测试 在现代软件开发中&#xff0c;自动化测试已经成为保证软件质量的重要手段。然而&#xff0c;在实践中&#xff0c;自动化测试的覆盖率和效率常常受到限制&#xff0c;导致潜在缺陷未能及时发现或测试资源浪费。因此&#xff0c;提升自动化测试的覆盖率和效…...

Django学习笔记(安装和环境配置)-01

Django学习笔记(安装和环境配置)-01 一、创建python环境 1、可以通过安装Anaconda来创建一个python环境 # 创建一个虚拟python环境 conda create -n django python3.8 # 切换激活到创建的环境中 activate django2、安装django # 进入虚拟环境中安装django框架 pip install …...

【PHP】部署和发布PHP网站到IIS服务器

欢迎来到《小5讲堂》 这是《PHP》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言安装PHP 稳定版本线程安全版解压使用 PHP配置 配置文件扩展文件…...

渗透测试之SSRF漏洞原理 危害 产生的原因 探测手法 防御手法 绕过手法 限制的手段

目录 SSRF说明: SSRF攻击流程 原理&#xff1a; 危害: SSRF产生的原因 ssrf漏洞利用{危害} 探测手法是否存在SSRF漏洞 如何找ssrf漏洞位置 分享连接地址 google hack url关键字 PHP语言中可能出现的ssrf漏洞函数 file_get_contents sockopen() curl_exec() SSRF…...

微信小程序-base64加解密

思路&#xff1a;先创建一个base64.js的文件&#xff0c;这个文件可以作为专门加解密的文件模块&#xff0c;需要时就引用&#xff1b;创建好后&#xff0c;引用base64.js里的加解密函数。 注意&#xff1a;引用模块一定要引用正确的路径&#xff0c;否则会报错。 base64.js:…...

Linux shell 批量验证端口连通性

脚本 #!/bin/bash # #database check #set -o nounset LOCALIPifconfig | grep inet | head -1 | awk {print $2} | sed s/addr\:// IPLIST192.168.1.99 192.168.1.98 192.168.1.97 PORTLIST81 82 83 84 85 86 check_nc(){ for CHECK_IP in $IPLIST dofor CHECK_PORT in $PORT…...

2025-1-21 Newstar CTF web week1 wp

文章目录 week1headach3会赢吗智械危机 week1 headach3 根据提示&#xff0c;在页面的请求头里找到flag flag{You_Ar3_R3Ally_A_9ooD_d0ctor} 会赢吗 打开控制台&#xff0c;拿到第一部分flag 将地址栏改为提示&#xff0c;去到下一关 控制台调用函数&#xff0c;得到flag …...

【系统架构】如何设计一个秒杀系统?

目录 1. 什么是秒杀&#xff1f; 2. 秒杀系统的特点 3. 如何设计秒杀系统&#xff1f; 3.1 前端秒杀设计 3.2 后端秒杀设计 4. 如何保证不超卖&#xff1f; 4.1 库存扣减方式 4.2 服务端库存处理 5. 总结 * 知识扩展&#xff1a;什么是CDN&#xff1f; 1. 什么是秒杀…...

C++模拟实现queue

C模拟实现queue 1.queue的基本概念2.queue的基本框架3.size()成员函数4.empty()成员函数5.push()成员函数6.pop()成员函数7.front()成员函数8.back()成员函数9.完整代码 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x…...

【2025小年源码免费送】

&#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜&#xff0c; &#x1f525;码农福利等你领&#xff01; &#x1f496;山高路远坑又深&#xff0c; &#x1f4d5;大军纵横任驰奔&#xff0c; &#x1f389;谁敢横刀立马行…...

PyQt5 超详细入门级教程上篇

PyQt5 超详细入门级教程 上篇&#xff1a;1-3部分&#xff1a;PyQt5基础与常用控件 第1部分&#xff1a;初识 PyQt5 和安装 1.1 什么是 PyQt5&#xff1f; PyQt5 是 Python 的图形用户界面 (GUI) 框架&#xff0c;它基于强大的 Qt 库。Qt 是一个跨平台的 C 框架&#xff0c;用…...

qiankun+vite+vue3

基座与子应用代码示例 本示例中,基座为Vue3,子应用也是Vue3,由于qiankun不支持Vite构建的项目,这里还要引入 vite-plugin-qiankun 插件 基座(主应用) 加载qiankun依赖 npm i qiankun -S qiankun配置(src/qiankun) src/qiankun/config.ts export default {subApp…...

【数据结构】顺序队列与链式队列

顺序队列与链式队列 1.队列的基本概念1.顺序存储的队列&#xff1a;循环队列3.链式存储的队列&#xff1a;链式队列 1.队列的基本概念 队列是一种逻辑结构&#xff0c;是一种特殊的线性表 只能在固定的两端操作线性表 只要满足上述条件&#xff0c;那么这种特殊的线性表就会…...

Cursor的详细使用指南

以下是一份关于 Cursor 的详细使用指南&#xff1a; 一、安装与设置 下载与安装&#xff1a; 首先&#xff0c;访问 Cursor 的官方网站&#xff0c;根据你的操作系统&#xff08;Windows、Mac 或 Linux&#xff09;下载相应的安装程序。运行安装程序&#xff0c;按照屏幕上的提…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...