【C语言】数 组与指针:深度剖析与等价表达


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: C语言

文章目录

  • 💯前言
  • 💯数组与指针的基本关系
  • 💯数组与指针的互换使用
    • 数组下标与指针的等价性
  • 💯六个表达式的等价性
  • 💯指针运算的注意事项
  • 💯数组与指针的转换实例
    • 分析和解释 C 语言代码:数组与指针的运算
    • 1. 定义数组和指针
    • 2. 输入 10 个整数
    • 3. 使用不同的方式输出数组元素
      • 3.1 使用 `arr[i]` 输出数组元素
      • 3.2 使用 `*(arr + i)` 输出数组元素
      • 3.3 使用 `*(i + arr)` 输出数组元素
      • 3.4 使用 `i[p]` 输出数组元素
      • 3.5 使用 `p[i]` 输出数组元素
      • 3.6 使用 `*(p + i)` 输出数组元素
    • 4. 输出示例
    • 总结
  • 💯小结

在这里插入图片描述


在这里插入图片描述


💯前言

  • 在 C 语言中,数组和指针是两个基础但至关重要的概念。它们之间有着复杂且紧密的关系,理解数组和指针的使用方法、它们如何互相转换,能够帮助程序员写出更加高效和简洁的代码。尤其是当我们在使用数组时,如果能够熟练地运用指针的方式来访问数组元素,不仅能够提升代码的执行效率,还能够加深对 C 语言内存模型的理解。
    本篇文章将详细探讨数组和指针的关系,特别是它们的互换性以及一些常见的等价表达。文章中的分析将基于以下几个方面:数组的定义与特性,指针的作用,数组与指针的互换使用,以及一些常见的数组访问方式。通过这些内容,我们将深入了解 C 语言中数组和指针的妙用。
    C语言
    在这里插入图片描述

💯数组与指针的基本关系

  1. 数组的本质:

    在 C 语言中,数组是同类型数据的集合,它在内存中占用一块连续的空间。数组的大小是固定的,并且数组的元素是按顺序存储的。数组的首元素的地址就是数组名,数组名相当于一个指针,指向数组的第一个元素。

    数组的定义:

    int arr[5];
    

    这段代码定义了一个包含5个整数的数组。数组 arr 在内存中占用的是一块连续的空间,数组名 arr 实际上代表了数组首元素的地址。

  2. 指针与数组:

    指针是一个存储内存地址的变量。指针本身可以指向任何类型的数据,比如指向整数、字符、结构体等。当我们定义一个指针时,指针变量需要存储一个地址,并且这个地址必须指向相应类型的数据。

    指针的定义:

    int* p;
    p = arr;
    

    这里,指针 p 指向了数组 arr 的首元素。此时,指针 p 和数组 arr 有着密切的关系,它们指向的内存地址是相同的。


💯数组与指针的互换使用

在 C 语言中,数组和指针是可以互换使用的。这是由于数组名代表的是指向数组首元素的指针。事实上,数组名和指针的关系让我们可以使用指针访问数组中的元素。具体来说,数组下标运算符([])和指针运算符(*)有着紧密的联系。

数组下标与指针的等价性

通过指针可以访问数组的元素,这是 C 语言中的一个重要特性。实际上,数组下标运算符和指针运算符在本质上是等价的,下面是几种等价的表达方式:

  1. arr[i]*(arr + i)

    • arr[i] 是数组下标的常见形式,访问数组中第 i 个元素。
    • *(arr + i) 是通过指针运算的方式访问数组中第 i 个元素。这里,arr 是数组名,表示数组首元素的地址,arr + i 就是数组首元素地址偏移 i 个位置,* 用于解引用得到相应的值。
  2. *(arr + i)*(i + arr)

    • 这两个表达式是等价的,因为加法运算是交换律的。arr + ii + arr 都指向数组中第 i 个元素。通过 * 运算符解引用后,它们的结果相同。
  3. arr[i]i[arr]

    • 虽然 i[arr] 看起来不太直观,但它与 arr[i] 是完全等价的。C 语言中的数组访问本质上是通过指针加偏移量来完成的,因此 arr[i] 实际上是 *(arr + i),而 i[arr]*(i + arr),它们的运算结果是一样的。

