深入理解指针(3)

目录

一.字符指针变量

初始化字符指针变量的两种方式

二.数组指针变量 

 2.1 数组指针的定义

 2.2 如何使用数组指针

三.二维数组传参的本质 

3.1 二维数组的内存布局 

3.2 数组名作为指针 

3.3 二维数组作为参数传递 

3.4 二维数组传参的例子 

四.函数指针变量 

4.1 函数指针的定义

 4.2 函数指针变量的声明

4.3 函数指针变量的使用 

4.4 typedef 关键字 

基本类型别名

结构体别名 

指针别名

函数指针别名 

五.函数指针数组 

5.1 函数指针数组的定义 

5.2 函数指针数组的声明 

5.3 函数指针数组的使用 

六.转移表

具体例子:计算器程序

步骤 1:定义数学运算函数

步骤 2:定义函数指针数组(转移表)

步骤 3:主函数和菜单 

后记 


一.字符指针变量

在C语言中,字符串通常通过字符数组来存储,但实际上,我们经常使用字符指针来操作字符串,因为字符指针提供了更多的灵活性和便利性。字符指针变量指向的是字符串的首个字符的地址。

初始化字符指针变量的两种方式

指向字符串常量: 

 

这里,ptr 指向了一个由 "Hello, World!" 组成的字符串常量,存储在只读内存区域。注意,字符串常量本身在内存中是自动以 \0(空字符)结尾的。

 指向字符数组

这里,arr 是一个字符数组,它存储了 "Hello, World!" 字符串,并且自动包含了结尾的空字符 \0ptr 被初始化为指向 arr 的首地址,即字符串的第一个字符 'H' 的地址。 

下面我们来研究这样一段代码,以帮助我们更好的理解字符指针变量 

 

 

下面我们来分析这段代码,理解出现这样的结果的原因

str1 和 str2 的比较

str1 和 str2 是两个独立的字符数组,它们各自在内存中占据不同的空间来存储字符串 "hello bit."

当在 if 语句中使用 str1 == str2 进行比较时,实际上比较的是这两个数组首元素的地址。由于 str1 和 str2 是两个独立的数组,它们的首元素地址自然不同。

因此,输出将是 "str1 and str2 are not same\n"。 

str3 和 str4 的比较

str3 和 str4 是两个指向常量字符串 "hello bit." 的指针。在 C 语言中,相同的字符串常量可能会被编译器优化为在内存中只存储一份副本,然后让多个指向该字符串的指针指向同一地址。 

如果编译器进行了这种优化,str3 和 str4 将指向相同的地址,比较结果将为真。

所以我们可以得到结论:这里str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域, 当几个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。从而str3和str4相同。

二.数组指针变量 

 2.1 数组指针的定义

数组指针是指向整个数组的指针,而不是指向数组中的单个元素。其定义方式稍有不同,需要在指针类型中明确指定数组的大小(在C99及以后的版本中,对于变长数组(VLA),这个大小可以是变量)。但在大多数情况下,特别是当使用固定大小的数组时,我们并不需要在指针类型中指定数组的大小,因为编译器通常通过上下文来确定这一点。

不过,为了理解数组指针的概念,我们可以这样声明一个指向整型数组的指针(假设数组大小为5):

 

这里,ptr是一个指针,它指向一个包含5个整数的数组。注意括号的使用非常重要,它们确保了*ptr首先被计算为一个指向数组的指针,而不是ptr被当作一个指针数组。 

 2.2 如何使用数组指针

一旦你有了指向数组的指针,你就可以通过它来访问数组中的元素了。但是,由于数组指针是指向整个数组的,你不能像使用普通指针那样通过解引用(即*ptr)来直接访问数组的第一个元素;相反,你需要通过指针和索引的组合来访问元素。

假设arr是一个包含5个整数的数组,并且我们有一个指向该数组的指针ptr,如下所示:

 

                           

 要访问arr中的元素,你可以这样做:

或者,由于ptr已经是指向数组的指针,你可以省略最外层的解引用操作符,直接通过索引访问: 

注意,这里ptr[0]实际上等同于*ptr,它们都指向arr这个数组。而[1]则是访问该数组的第二个元素。 

三.二维数组传参的本质 

3.1 二维数组的内存布局 

 首先,理解二维数组在内存中的布局是基础。一个二维数组int arr[M][N];在内存中连续存储,可以视为一个包含M个元素的一维数组,其中每个元素又是一个包含N个整数的数组。这意呀着,如果我们有一个int arr[3][4];,那么内存中的布局将是连续的12个整数,但我们可以通过两个索引来访问它们,即arr[i][j]

3.2 数组名作为指针 

在C/C++中,数组名在大多数情况下会被视为指向其首元素的指针。然而,这个规则在多维数组上有一个重要的例外。对于二维数组arr[M][N]arr本身不是一个指向int的指针,而是一个指向含有N个int的数组的指针,即int (*)[N]。 二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。

