嵌入式开发学习日记——认识指针及和数组函数的联系(c语言)

一、指针的定义

一般格式

数据类型 * 指针变量名 [=初始地址值];

        数据类型是指针所指向的地址处的数据类型,如 int、char、float 等。

        符号 * 用于通知系统,这里定义的是一个指针变量,通常跟在类型关键字的后面,表示指针指向的是什么类型的值。比如,char  * 表示一个指向字符的指针,float * 表示一个指向浮点数的指针。

取址运算符和取值运算符

        取址运算符,使用 & 符号表示,作用是取出变量的内存地址。如果要格式化输出地址,需使用格式占位符 %p。

        取值运算符,使用 * 符号表示,作用是取出指针指向的内存地址处的数据值,也称为解引用运算符或间接引用运算符。

案例

通过指针修改所指向的值

#include <stdio.h>
int main()
{
  // 通过指针修改所指向位置的值
  double num = 2.88;
  // 创建指针变量p1指向num变量的地址
  double *p1 = &num;
  // 创建指针变量p2指向指针变量p1(p1中存储的是num变量的地址,所以p2中存储的也是num变量的地址)
  double *p2 = p1;
  printf("%.2f \n", num);
  *p1 = 3.88;
  printf("%.2f \n", num);
  *p2 += 10;
  printf("%.2f \n", num);
  return 0;
}

二、指针运算

1.指针加减整数

        指针与整数的加减运算,表示指针所指向的内存地址的移动(加,向后移动;减,向前移动),指针移动多少,与指针指向的数据类型有关,数据类型占据多少个字节,每单位就移动多少个字节,比如一个 int 类型指针,+1 向后移动 4 个字节,-2 向前移动 8 个字节。

        数组的元素在内存中连续存储的,我们通过数组元素来演示指针加减整数的情况。

代码如下:

#include <stdio.h>
int main()
{
  int nums[] = {10, 20, 30, 40, 50};
  int *ptr = &nums[0];
  // 打印指针变量ptr的值,和指针变量ptr指向的值
  printf("ptr = %p,*ptr = %d \n", ptr, *ptr);
  // ptr指针加3,指针变量ptr指向int类型,占4个字节,此时指针向后移动12个字节
  ptr += 3;
  printf("ptr = %p, *ptr = %d \n", ptr, *ptr);
  // ptr指针减2,指针变量ptr指向int类型,占4个字节,此时指针指向向前移动8个字节
  ptr -= 2;
  // 此时指针会向前移动8个字节
  printf("ptr = %p, *ptr = %d \n", ptr, *ptr);
  return 0;
}

2.指针自增自减

        指针自增、自减本质上就是指针加减整数,自增地址后移,自减地址前移。下面我们利用指针的自增自减实现数组的遍历,代码如下:

#include <stdio.h>
int main()
{
  short nums[] = {10, 20, 30, 40, 50};
  // 计算元素个数(数组长度)
  const int len = sizeof nums / sizeof nums[0];
  short *ptr = &nums[0]; // 定义short*类型的指针变量ptr指向nums数组中第一个元素的地址
  for (int i = 0; i < len; i++)
  {
    printf("元素索引:%d,元素地址:%p,元素值:%d \n", i, ptr, *ptr);
    ptr++;
  }
  printf("=============\n");
  // 利用指针自减再次遍历数组
  // 此时指针超出数组界限,需先自减一次
  for (int i = len - 1; i >= 0; i--)
  {
    ptr--;
    printf("元素索引:%d,元素地址:%p,元素值:%d \n", i, ptr, *ptr);
  }

  return 0;
}

3.同类型指针相减

在C语言中,指针相减的结果表示的是指针之间元素的个数。

当两个指针相减时,结果是通过计算两个指针所指向的元素之间的差异得到的。这个结果是一个整数,表示了两个指针所指向的元素在内存中的相对位置差异。具体来说,如果指针p指向数组a[i],指针q指向数组a[j],那么p-q的结果就等于i-j,即两个元素之间的索引差。这个结果是一个整数,其值取决于指针所指向的数据类型的大小。

例如,如果指针是指向字符类型的,那么相减的结果就是两个字符在内存中占用的字节数之差;如果指针是指向整数类型的,那么相减的结果就是两个整数在内存中占用的字节数之差。

