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

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

很高兴在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.使用场景和注意事项
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

引言

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

快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

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

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

相关文章

deep generative model stanford lecture note3 --- latent variable

1 Introduction 自回归模型随着gpt的出现取得很大的成功&#xff0c;还是有很多工程上的问题并不是很适合使用自回归模型&#xff1a; 1&#xff09;自回归需要的算力太大&#xff0c;满足不了实时性要求&#xff1a;例如在自动驾驶的轨迹预测任务中&#xff0c;如果要用纯自回…

STM32_SD卡的SDIO通信_DMA读写

本篇&#xff0c;将使用CubeMXKeil&#xff0c;创建一个SD卡的DMA读写工程。 目录 一、简述 二、CubeMX 配置 SDIO DMA 三、Keil 编辑代码 四、实验效果 实现效果&#xff0c;如下图&#xff1a; 一、简述 上篇已简单介绍了SD、SDIO&#xff0c;本篇不再啰嗦&#xff0c;…

互联网行业常用12个数据分析指标和八大模型

本文目录 前言 一、互联网线上业务数据分析的12个指标 1. 用户数据&#xff08;4个&#xff09; (1) 存量&#xff08;DAU/MAU&#xff09; (2) 新增用户 (3) 健康程度&#xff08;留存率&#xff09; (4) 渠道来源 2. 用户行为数据&#xff08;4个&#xff09; (1) 次数/频率…

【学术投稿-2025年计算机视觉研究进展与应用国际学术会议 (ACVRA 2025)】从计算机基础到HTML开发:Web开发的第一步

会议官网&#xff1a;www.acvra.org 简介 2025年计算机视觉研究进展与应用&#xff08;ACVRA 2025&#xff09;将于2025年2月28-3月2日在中国广州召开&#xff0c;将汇聚世界各地的顶尖学者、研究人员和行业专家&#xff0c;聚焦计算机视觉领域的最新研究动态与应用成就。本次…

【Unity踩坑】Unity项目管理员权限问题(Unity is running as administrator )

问题描述&#xff1a; 使用Unity Hub打开或新建项目时会有下面的提示。 解决方法&#xff1a; 打开“本地安全策略”&#xff1a; 在Windows搜索栏中输入secpol.msc并回车&#xff0c;或者从“运行”对话框&#xff08;Win R&#xff0c;然后输入secpol.msc&#xff09;启…

开发板上Qt运行的环境变量的三条设置语句的详解

在终端中运行下面三句命令用于配置开发板上Qt运行的环境变量&#xff1a; export QT_QPA_GENERIC_PLUGINStslib:/dev/input/event1 export QT_QPA_PLATFORMlinuxfb:fb/dev/fb0 export QT_QPA_FONTDIR/usr/lib/fonts/设置成功后可以用下面的语句检查设置成功没有 echo $QT_QPA…

【PyQt】pyqt小案例实现简易文本编辑器

pyqt小案例实现简易文本编辑器 分析 实现了一个简单的文本编辑器&#xff0c;使用PyQt5框架构建。以下是代码的主要功能和特点&#xff1a; 主窗口类 (MyWindow): 继承自 QWidget 类。使用 .ui 文件加载用户界面布局。设置窗口标题、状态栏消息等。创建菜单栏及其子菜单项&…

Java 数据库连接池:HikariCP 与 Druid 的对比

Java 数据库连接池&#xff1a;HikariCP 与 Druid 的对比 数据库连接池&#xff1a;HikariCP 1. 卓越的性能表现 HikariCP 在数据库连接池领域以其卓越的性能脱颖而出。 其字节码经过精心优化&#xff0c;减少了不必要的开销&#xff0c;使得连接获取和释放的速度极快。 在…

PHP实现混合加密方式,提高加密的安全性(代码解密)

代码1&#xff1a; <?php // 需要加密的内容 $plaintext 授权服务器拒绝连接;// 1. AES加密部分 $aesKey openssl_random_pseudo_bytes(32); // 生成256位AES密钥 $iv openssl_random_pseudo_bytes(16); // 生成128位IV// AES加密&#xff08;CBC模式&#xff09…

Turing Complete-3位解码器

要求如下&#xff1a; 就是搭建一个3-8译码器 思路一&#xff1a; 使用四种判断来解决问题。 判断一&#xff1a;3个输入中是否有0个绿色。 解决办法&#xff1a;三个输入通过三输入或门再取反。 判断二&#xff1a;3个输入中是否有1个绿色&#xff0c;并确定是输入1、输入…