💯六个表达式的等价性

在 C 语言中,数组和指针的关系使得某些看起来不同的写法,实际上是完全等价的。以下是六个等价的表达式:

  1. arr[i]*(arr + i)

    • arr[i] 是常见的数组下标访问方式,而 *(arr + i) 则是通过指针访问第 i 个元素。两者等价。
  2. *(arr + i)*(i + arr)

    • 由于加法的交换律,*(arr + i)*(i + arr) 也是等价的,指向的是同一元素。
  3. arr[i]i[arr]

    • arr[i]i[arr] 是等价的表达方式,虽然后者不常见,但在 C 语言中合法且结果相同。
  4. *(arr + i)p[i]

    • p[i] 是指针访问数组的常见形式,等同于 *(p + i),通过偏移量访问数组中的元素。
  5. *(arr + i)*(p + i)

    • *(arr + i)*(p + i) 之间的关系类似,都是通过指针加偏移量来访问数组元素。
  6. *(i + arr)i[arr]

    • 这两种表达式本质上相同,*(i + arr)i[arr] 指向的是相同的内存位置,因而是等价的。

💯指针运算的注意事项

尽管 i[arr] 是合法的且等价于 arr[i],但是这种写法通常不推荐使用,因为它不直观,可能让代码的阅读者产生困惑。通常我们更倾向于使用 arr[i]p[i] 这样的常见形式,这样代码更加清晰易懂。


💯数组与指针的转换实例

我们来分析一个简单的程序,演示如何使用指针访问数组。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdio.h>

int main() {
    int arr[10] = { 0 };  // 定义一个大小为 10 的整数数组
    int* p = arr;       // 定义指针 p,指向数组 arr
    int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组元素个数
    int i = 0;

    // 输入 10 个整数
    for (i = 0; i < sz; i++) {
        scanf("%d", p + i);  // 使用指针来访问数组
    }

    printf("使用arr[i]:");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);  // 使用指针访问数组并输出
    }
    printf("\n");

    printf("使用*(arr + i):");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", *(arr + i));  // 使用指针访问数组并输出
    }
    printf("\n");

    printf("使用*(i + arr):");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", *(i + arr));  // 使用指针访问数组并输出
    }
    printf("\n");

    printf("使用arr[i]:");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);  // 使用指针访问数组并输出
    }
    printf("\n");

    printf("使用i[p]:");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", i[p]);  // 使用指针访问数组并输出
    }
    printf("\n");


    printf("使用p[i]:");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", p[i]);  // 使用指针访问数组并输出
    }
    printf("\n");

    printf("使用*(p + i):");
    // 输出 10 个整数
    for (i = 0; i < sz; i++) {
        printf("%d ", *(p + i));  // 使用指针访问数组并输出
    }


    return 0;
}

在这里插入图片描述

分析和解释 C 语言代码:数组与指针的运算

这段代码展示了如何在 C 语言中使用数组和指针的不同方式来访问和输出数组的元素。通过指针运算,可以以多种形式访问和输出数组的内容。让我们逐步分析这段代码。

1. 定义数组和指针

int arr[10] = { 0 };  // 定义一个大小为 10 的整数数组
int* p = arr;         // 定义指针 p,指向数组 arr
int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组元素个数
  • arr 是一个大小为 10 的整型数组,初始化为 0
  • p 是一个指针,指向 arr 数组的首元素。
  • sz 是数组 arr 的大小,通过 sizeof(arr) / sizeof(arr[0]) 来计算数组的元素个数。这里 sizeof(arr) 获取数组总大小,sizeof(arr[0]) 获取数组单个元素的大小,因此 sz 即为数组中元素的个数。