需要注意的是,指针相减的结果会自动转化为整形,这是因为指针算术运算的结果类型是整数。这个转化是通过将指针之间的差异除以数据类型所占的字节数来得出的。例如,如果两个指针之间相差10个字节,而数据类型是字符型(每个字符占1个字节),那么相减的结果就是10;如果数据类型是整数型(每个整数占4个字节),那么相减的结果就是2(因为10除以4得到2余2,但结果会向下取整为2)。

此外,C语言支持3种格式的指针算术运算,包括指针加上整数、指针减去整数和两个指针相减。

相同类型的指针可以进行减法运算,返回它们之间的距离,即相隔多少个数据单位。高位地址减去低位地址,返回的是正值;低位地址减去高位地址,返回的是负值。

同类型指针相减的结果是一个 ptrdiff_t 类型数据,ptrdiff_t 类型是一个带符号的整数,格式输出中对应的格式占位符是 %td,相关案例如下:

#include <stdio.h>
int main()
{
  int nums[] = {10, 20, 30, 40, 50};
  // 定义指针变量指向数组中第一个元素的地址
  int *ptr1 = &nums[0];
  // 定义指针变量指向数组中第四个元素的地址
  int *ptr2 = &nums[3];
  printf("ptr2 - ptr1 = %td \n", ptr2 - ptr1); // 3 相差3个数据单位
  printf("ptr1 - ptr2 = %td \n", ptr1 - ptr2); // -3 相差3个数据单位
  // 定义两个指针相减
  double d1 = 1.0, d2 = 2.0;
  double *p1 = &d1, *p2 = &d2;
  printf("p2 - p1 = %td \n", p2 - p1); // -1 相差一个数据单位
  printf("p1 - p2 = %td \n", p1 - p2); // 1 相差一个数据单位
  return 0;
}

4.指针的比较运算

        指针之间可以进行比较运算,如 ==、<、 <= 、 >、 >=,比较的是各自指向的内存地址的大小,返回值是 int 类型整数 1 (true)或 0  (false)。案例演示如下:

#include <stdio.h>
int main()
{
  // 比较指针
  int nums[] = {10, 20, 30, 40, 50};
  double n = 1.0;
  // 定义指针变量指向数组第一个元素的地址
  int *ptr1 = &nums[0];
  // 定义指针变量指向数组第四个元素的地址
  int *ptr2 = &nums[3];
  // 定义指针变量指向数组第一个元素的地址
  int *ptr3 = &nums[0];
  // 定义指针变量指向变量n的地址
  double *ptr4 = &n;
  // 输出指针指向的地址
  printf("ptr1 = %p \n", ptr1);
  printf("ptr2 = %p \n", ptr2);
  printf("ptr3 = %p \n", ptr3);
  printf("ptr4 = %p \n", ptr4);
  // 比较
  printf("ptr1>ptr2 = %d \n", ptr1 > ptr2);
  printf("ptr1<ptr2 = %d \n", ptr1 < ptr2);
  printf("ptr1==ptr3 = %d \n", ptr1 == ptr3);
  // 由于是不同类型的指针进行比较,所以会有一个警告
  printf("ptr4>ptr1 = %d \n", ptr4 > ptr1);
  return 0;
}

三、指针和数组

1.指针数组

指针数组(Pointer Array)是一个数组,其中的每个元素都是指针。

语法规则:

数据类型 * 指针数组名 [长度];

示例代码:

#include <stdio.h>
int main()
{
  int num1 = 10, num2 = 20, num3 = 30;
  // 定义一个整型指针数组
  int *arr[3];
  // 指针数组中的每个元素指向不同的变量地址
  arr[0] = &num1;
  arr[1] = &num2;
  arr[2] = &num3;
  for (int i = 0; i < 3; i++)
  {
    printf("%d,%p,%d \n", i, arr[i], *arr[i]);
  }
  return 0;
}

2.数组指针

数组指针(Array Pointer)是一个指针,它指向一个数组。注意,数组指针指向的是整个数组的地址而不是第一个元素的地址,虽然二者值是相同的,但在运算中会表现出不同。

语法规则:

数据类型 (*数组指针名)[长度]

示例代码:

