重生之我在异世界学编程之C语言:深入指针篇(上)

news2024/12/27 8:56:51

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

本文目录

  • 引言
  • 正文
    • (1)内置数据类型指针
      • (2) 自定义类型指针
        • 1.数组指针与指针数组
        • 2. 结构体指针
        • 3.联合体指针
          • (1)联合体指针的定义
          • (2)联合体指针的使用
          • (3)联合体指针的注意事项
      • (3)函数指针
        • 1.函数指针的定义
        • 2.函数指针的赋值
        • 3.函数指针的使用
        • 4.函数指针作为参数
        • 5.函数指针作为返回值
        • 6.函数指针的注意事项
      • (4)空指针(`NULL`)
          • (1)空指针的使用
          • (2)空指针与空字符
          • (3)空指针的注意事项
      • (5)二级指针
        • (1)二级指针的定义
        • (2)二级指针的内存分配
        • (3)二级指针的使用
        • (4)二级指针作为函数参数
        • (5)二级指针的注意事项
      • (6)常量指针与指向常量的指针(const 的用法)
        • 1.指向常量的指针(Pointer to a Constant)
        • 2.常量指针(Constant Pointer)
        • 3.常量指针指向常量(Constant Pointer to a Constant)
        • 4.使用场景和注意事项
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=pw3ykc3fqyp1

引言

指针作为C语言语法学习中的一块既极重要又极难懂的知识点,让初学C语言的我们常常苦不堪言。而本文就是为了让像曾经的作者一样的宝子们深刻理解指针这一章节的内容而作,那接下来就跟随作者的视角,一起把各种类型的指针理解透彻!

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

正文

指针的类型繁复,为了避免出现知识点的遗漏,这里小编采根据指向的内容进行分类,把指针分为了以下6种。让我们一一来学习吧!!


内置类型数据是我们最为常用的数据类型,所以指针的学习我们就从它开始。

(1)内置数据类型指针

顾名思义,这些指针指向内置类型的变量,如整型指针,字符指针等。

详见:

类型内涵及应用
整型指针int*指向整数的指针
浮点型指针float*double*指向浮点数的指针
字符(型)指针char*指向字符的指针,常用于字符串处理
布尔型指针bool*指向布尔值的指针

其次就是自定义类型指针,一起来看看吧!!

(2) 自定义类型指针

自定义类型数据类型有:数组,结构体,枚举,联合体。但是枚举没有对应的枚举指针变量。


这里小编先从数组与指针开始讲起。

1.数组指针与指针数组

数组指针和指针数组是C编程中两个重要的概念,它们虽然名字相似,但含义和用法却有很大的不同。我们先来看看这两个的定义:

  • 数组指针指的是指向数组的指针。它是一个指针,指向一个数组类型的数据。声明一个数组指针时,需要指定数组的元素类型和大小。例如,int (*arrayPtr)[10]表示一个指向包含10个整数的数组的指针。使用数组指针时,可以通过(*arrayPtr)[index]来访问数组中的元素。
  • 指针数组则是指存储指针的数组。它是一个数组,其中的每个元素都是指针。声明指针数组时,需要指定数组的大小和指针指向的类型。例如,int *pointerArray[10]表示一个包含10个指向整数的指针的数组。访问指针数组中的元素,可以直接使用pointerArray[index],然后通过解引用来访问指针指向的数据。

主要区别在于它们的使用场景和内存布局

数组指针通常用于函数参数,以传递多维数组,而指针数组则常用于创建动态的数据结构,如链表和树。

在内存中,数组指针指向的是连续的内存块,而指针数组中的每个指针可以指向任意位置的内存。因此,数组指针在内存访问上更为高效,而指针数组则在数据组织上更为灵活。

  • 总之,理解数组指针和指针数组的区别对于编写高效的C语言程序至关重要。掌握它们各自的特性和适用场景,可以帮助我们更好地设计和实现程序。

2. 结构体指针