2. 输入 10 个整数

for (i = 0; i < sz; i++) {
    scanf("%d", p + i);  // 使用指针来访问数组
}
  • 使用 scanf 输入 10 个整数,并将它们存入数组 arr 中。这里通过 p + i 来操作数组,因为 p 是指向数组首元素的指针,p + i 相当于指向数组第 i 个元素的指针,scanf 会把输入的整数存入该位置。

3. 使用不同的方式输出数组元素

接下来的代码展示了如何用不同的方式输出数组的元素:

3.1 使用 arr[i] 输出数组元素

printf("使用arr[i]:");
for (i = 0; i < sz; i++) {
    printf("%d ", arr[i]);  // 直接使用数组下标访问数组元素并输出
}
  • 通过 arr[i] 直接访问数组元素并输出。这是最常见的访问数组元素的方式。

3.2 使用 *(arr + i) 输出数组元素

printf("使用*(arr + i):");
for (i = 0; i < sz; i++) {
    printf("%d ", *(arr + i));  // 使用指针运算来访问数组元素并输出
}
  • *(arr + i) 是通过指针加偏移量来访问数组元素。arr 是数组首元素的地址,arr + i 就是数组第 i 个元素的地址,* 用来解引用,得到该地址存储的值。

3.3 使用 *(i + arr) 输出数组元素

printf("使用*(i + arr):");
for (i = 0; i < sz; i++) {
    printf("%d ", *(i + arr));  // 使用指针运算来访问数组元素并输出
}
  • *(i + arr)*(arr + i) 完全等价,它们的运算方式是一样的。这里也展示了指针偏移量的交换性。

3.4 使用 i[p] 输出数组元素

printf("使用i[p]:");
for (i = 0; i < sz; i++) {
    printf("%d ", i[p]);  // 通过指针加偏移量来访问数组元素并输出
}
  • i[p] 是合法且等价于 p[i] 的写法。它看起来不太直观,但本质上是通过指针偏移量访问数组元素。实际上,i[p] 是等同于 *(i + p),和 *(p + i) 相同。

3.5 使用 p[i] 输出数组元素

printf("使用p[i]:");
for (i = 0; i < sz; i++) {
    printf("%d ", p[i]);  // 使用指针访问数组并输出
}
  • p[i] 是通过指针访问数组元素的常见方式,等同于 *(p + i)

3.6 使用 *(p + i) 输出数组元素

printf("使用*(p + i):");
for (i = 0; i < sz; i++) {
    printf("%d ", *(p + i));  // 使用指针运算来访问数组元素并输出
}
  • *(p + i) 是通过指针偏移量访问数组元素。p 是指向数组首元素的指针,p + i 就是数组第 i 个元素的指针,* 解引用该指针并输出元素值。

4. 输出示例

假设你输入了以下数据:

1 2 3 4 5 6 7 8 9 10

输出结果将是:

使用arr[i]:1 2 3 4 5 6 7 8 9 10
使用*(arr + i):1 2 3 4 5 6 7 8 9 10
使用*(i + arr):1 2 3 4 5 6 7 8 9 10
使用arr[i]:1 2 3 4 5 6 7 8 9 10
使用i[p]:1 2 3 4 5 6 7 8 9 10
使用p[i]:1 2 3 4 5 6 7 8 9 10
使用*(p + i):1 2 3 4 5 6 7 8 9 10

总结

这段代码展示了数组和指针之间的密切关系。通过不同的指针访问方式,如 arr[i]*(arr + i)*(i + arr)i[p]p[i],我们可以看到这些方式实际上是等价的,它们都可以用来访问数组中的元素。通过指针的运算,可以非常灵活地操作数组元素。


💯小结