#include <stdio.h>
// 数组指针:本身是一个指针,指向的是一个数组地址
int main()
{
  int arr[5] = {10, 20, 30, 40, 50};
  // 定义一个数组指针变量,指向数组(&nums就是整个数组地址)
  int(*ptr)[5] = &arr;
  // arr的类型是int *,ptr类型是int(*)[]
  // int *ptr2[5] = arr; // 报错,无效的初始值设定项
  // 二者值相同
  printf("%p \n", arr);
  printf("%p \n", ptr);
  // 数组指针指向的是数组的地址,而不是第一个元素的地址
  // 数组指针+1,会向后移动4*5=20个字节(整个数组的字节向后移动一倍),数组+1会向后移动4个字节
  printf("arr+1 = %p \n", arr + 1);
  printf("ptr+1 = %p \n", ptr + 1);
  for (int i = 0; i < 5; i++)
  {
    printf("%d \n", (*ptr)[i]);
  }
  return 0;
}

数组指针和数组名的区别:

  1. 指向不同:数组名指向元素首地址,数组指针指向数组的地址。
  2. 类型不同:上面案例中,arr 的类型是 int[5],;ptr 的类型是 int(*)[5]。
  3. 可变性:数组名通常是不可变的;数组指针是可变的,你可以将它指向不同的数组。
  4. 初始化:数组名不需要显式初始化,它会自动指向数组的首元素;数组指针需要显式初始化,指定它将指向的数组。
  5. 访问元素:数组名访问数组元素不需要解引用;数组指针通常需要解引用才能访问数组元素。窗体顶端

四、指针和函数

1.传递指针给参数

当函数的形参类型是指针类型时,使用该函数时,需要传递指针、地址或者数组给该形参。

        传地址或指针给函数

        传数组给函数

数组名本身就代表数组首地址,因此传数组的本质就是传地址。

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

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

相关文章

从入门到高手的99个Python案例

想掌握Python编程语言&#xff0c;从零基础的小白晋升为大神&#xff1f;没问题&#xff01;接下来我们将以轻松有趣的方式&#xff0c;逐一解锁Python学习路上的99个关键知识点。每一步都将结合实际应用场景、函数功能解析及简洁代码演示&#xff0c;带你深度领略Python的魅力…

为什么火箭回收技术如此重要?——以马斯克的星舰为例

引言 随着人类对太空探索的深入&#xff0c;火箭技术成为了人类通往星辰大海的关键工具。在这个领域&#xff0c;SpaceX 的火箭回收技术近年来取得了重要突破&#xff0c;尤其是其 “筷子夹火箭” 的设计进一步引发了广泛讨论。2024年10月13日&#xff0c;马斯克的第五次星舰试…

Flink窗口分配器WindowAssigner

前言 Flink 数据流经过 keyBy 分组后&#xff0c;下一步就是 WindowAssigner。 WindowAssigner 定义了 stream 中的元素如何被分发到各个窗口&#xff0c;元素可以被分发到一个或多个窗口中&#xff0c;Flink 内置了常用的窗口分配器&#xff0c;包括&#xff1a;tumbling wi…

而今再看unet

从最开始听到人用Unet左inpainting&#xff0c;再到自己使用Unet做图像去噪任务&#xff0c;虽然没有用Unet做过分割&#xff0c;但Unet也可以称得上是老朋友了。现在回头再看Unet&#xff0c;温故知新&#xff0c;一些魔鬼真就藏在一些细节之中。 structure 结构由forward函数…

【C++】:工厂模式

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 简单工厂模什么是简单工厂模式&#xff1f;如何实现简单工厂模式&#xff1f; 工厂方法抽象工厂模式总结简单工厂模式工厂方法抽象工厂「Abstract Factory」 简单工厂模 什么是简单工厂模式&#xf…

【计算机网络】详解数据链路层数据帧Mac地址ARP协议

一、以太网帧 "以太网" 不是一种具体的网络&#xff0c;而是一种技术标准&#xff1b;既包含了数据链路层的内容&#xff0c;也包含了一些物理层的内容 。例如&#xff1a;规定了网络拓扑结构&#xff0c;访问控制方式&#xff0c;传输速率等&#xff1b;例如以太网中…

【智能算法应用】引力搜索算法求解二维路径规划问题

摘要 引力搜索算法&#xff08;GSA&#xff09;是一种基于引力学说的启发式算法&#xff0c;用于解决复杂的优化问题。本文应用 GSA 于二维路径规划问题&#xff0c;通过优化路径来避开障碍物并达到目标点。实验结果表明&#xff0c;GSA 在路径规划中具有良好的表现&#xff0…

课程作业管理系统的设计与实现(论文+源码)_kaic

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;课程作业管理系统当然也不能排除在外。课程作业管理系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法…

基于Docker安装Grafana及其基本功能