定义:结构体指针允许我们通过指针来访问和操作结构体中的数据。结构体是一种复合数据类型,它允许我们将多个不同类型的数据项组合成一个单一的类型。结构体指针则是指向这种复合数据类型的指针。

在C中,结构体指针的声明方式是在结构体类型的前面加上一个星号()。例如,如果有一个名为Person的结构体,声明一个指向该结构体的指针可以写作Person *ptr;。这个指针可以用来指向一个Person类型的结构体实例。

我们以一个代码为例:

#include<stdio.h>
struct book {
	char name[20];
	char author[20];
	int prince;
};
void print(struct book* p) {
	printf("%s %s %d\n", (*p).name, (*p).author, (*p).prince);
	printf("%s %s %d\n", p->name, p->author, p->prince);    //“->”操作符可用在:结构体指针指向我们想要访问的结构体中的元素;
}

int main() {
	struct book b1 = {"C语言", "张三", 10};
	printf("%s %s %d\n", b1.name, b1.author, b1.prince);     //“.”操作符可用在:找到我们想要访问的结构体的元素。
	print(&b1);
	return 0;
}

这里的struct book* p就是一个结构体指针变量

主要优点:是它们提供了一种通过内存地址间接访问和修改结构体成员的方法。这在处理大型数据结构时尤其有用,因为它们可以减少内存复制,提高程序的效率。

使用结构体指针时,我们可以通过箭头操作符(->)来访问结构体的成员。例如,如果Person结构体有一个名为name的成员,我们可以通过ptr->name来访问或修改它。

结构体指针也常用于动态内存分配。使用new关键字可以动态创建结构体实例,并返回指向该实例的指针。例如,Person *ptr = new Person;会创建一个新的Person结构体,并使ptr指向它。当不再需要这个结构体时,应该使用delete来释放内存,避免内存泄漏。

结构体指针还可以作为函数参数,允许函数直接修改传入的结构体实例。这在设计模块化和可重用代码时非常有用,因为它允许函数与调用者共享数据,而无需复制整个结构体。

  • 总之,结构体指针提供了一种高效、灵活的方式来访问和操作结构体数据,是编写高效、模块化代码的关键。理解结构体指针的工作原理和正确使用它们,对于任何C/C++程序员来说都是一项基本技能。

3.联合体指针

联合体(Union)指针是指向联合体类型的指针。联合体是一种特殊的数据结构,允许在相同的内存位置存储不同的数据类型。这使得联合体成为一种节省空间的数据类型,因为联合体的大小等于其最大成员的大小,而不是所有成员的总和。

(1)联合体指针的定义

在C/C++中,联合体指针的声明方式是在联合体类型的前面加上一个星号()。例如,如果有一个名为Data的联合体,声明一个指向该联合体的指针可以写作Data *ptr;。这个指针可以用来指向一个Data类型的联合体实例。

(2)联合体指针的使用

使用联合体指针时,我们可以通过箭头操作符(->)来访问联合体的成员。例如,如果Data联合体有一个名为num的成员,我们可以通过ptr->num来访问或修改它。

联合体指针的使用特点:

  • 内存共享:联合体指针允许我们通过指针访问联合体成员,这些成员共享相同的内存位置。这意味着对一个成员的修改会影响其他成员。
  • 类型安全:尽管联合体可以存储不同类型的数据,但使用联合体指针时,编译器会根据联合体的声明来检查成员访问的类型安全。
  • 动态内存分配:与结构体类似,联合体指针也常用于动态内存分配。使用new关键字可以动态创建联合体实例,并返回指向该实例的指针。
  • 灵活性:联合体指针提供了一种灵活的方式来处理不同类型的数据,尤其是在需要节省空间或者需要通过同一个内存位置存储不同类型数据的场景中。
(3)联合体指针的注意事项
  • 初始化:在使用联合体指针之前,应该确保指针已经被正确初始化,指向一个有效的联合体实例。
  • 内存释放:如果使用new动态分配了联合体实例,那么在不再需要时,应该使用delete来释放内存,避免内存泄漏。
  • 成员访问:在使用联合体指针访问成员时,必须确保访问的是当前存储在联合体中的成员类型,否则可能会导致未定义行为。
  • 联合体指针提供了一种节省空间且灵活的方式来处理不同类型的数据。