我主编的电子技术实验手册(24)——RL并联电路

本专栏是笔者主编教材&#xff08;图0所示&#xff09;的电子版&#xff0c;依托简易的元器件和仪表安排了30多个实验&#xff0c;主要面向经费不太充足的中高职院校。每个实验都安排了必不可少的【预习知识】&#xff0c;精心设计的【实验步骤】&#xff0c;全面丰富的【思考习…

手写MVVM框架-模板渲染1

虚拟dom创建好了&#xff0c;依赖也收集好了&#xff0c;这个时候就该去渲染dom了&#xff0c;把页面上的 { {name}} 渲染成具体的值。 渲染之前我们给原型上添加一个render方法 //代码在src/core/render.jsexport function renderMixin(MiniVue) {MiniVue.prototype.$render …

人类心智逆向工程:AGI的认知科学基础

文章目录 引言:为何需要逆向工程人类心智?一、逆向工程的定义与目标1.1 什么是逆向工程?1.2 AGI逆向工程的核心目标二、认知科学的四大支柱与AGI2.1 神经科学:大脑的硬件解剖2.2 心理学:心智的行为建模2.3 语言学:符号与意义的桥梁2.4 哲学:意识与自我模型的争议三、逆向…

【C语言篇】“三子棋”

一、游戏介绍 三子棋&#xff0c;英文名为 Tic - Tac - Toe&#xff0c;是一款简单而经典的棋类游戏。游戏在一个 33 的棋盘上进行&#xff0c;两名玩家轮流在棋盘的空位上放置自己的棋子&#xff08;通常用 * 和 # 表示&#xff09;&#xff0c;率先在横、竖或斜方向上连成三个…

尝试ai生成figma设计

当听到用ai 自动生成figma设计时&#xff0c;不免好奇这个是如何实现的。在查阅了不少资料后&#xff0c;有了一些想法。参考了&#xff1a;在figma上使用脚本自动生成色谱 这篇文章提供的主要思路是&#xff1a;可以通过脚本的方式构建figma设计。如果我们使用ai 生成figma脚本…

【玩转 Postman 接口测试与开发2_015】第12章:模拟服务器(Mock servers)在 Postman 中的创建与用法(含完整实测效果图)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十二章 模拟服务器&#xff08;Mock servers&#xff09;在 Postman 中的创建与用法1 模拟服务器的概念2 模拟服务器的创建2.1 开启侧边栏2.2 模拟服务器的两种创建方式2.3 私有模拟器的 API 秘钥的用法…

Java面试题2025-并发编程基础(多线程、锁、阻塞队列)

并发编程 一、线程的基础概念 一、基础概念 1.1 进程与线程A 什么是进程&#xff1f; 进程是指运行中的程序。 比如我们使用钉钉&#xff0c;浏览器&#xff0c;需要启动这个程序&#xff0c;操作系统会给这个程序分配一定的资源&#xff08;占用内存资源&#xff09;。 …

Android学习19 -- 手搓App

1 前言 之前工作中&#xff0c;很多时候要搞一个简单的app去验证底层功能&#xff0c;Android studio又过于重型&#xff0c;之前用gradle&#xff0c;被版本匹配和下载外网包折腾的堪称噩梦。所以搞app都只有找应用的同事帮忙。一直想知道一些简单的app怎么能手搓一下&#x…

实验六 项目二 简易信号发生器的设计与实现 (HEU)

声明&#xff1a;代码部分使用了AI工具 实验六 综合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 实验项目 要求利用硬件描述语言Verilog&#xff08;或VHDL&#xff09;、图形描述方式、IP核&#xff0c;结合数字系统设计方法&#xff0c;在Quartus开发环境下&#xff…

< 自用文儿 > 下载 MaxMind GeoIP Databases 对攻击的 IP 做 地理分析

起因 两个 VPM/VPS&#xff0c;安装了 fail2ban 去拦截密码穷举攻击。每天的记录都在增长&#xff0c;以前复制屏幕输出就行&#xff0c;一屏的内容还容易粘贴出来的。昨天已经过 500 条&#xff0c;好奇 fail2ban 是如何存储这些内容的&#xff1f;就发现它在使用 SQLite3 数…