Grafana是一款用Go语言开发的开源数据可视化工具&#xff0c;可以做数据监控和数据统计&#xff0c;带有告警功能。 拉取Grafana镜像 docker pull grafana/grafana 运行镜像 docker run -d -p 3000:3000 --namegrafana grafana/grafana 打开浏览器&#xff0c;访问 http://l…

|动漫爬取|001_djangodjango基于Spark的国漫推荐系统的设计与实现2024_tpd6q1o4

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

to_sql报错not all arguments converted during string formatting

报错&#xff1a; DatabaseError: Execution failed on sql SELECT name FROM sqlite_master WHERE typetable AND name?;: not all arguments converted during string formattingb 报错的代码如下&#xff1a; import pymysql import pandas as pd con pymysql.connect(…

【小技能】一、windows的远程桌面连接

一、简介 windows系列都带有远程桌面连接的程序&#xff1b;学会配置并使用此程序&#xff0c;可以避免让我们每次连接一台新的电脑时去安装远程软件。所以特地前来写下这篇文章&#xff0c;让自己以后忘记了可以来瞧瞧。 二、配置被远程电脑 步骤一&#xff1a;开启远程桌…

诺贝尔物理学奖:机器学习与神经网络的时代

前言 2024年&#xff0c;诺贝尔物理学奖首次颁发给机器学习与神经网络领域的研究者&#xff0c;标志着科学评奖标准的历史性转变。这一决定引发了学术界的广泛关注&#xff0c;也促使人们深入思考科学研究及其应用的未来。 机器学习与物理学的交融 传统上&#xff0c;诺贝尔物…

前端开发设计模式——命令模式

目录 一、命令模式的定义和特点 1.定义&#xff1a; 2. 特点&#xff1a; 二、命令模式的结构与原理 1.结构&#xff1a; 2.原理&#xff1a; 三、命令模式的实现方式 1.定义接口命令&#xff1a; 2.创建具体的命令类&#xff1a; 3.定义接收者&…

【MySQL 保姆级教学】数据库的操作(3)

数据库的操作 1. 创建数据库2. 创建数据库的案例3. 字符集和校验规则3.1 查看系统默认字符集以及校验规则3.2 查看数据库支持的字符集3.3 查看数据库支持的字符集校验规则3.4 校验规则对数据库的影响 4. 操纵数据库4.1 查看数据库4.2 显示创建语句4.3 修改数据库4.4 删除数据库…

OpenCV打开摄像头或视频

文章目录 1. 视频读取流程创建视频对象设置摄像头参数循环检查是否初始化成功并读取读取视频释放摄像头删除建立的全部窗口cv2.CAP_DSHOW设置后帧率变慢的问题 2.所有python实现代码参考文献 1. 视频读取流程 创建视频对象 cap cv2.VideoCapture(videoPath) #加载视频 cap …

【风控安全产品系统架构设计的一些思考】

随着互联网的不断发展和普及&#xff0c;信息安全和风险控制成为了各行各业关注的焦点之一。在这样的背景下&#xff0c;风控安全产品成为了保障企业信息资产安全的重要手段之一。本文将探讨风控安全产品系统架构设计的一些关键思考和实践经验。 1. 深度了解业务需求 在设计…

FFmpeg的简单使用【Windows】

目录 一、视频生成图片 静态图片 转码过程 动态图片gif 二、图片生成视频 三、FFmpeg常用参数命令 3.1 主要参数 3.1.1、-i 3.1.2、-f 3.1.3、-ss 3.1.4、-t 3.2 音频参数 3.2.1、-aframes 3.2.2、 -b:a 3.2.3、-ar 3.2.4、-ac 3.2.5、-acodec 3.2.6、-an 3…

未来汽车究竟该是什么样子?

24年10月14日&#xff0c;在中国&#xff08;深圳&#xff09;机器视觉展暨机器视觉技术及工业应用研讨会上&#xff0c;同行者分享了未来智能座舱应该长什么样子。 受此启发&#xff0c;个人觉得当前大多数新能源车都极力想做出电动感&#xff0c;但是布局传统没跳出来&#…

【Golang】Go 语言中的 time 包详解:全面掌握时间处理与应用

在 Go 语言中&#xff0c;time 包提供了强大的时间处理功能&#xff0c;适用于各种场景&#xff1a;获取当前时间、格式化和解析时间、计算时间间隔、设置定时器、处理超时等。在开发过程中&#xff0c;熟练掌握 time 包能够帮助我们轻松处理时间相关的操作&#xff0c;尤其是定…