(3)函数指针

函数指针是C语言中一个强大的特性,它允许将函数的地址赋给一个变量,使得可以通过这个变量来调用函数。这种机制提供了一种灵活的方式来处理函数,使得函数可以像数据一样被传递和操作。

1.函数指针的定义

函数指针的声明需要指定函数的返回类型、指针类型(即*),以及函数的参数列表。例如,如果有一个返回int类型、接受两个int参数的函数add,那么对应的函数指针声明如下:int (*funcPtr)(int, int);

这里,funcPtr是一个指向函数的指针,它可以存储add函数的地址。

2.函数指针的赋值

要将函数的地址赋给函数指针,可以直接使用函数名。

这是因为:在C语言中,函数名本身就是一个指向函数的指针,因此可以直接赋值给函数指针。

3.函数指针的使用

使用函数指针调用函数时,需要使用括号来包围函数指针和参数。

例如,int result = funcPtr(5, 3); // 调用add函数

4.函数指针作为参数

函数指针常用作其他函数的参数,这使得函数可以接收另一个函数作为输入,从而提供高度的灵活性。例如,可以定义一个接受函数指针作为参数的函数:

void applyFunction(int x, int y, int (*func)(int, int)) {
    int result = func(x, y);
    // 处理结果
}

在这个例子中,applyFunction接受两个int参数和一个函数指针参数,然后调用这个函数指针指向的函数。

5.函数指针作为返回值

函数指针也可以作为函数的返回值,这允许函数返回一个函数。这种技术可以用来实现回调函数和策略模式。

小编已经在之前的函数篇提及。

6.函数指针的注意事项
  • 类型匹配:函数指针必须指向与声明匹配的函数类型,否则会导致编译错误或运行时错误。
  • 空函数指针:函数指针可以被初始化为NULL,表示它不指向任何函数。
  • 内存管理:如果函数指针用于动态分配的函数对象,需要确保正确管理内存,避免内存泄漏。

在C语言中,空指针(Null Pointer)是一个特殊的指针值,它不指向任何有效的对象或函数。空指针的主要作用是表示“没有指向任何东西”或“没有有效的地址”。在C语言中,空指针常被用来表示一个指针变量尚未被分配具体的内存地址,或者用来表示某个指针变量不再指向任何对象。