本文详细分析了 C 语言中的数组与指针,特别是它们之间的等价表达方式。我们发现,数组下标运算符和指针运算符在很多情况下是等价的,指针的使用不仅可以访问数组,还能使得程序更加灵活与高效。通过理解数组名、指针和下标之间的关系,程序员可以在编写代码时更加得心应手。

对于数组和指针的相关表达式,尽管它们在语法上可能有不同的形式,但它们在底层的运作原理是相同的。掌握这些等价的表达方式,将有助于你在 C 语言编程中更加灵活地运用数组与指针。

通过本篇文章的讲解,希望读者能够对数组和指针之间的关系有更深刻的理解,并能够在实践中熟练地使用这些技巧,以提高代码的效率和可读性。


在这里插入图片描述


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

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

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

相关文章

Explain 是 SQL 查询优化中非常重要的工具,它用于分析 SQL 查询的执行计划

Explain 是 SQL 查询优化中非常重要的工具&#xff0c;它用于分析 SQL 查询的执行计划https://mp.weixin.qq.com/s/QKra-Sp5JoaEPSCqfffOtA

Leetcode—487. 最大连续1的个数 II【中等】Plus

2025每日刷题&#xff08;210&#xff09; Leetcode—487. 最大连续1的个数 II 实现代码 class Solution { public:int findMaxConsecutiveOnes(vector<int>& nums) {int zeros 0;int ans 0;for(int l 0, r 0; r < nums.size(); r) {if(nums[r] 0) {zeros;…

C32.【C++ Cont】静态实现双向链表及STL库的list

目录 1.知识回顾 2.静态实现演示图 3.静态实现代码 1.初始双向链表 2.头插 3.遍历链表 4.查找某个值 4.任意位置之后插入元素 5.任意位置之前插入元素 6.删除任意位置的元素 4.STL库的list 1.知识回顾 96.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删 97.【C…

Docker的镜像

Docker的镜像 一&#xff0e;Docker镜像的概念 镜像是Docker&#xff08;镜像&#xff0c;容器&#xff0c;仓库&#xff09;三大核心概念之一。镜像本质上是一个只读文件&#xff0c;它包含了文件系统、源码、库文件、依赖、工具等运行应用程序所必须的文件。 镜像是由文件…

如何在Windows上使用Docker

引言 WSL2&#xff08;Windows Subsystem for Linux2&#xff09;是微软开发的一种技术&#xff0c;允许在 Windows 操作系统上运行 Linux 环境。它提供了一个兼容层&#xff0c;使得用户可以在 Windows 系统中直接运行 Linux 命令行工具、应用程序和开发工具&#xff0c;而无需…

赛博算命之 ”梅花易数“ 的 “JAVA“ 实现 ——从玄学到科学的探索

hello~朋友们&#xff01;好久不见&#xff01; 今天给大家带来赛博算命第三期——梅花易数的java实现 赛博算命系列文章&#xff1a; 周易六十四卦 掐指一算——小六壬 更多优质文章&#xff1a;个人主页 JAVA系列&#xff1a;JAVA 大佬们互三哦~互三必回&#xff01;&#xf…

Spring Web MVC项目的创建及使用

一、什么是Spring Web MVC&#xff1f; Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从⼀开始就包含在 Spring 框架中&#xff0c;通常被称为Spring MVC。 1.1 MVC的定义 MVC 是 Model View Controller 的缩写&#xff0c;它是软件工程中的一种软件架构…

Websocket从原理到实战

引言 WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议&#xff0c;它使得客户端和服务器之间能够进行实时、双向的通信&#xff0c;既然是通信协议一定要从发展历史到协议内容到应用场景最后到实战全方位了解 发展历史 WebSocket 最初是为了解决 HTTP 协议在实时…

IDEA+DeepSeek让Java开发起飞

1.获取DeepSeek秘钥 登录DeepSeek官网 : https://www.deepseek.com/ 进入API开放平台&#xff0c;第一次需要注册一个账号 进去之后需要创建一个API KEY&#xff0c;然后把APIkey记录保存下来 接着我们获取DeepSeek的API对话接口地址&#xff0c;点击左边的&#xff1a;接口…