3.3 二维数组作为参数传递 

当我们将二维数组作为参数传递给函数时,我们实际上传递的是指向这个二维数组首元素的指针。但由于二维数组的首元素是一个一维数组,因此传递给函数的实际上是指向这个一维数组的指针的指针(或指向数组的指针)。但是,在函数声明和定义时,我们通常不直接使用指针的指针,而是采用数组语法来简化表示。

例如,下面是一个接受二维数组作为参数的函数声明:

 

注意这里N必须是已知的,因为编译器需要知道每个内层数组的大小来正确计算内存地址。而M(外层数组的大小)则作为参数传递,因为编译器不需要在编译时就知道这个值来计算内存地址。 

3.4 二维数组传参的例子 

 

总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。 

四.函数指针变量 

4.1 函数指针的定义

函数指针变量是存储函数地址的变量。在C语言中,函数名在表达式中会被转换为指向该函数的指针。因此,你可以定义一个指针变量来存储这个地址,并通过这个指针来调用函数。

 4.2 函数指针变量的声明

 

4.3 函数指针变量的使用 

 

4.4 typedef 关键字 

typedef 关键字在 C 语言中用于为现有的数据类型定义一个新的名称(别名)。这个特性在简化复杂类型声明、提高代码可读性和可维护性方面非常有用。下面通过几个例子来讲解 typedef 的用法。

基本类型别名

 

在这个例子中,typedef 用于为 int 类型定义了一个新的名称 Integer。之后,在代码中就可以使用 Integer 来代替 int 声明整型变量了。 

结构体别名 

 

在这个例子中,typedef 与 struct 一起使用,为包含两个整型成员 x 和 y 的结构体定义了一个别名 Point。这使得在声明结构体变量时不必每次都写 struct 关键字。 

指针别名

 

在这个例子中,typedef 为指向 int 类型的指针定义了一个别名 IntPtr。使用 IntPtr 可以使指针的声明更加简洁。 

函数指针别名 

 

在这个例子中,typedef 为函数指针定义了一个别名 FuncPtr,该函数指针指向的函数接受两个 int 类型的参数并返回一个 int 类型的值。这使得在声明和使用函数指针时更加直观和方便。 

五.函数指针数组 

5.1 函数指针数组的定义 

在C语言中,函数指针数组是一个数组,其元素是指向函数的指针。 

简单地说,把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组 

5.2 函数指针数组的声明 

首先,我们需要理解如何声明一个指向特定函数的指针。假设我们有一个函数原型如下: 

 

那么,指向这个函数的指针可以这样声明: 

 

接下来,我们可以声明一个数组,其元素是这种类型的指针: 

 

注意这里的括号,它们对于正确地解析声明至关重要。上面的声明意味着 funcPtrArray 是一个数组,其元素是指向函数的指针,这些函数接受两个 int 类型的参数并返回一个 int 类型的值。 

5.3 函数指针数组的使用 

为了更具体地说明这一点,我们来看一个例子,其中我们定义了几个简单的计算函数,并将它们的地址存储在函数指针数组中,然后遍历这个数组来调用这些函数。 

 

在这个例子中,我们定义了三个简单的函数 addsubtract 和 multiply,它们分别执行加法、减法和乘法操作。然后,我们声明了一个函数指针数组 funcPtrArray,并使用花括号列表将其初始化为这三个函数的地址。

在 main 函数中,我们通过一个 for 循环遍历 funcPtrArray 数组,并使用数组索引来调用每个函数。注意,由于数组中的元素是函数指针,因此我们可以像调用函数一样使用 funcPtrArray[i](5, 3) 来调用它们,其中 i 是数组索引,5 和 3 是传递给函数的参数。

六.转移表

函数指针数组的用途之一是作为转移表(也称为分派表或跳转表),它允许程序根据输入或条件动态地选择并执行相应的函数。这种机制在处理多个条件分支时特别有用,因为它可以提高代码的可读性、可维护性和性能。下面将详细讲解函数指针数组作为转移表的用途,并通过一个具体的例子来说明。 

具体例子:计算器程序

假设我们有一个简单的计算器程序,它支持加法、减法、乘法和除法运算。我们可以使用函数指针数组来实现一个转移表,根据用户输入的操作符来调用相应的数学运算函数。

步骤 1:定义数学运算函数

首先,我们定义四个数学运算函数:addsubtractmultiplydivide

 

步骤 2:定义函数指针数组(转移表)

然后,我们定义一个函数指针数组,将上述四个函数的地址存储在其中。数组的索引可以与用户输入的操作符相对应。

 

步骤 3:主函数和菜单 

 

在这个例子中,函数指针数组operationFunc作为转移表,根据用户输入的操作符索引来调用相应的数学运算函数。这种方式使得代码更加清晰、易于扩展和维护。 

后记 

兄弟们点点赞,点点关注咯,谢谢各位大佬们!

共勉!!! 

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值