(4)空指针(NULL

定义:在C语言中,空指针被定义为NULL,它是一个宏,在标准库<stddef.h>中定义。NULL的具体值是0,这意味着在大多数平台上,空指针和数值0是等价的。然而,NULL的使用更加明确,因为它专门用来表示空指针,而0可能在其他上下文中有其他含义。

(1)空指针的使用

空指针通常用于以下几种情况:

  • 初始化指针:在声明指针变量时,如果没有立即分配内存,可以将指针初始化为NULL,以表明该指针当前不指向任何对象。

函数返回值:当一个函数需要返回一个指针,但没有有效的对象可以返回时,可以返回NULL

  • 检查指针有效性:在使用指针之前,检查它是否为NULL是一个好习惯,这可以防止解引用空指针导致的程序崩溃。
(2)空指针与空字符

需要注意的是:空指针(NULL)和空字符('\0')是两个完全不同的概念。空指针是一个指针值,表示没有指向任何对象,而空字符是一个字符值,通常用来表示字符串的结束。

(3)空指针的注意事项
  • 空指针赋值:不要将NULL赋值给非指针类型的变量,这会导致编译错误。
  • 平台依赖性:虽然在大多数平台上NULL被定义为0,但在某些系统上,NULL可能有不同的定义。因此,最好始终使用NULL而不是直接使用0
  • 空指针与空数组:不要将空指针与空数组混淆。空数组是指一个长度为零的数组,而空指针是一个不指向任何对象的指针。

结论:

  • 空指针可以帮助程序员处理指针变量的未初始化状态和错误情况。

(5)二级指针

在C语言中,二级指针(Double Pointer)是指指向指针的指针它是一个指针变量,存储的值是另一个指针变量的地址。二级指针在处理动态内存分配、多维数组、函数参数传递等方面非常有用。理解二级指针对于深入掌握C语言的指针操作至关重要。

(1)二级指针的定义

二级指针的声明涉及到两个星号()。例如,int **ptr;声明了一个指向int类型指针的指针。这里,ptr是一个二级指针,它可以存储一个int*类型的地址。

(2)二级指针的内存分配

二级指针常用于动态分配多维数组。例如,创建一个二维数组可以通过分配一个指针数组(一级指针),然后为每个指针分配一个一维数组(二级指针)。

(3)二级指针的使用

使用二级指针时,可以通过连续的解引用来访问最终指向的数据。例如,*arr会得到一个int*类型的指针,而**arr会得到一个int类型的值。

(4)二级指针作为函数参数

二级指针也常用于函数参数,特别是需要修改指针指向的值或者需要传递多维数组时。

(5)二级指针的注意事项
  • 初始化:在使用二级指针之前,应该确保它们已经被正确初始化,指向有效的内存地址。
  • 内存释放:如果使用malloccalloc分配了内存,应该在不再需要时使用free来释放内存,避免内存泄漏。
  • 解引用:在使用二级指针时,必须确保已经正确解引用,否则可能会导致访问无效内存,引起程序崩溃。

在C语言中,常量指针和指向常量的指针是两个不同的概念,它们在声明和使用上有所区别,但都与指针和常量的关系有关。

(6)常量指针与指向常量的指针(const 的用法)

1.指向常量的指针(Pointer to a Constant)

指向常量的指针是指指针本身可以被修改,但其指向的数据(常量)不能被修改。这种指针的声明方式是在指针的声明中,将const关键字放在指针的后面,紧挨着指针的类型前面。例如:const int *ptr;

这里,ptr是一个指向int类型的常量的指针。这意味着你可以通过ptr来改变它所指向的地址,但是不能通过ptr来改变所指向地址处的值。

2.常量指针(Constant Pointer)

常量指针是指指针本身的值不能被修改,即一旦指针被初始化后,就不能指向另一个地址。这种指针的声明方式是将const关键字放在指针的声明中,紧挨着指针变量的前面。例如:int value = 10; int *const ptr = &value;

这里,ptr是一个常量指针,它被初始化为指向value的地址,之后你不能再让ptr指向另一个地址,但可以通过ptr来修改value的值。

3.常量指针指向常量(Constant Pointer to a Constant)

这种指针既不能改变指向的地址,也不能通过这个指针来改变指向地址处的值。声明时,const关键字同时放在指针类型和指针变量之间,以及指针类型和指针指向的类型之间。例如:const int *const ptr = &value;

在这个例子中,ptr是一个常量指针,指向一个int类型的常量。这意味着ptr的值(即它所指向的地址)不能被改变,同时ptr所指向的数据也不能被修改。

4.使用场景和注意事项
  • 指向常量的指针:当你需要一个指针来读取但不能修改某些数据时使用,例如,函数参数中传递的只读数据。
  • 常量指针:当你需要一个指针的地址固定不变时使用,例如,指向全局变量或静态变量的指针。
  • 常量指针指向常量:当你需要一个既不能改变指向地址,也不能通过指针改变数据的指针时使用,例如,防止函数内部修改传入的参数。
  • 常量指针与指向常量的指针提供了不同的保护级别,帮助程序员控制数据的访问和修改。

快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=pw3ykc3fqyp1

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

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

相关文章

TypeScript (一)运行环境配置,数据类型,可选类型,联合类型,type与interface,交叉类型,断言as,字面量类型,类型缩小

文章目录 一、认识TS1.1 JS 存在的问题1.2 TS的出现1.3 TS运行环境运行ts的三种方式 1.4 变量声明1.5 类型推断 二、数据类型2.1 JS数据类型(1) 数组Array(2) 对象Object(3) 其他类型 2.2 TS特有数据类型(1) any类型(2) unknown类型(3) void类型(4) never (了解)(5) tuple类型 …

【Leetcode 每日一题】3274. 检查棋盘方格颜色是否相同

问题背景 给你两个字符串 c o o r d i n a t e 1 coordinate1 coordinate1 和 c o o r d i n a t e 2 coordinate2 coordinate2&#xff0c;代表 8 8 8 \times 8 88 国际象棋棋盘上的两个方格的坐标。 以下是棋盘的参考图。 如果这两个方格颜色相同&#xff0c;返回 t …

【Dubbo03】消息队列与微服务之dubbo-admin 二进制与编译安装

实战案例&#xff1a;二进制安装 dubbo-admin 新版用Golang重构&#xff0c;提供了二进制包&#xff0c;可以直接部署 #下载二进制包 [rootubuntu2204 ~]#wget https://github.com/apache/dubbo-admin/releases/download/0.5.0/apache-dubbo-admin-0.5.0-bin-release.tar.gz …

Kylin Server V10 下 Kafka 集群部署

一、ZooKeeper 集群部署 1、主机规划 主机名 IP 地址 myid 10.8.3.35 1 10.8.3.36 2 10.8.3.37 3 2、拓扑结构 3、部署 (1) 下载Zookeeper [root@localhost ~]# cd /usr/local [root@localhost local]# wget https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-…

redis的应用----缓存

redis的应用----缓存 一、缓存的概念二、使用redis作为缓存2.1使用redis作为缓存的原因2.2缓存机制的访问步骤 三、缓存的更新策略3.1定期更新3.2实时更新3.3淘汰策略 四、缓存常见的问题4.1缓存预热(Cache preheating)4.2缓存穿透(Cache penetration)4.3缓存雪崩(Cache avalan…

用于LiDAR测量的1.58um单芯片MOPA(一)

--翻译自M. Faugeron、M. Krakowski1等人2014年的文章 1.简介 如今&#xff0c;人们对高功率半导体器件的兴趣日益浓厚&#xff0c;这些器件主要用于遥测、激光雷达系统或自由空间通信等应用。与固态激光器相比&#xff0c;半导体器件更紧凑且功耗更低&#xff0c;这在低功率供…

SpringBoot两天

SpringBoot讲义 什么是SpringBoot&#xff1f; Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xf…

vue3项目最新eslint9+prettier+husky+stylelint+vscode配置

一、eslint9和prettier通用配置 安装必装插件 ESlint9.x pnpm add eslintlatest -DESlint配置 vue 规则 , typescript解析器 pnpm add eslint-plugin-vue typescript-eslint -DESlint配置 JavaScript 规则 pnpm add eslint/js -D配置所有全局变量 globals pnpm add globa…

LSTM-CNN-BP-RF-SVM五模型咖喱融合策略混合预测模型

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 LSTM-CNN-BP-RF-SVM五模型咖喱融合策略混合预测模型 Matlab代码注释清晰。 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复LSTM-CNN-BP-RF-SVM五模型咖喱融合策略混合预测模型&#xff08;Matlab&#…

【数据集】细胞数据集:肿瘤-胎儿重编程的内皮细胞驱动肝细胞癌中的免疫抑制性巨噬细胞(Sharma等人)

引用此数据集&#xff1a; Sharma, Ankur (2020), “Onco-fetal reprogramming of endothelial cells drives immunosuppressive macrophages in Hepatocellular Carcinoma (Sharma et al)”, Mendeley Data, V1, doi: 10.17632/6wmzcskt6k.1 下载地址&#xff1a;Onco-feta…

A30 PHP+MYSQL+LW+工厂库存仓储订单销售后台管理系统的设计与实现 源代码 配置 文档

工厂库存仓储订单销售管理系统 1.摘要2.开发目的和意义3.系统功能设计4.数据库设计5.系统界面截图6.源码获取 1.摘要 互联网给人们生活带来了极大的转变&#xff0c;现代化、信息化管理系统也成为企业管理的必要手段。电子信息化服务模式&#xff0c;一切以信息化手段来处理和…

利用Docker一键发布Nginx-Tomcat-MySQL应用集群

Docker简介&#xff0c;可以看上一篇文章&#xff1a; 为什么互联网公司离不开Docker容器化&#xff0c;它到底解决了什么问题&#xff1f;-CSDN博客 Docker体系结构 docker核心就是镜像和容器&#xff1a; 镜像就是应用程序的安装文件&#xff0c;包含了所有需要的资源&…

python使用python-docx处理word

文章目录 一、python-docx简介二、基本使用1、新建与保存word2、写入Word&#xff08;1&#xff09;打开文档&#xff08;2&#xff09;添加标题&#xff08;3&#xff09;添加段落&#xff08;4&#xff09;添加文字块&#xff08;5&#xff09;添加图片&#xff08;6&#xf…

echarts的双X轴,父级居中的相关配置

前言&#xff1a;折腾了一个星期&#xff0c;在最后一天中午&#xff0c;都快要放弃了&#xff0c;后来坚持下来&#xff0c;才有下面结果。 这个效果就相当是复合表头&#xff0c;第一行是子级&#xff0c;第二行是父级。 子级是奇数个时&#xff0c;父级label居中很简单&…

CTF-PWN: WEB_and_PWN [第一届“吾杯”网络安全技能大赛 Calculator] 赛后学习(不会)

附件 calculate.html <!DOCTYPE html> <html lang"en"> <head><!-- 设置字符编码为 UTF-8&#xff0c;支持多语言字符集 --><meta charset"UTF-8"><!-- 设置响应式视图&#xff0c;确保页面在不同设备上自适应显示 --&…

STM32 PWM波形详细图解

目录 前言 一 PWM介绍 1.1 PWM简介 1.2 STM32F103 PWM介绍 1.3 时钟周期与占空比 二.引脚映像关系 2.1引脚映像与寄存器 2.2 复用功能映像 三. PWM 配置步骤 3.1相关原理图 3.2配置流程 3.2.1 步骤一二&#xff1a; 3.2.2 步骤三&#xff1a; 3.2.3 步骤四五六七&#xff1a; …

6.824/6.5840 Lab 1: MapReduce

宁静的夏天 天空中繁星点点 心里头有些思念 思念着你的脸 ——宁夏 完整代码见&#xff1a; https://github.com/SnowLegend-star/6.824 由于这个lab整体难度实在不小&#xff0c;故考虑再三还是决定留下代码仅供参考 6.824的强度早有耳闻&#xff0c;我终于也是到了挑战这座高…

东方隐侠网安瞭望台第8期

谷歌应用商店贷款应用中的 SpyLoan 恶意软件影响 800 万安卓用户 迈克菲实验室的新研究发现&#xff0c;谷歌应用商店中有十多个恶意安卓应用被下载量总计超过 800 万次&#xff0c;这些应用包含名为 SpyLoan 的恶意软件。安全研究员费尔南多・鲁伊斯上周发布的分析报告称&…

【python自动化一】pytest的基础使用

1.pytest简述 pytest‌ 是一个功能强大且灵活的Python测试框架&#xff0c;其主要是用于流程控制&#xff0c;具体用于UI还是接口自动化根据个人需要而定。且其具有丰富插件&#xff0c;使用时较为方便。咱们具体看下方的内容&#xff0c;本文按照使用场景展开&#xff0c;不完…

EasyDSS视频推拉流技术的应用与安防摄像机视频采集参数

安防摄像机的视频采集参数对于确保监控系统的有效性和图像质量至关重要。这些参数不仅影响视频的清晰度和流畅度&#xff0c;还直接影响存储和网络传输的需求。 安防摄像机图像效果的好坏&#xff0c;由DSP处理器和图像传感器sensor决定&#xff0c;如何利用好已有的硬件资源&…