深度解读 Docker Swarm

一、引言 随着业务规模的不断扩大和应用复杂度的增加,容器集群管理的需求应运而生。如何有效地管理和调度大量的容器,确保应用的高可用性、弹性伸缩和资源的合理分配,成为了亟待解决的问题。Docker Swarm 作为 Docker 官方推出的容器集群管理工具,正是在这样的背景下崭露头…

嵌入式面试题 C/C++常见面试题整理_7

一.什么函数不能声明为虚函数? 常见的不能声明为虚函数的有:普通函数(非成员函数):静态成员函数;内联成员函数;构造函数;友元函数。 1.为什么C不支持普通函数为虚函数?普通函数(非成员函数)只能被overload&#xff0c;不能被override&#xff0c;声明为虚函数也没有什么意思…

Ubuntu MKL(Intel Math Kernel Library)

Get Intel oneAPI Math Kernel Library wget https://registrationcenter-download.intel.com/akdlm/IRC_NAS/79153e0f-74d7-45af-b8c2-258941adf58a/intel-onemkl-2025.0.0.940_offline.sh sudo sh ./intel-onemkl-2025.0.0.940_offline.sh MKL库的配置和使用-CSDN博客 CMak…

如何在Vscode中接入Deepseek

一、获取Deepseek APIKEY 首先&#xff0c;登录Deepseek官网的开放平台&#xff1a;DeepSeek 选择API开放平台&#xff0c;然后登录Deepseek后台。 点击左侧菜单栏“API keys”&#xff0c;并创建API key。 需要注意的是&#xff0c;生成API key复制保存到本地&#xff0c;丢失…

go-zero学习笔记(三)

利用goctl生成rpc服务 编写proto文件 // 声明 proto 使用的语法版本 syntax "proto3";// proto 包名 package demoRpc;// golang 包名(可选) option go_package "./demo";// 如需为 .proto 文件添加注释&#xff0c;请使用 C/C 样式的 // 和 /* ... */…

将Deepseek接入pycharm 进行AI编程

目录 专栏导读1、进入Deepseek开放平台创建 API key 2、调用 API代码 3、成功4、补充说明多轮对话 总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——…

yolov8 opencv模型部署(C++版)

TensorRT系列之 Windows10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速…

Android 常用命令和工具解析之Battery Historian

Batterystats是包含在 Android 框架中的一种工具&#xff0c;用于收集设备上的电池数据。您可以使用adb bugreport命令抓取日志&#xff0c;将收集的电池数据转储到开发机器&#xff0c;并生成可使用 Battery Historian 分析的报告。Battery Historian 会将报告从 Batterystats…

leetcode刷题日记 1

https://leetcode.cn/problems/decode-ways/description/ 题目分析 分析了一下题目&#xff0c;我的第一想法&#xff1a;和之前的上楼梯问题很像 为什么这么说呢&#xff0c;感觉他们的值和他们之前元素都有千丝万缕的联系 就像上楼梯问题 就是我们的dp问题 怎么解释呢&a…

初阶数据结构:树---堆

目录 一、树的概念 二、树的构成 &#xff08;一&#xff09;、树的基本组成成分 &#xff08;二&#xff09;、树的实现方法 三、树的特殊结构------二叉树 &#xff08;一&#xff09;、二叉树的概念 &#xff08;二&#xff09;、二叉树的性质 &#xff08;三&#…

学习笔记:机器学习中的数学原理(一)

1. 集合 集合分为有限集和无限集&#xff1b; 对于有限集&#xff0c;两集合元素数相等即为等势&#xff1b; 对于无限集&#xff0c;两集合元素存在一一映射关系即为等势&#xff1b; 无限集根据是否与正整数集等势分为可数集和不可数集。 2. sigmoid函数&#xff08;也叫…