C++指针的应用

C++指针 文章中我们介绍了指针的基本概念和应用简介。我们有提到指针可以使用在链表、队列和二叉树,等等。但是这些都会比较复查,后面"数据结构” 时,我们会用专门的章节来讲解这些知识。

这篇文章,详细的探讨一下指针和其他关联的具体应用。

1. 指针和数组的关系 

在C语言中,指针与数组之间的关系十分密切。实际上,许多可以用数组完成的工作都可以使用指针来完成。一般来说,用指针编写的程序比用数组编写的程序执行速度快,但另一方面,用指针实现的程序理解起来稍微困难一些。

我们先声明一个数组:

int a[10];// 声明一个int类型的数组,这个数组有10个元素

我们可以用 a[0]、a[1]、...、a[9] 来表示这个数组中的10个元素,这10个元素是存储在一段连续相邻的内存区域中的。

接下来,我们再声明一个指针:

int *p;  // 声明一个int类型的指针变量

p 是一个指针变量,指向内存中的一个区域。如果我们对指针 p 做如下的初始化:

p = &a[0];  // 对指针进行初始化,p将指向数组 a 的第 1 个元素 a[0]

我们知道,对指针进行自增操作会让指针指向与当前元素相邻的下一个元素,即 *(p + 1) 将指向 a[1] ;同样的, *(p + i) 将指向 a[i] 。因此,我们可以使用该指针来遍历数组 a[10] 的所有元素。可以看到,数组下标与指针运算之间的关系是一一对应的。而根据定义,数组类型的变量或表达式的值是该数组第 1 个元素的地址,且数组名所代表的的就是该数组第 1 个元素的地址,故,上述赋值语句可以直接写成:

p = a; // a 为数组名,代表该数组最开始的一个元素的地址

很显然,一个通过数组和下标实现的表达式可以等价地通过指针及其偏移量来实现,这就是数组和指针的互通之处。但有一点要明确的是,数组和指针并不是完全等价,指针是一个变量,而数组名不是变量,它数组中第 1 个元素的地址,数组可以看做是一个用于保存变量的容器。更直接的方法,我们可以直接看二者的地址,并不一样:

#include "stdio.h"                                                                          int main(){int x[10] = {1,2,3,4,5,6,7,8,9,0};int *p = x;printf("x的地址为:%p\n",x);printf("x[0]的地址为:%p\n",&x[0]);printf("p的地址为:%p\n",&p);      // 打印指针 p 的地址,并不是指针所指向的地方的地址p += 2;printf("*(p+2)的值为:%d\n",*p);    // 输出结果为 3,*(p+2)指向了 x[2]return 0;
}

结果如下:

可以看到, x 的值与 x[0] 的地址是一样的,也就是说数组名即为数组中第 1 个元素的地址。实际上,打印 &x 后发现,x 的地址也是这个值。而 x 的地址与指针变量 p 的地址是不一样的。故而数组和指针并不能完全等价。

2. 指针数组

指针是一个变量,而数组是用于存储变量的容器,因此,指针也可以像其他变量一样存储在数组中,也就是指针数组。指针数组是一个数组,数组中的每一个元素都是指针。声明一个指针数组的方法如下:

// 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向int类型的指针
int *p[10];

在上述声明中,由于 [] 的优先级比 * 高,故 p 先与 [] 结合,成为一个数组 p[];再由 int * 指明这是一个 int 类型的指针数组,数组中的元素都是 int 类型的指针。数组的第 i 个元素是 *p[i],而 p[i] 是一个指针。由于指针数组中存放着多个指针,操作灵活,在一些需要操作大量数据的程序中使用,可以使程序更灵活快速。

3. 数组指针

数组指针是一个指针,它指向一个数组。声明一个数组指针的方法如下:

int (*p)[10];        // 声明一个数组指针 p ,该指针指向一个数组

由于 () 的优先级最高,所以 p 是一个指针,指向一个 int 类型的一维数组,这个一维数组的长度是 10,这也是指针 p 的步长。也就是说,执行 p+1 时,p 要跨过 n 个 int 型数据的长度。数组指针与二维数组联系密切,可以用数组指针来指向一个二维数组,如下:

#include "stdio.h"int main(){int arr[2][3] = {1,2,3,4,5,6};               // 定义一个二维数组并初始化int (*p)[3];           // 定义一个数组指针,指针指向一个含有3个元素的一维数组 p = arr;            // 将二维数组的首地址赋给 p,此时 p 指向 arr[0] 或 &arr[0][0]printf("%d\n",(*p)[0]);              // 输出结果为 1p++;   // 对 p 进行算术运算,此时 p 将指向二维数组的下一行的首地址,即 &arr[1][0]printf("%d\n",(*p)[1]);                      // 输出结果为5return 0;                                                                               } 

4. 结构体和指针

1) 简单介绍一下结构体

结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下。由于结构将一组相关的变量看做一个单元而不是各自独立的实体,因此结构有助于组织复杂的数据,特别是在大型的程序中。声明一个结构的方式如下:

struct message{            // 声明一个结构 messagechar name[10];             // 成员int age;int score;  
};typedef struct message s_message;     // 类型定义符 typedef
s_message mess = {"tongye",23,83};    // 声明一个 struct message 类型的变量 mess,并对其进行初始化 /* 另一种更简便的声明方法 */
typedef struct{char name[10];int age;int score;
}message;

可以使用 “结构名.成员” 的方式来访问结构中的成员,如下:

2) 结构体指针 

结构指针是指向结构的指针,以上面的结构为例,可以这样定义一个结构指针:

s_message *p;   // 声明一个结构指针 p ,该指针指向一个 s_message 类型的结构
*p = &mess;  // 对结构指针的初始化与普通指针一样,也是使用取地址符 &

C语言中使用 “->” 操作符来访问结构指针的成员,举个例子:

#include "stdio.h"typedef struct{char name[10];int age;int score;  
}message;
int main(){message mess = {"tongye",23,83};message *p = &mess;printf("%s\n",p->name);      // 输出结果为:tongyeprintf("%d\n",p->score);         // 输出结果为:83return 0;
} 

5. 指针和函数的关系

C语言的所有参数均是以“传值调用”的方式进行传递的,这意味着函数将获得参数值的一份拷贝。这样,函数可以放心修改这个拷贝值,而不必担心会修改调用程序实际传递给它的参数。 

1) 指针作为函数的参数

传值调用的好处是是被调函数不会改变调用函数传过来的值,可以放心修改。但是有时候需要被调函数回传一个值给调用函数,这样的话,传值调用就无法做到。为了解决这个问题,可以使用传指针调用。指针参数使得被调函数能够访问和修改主调函数中对象的值。用一个例子来说明:

#include "stdio.h"
// 参数为普通的 int 变量
void swap1(int a,int b)        
{int temp;temp = a;a = b;b = temp;
}
// 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
// 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
void swap2(int *a,int *b)      
{int temp;                    temp = *a;*a = *b;*b = temp;
}
int main()
{int x = 1,y = 2;swap1(x,y);                     // 将 x,y 的值本身作为参数传递给了被调函数printf("%d %5d\n",x,y);         // 输出结果为:1     2swap(&x,&y);                     // 将 x,y 的地址作为参数传递给了被调函数printf("%d %5d\n",x,y);         // 输出结果为:2     1return 0;
}

2) 指向函数的指针

在C语言中,函数本身不是变量,但是可以定义指向函数的指针,也称作函数指针,函数指针指向函数的入口地址。这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等。声明一个函数指针的方法如下:

返回值类型 (* 指针变量名)([形参列表]);

int (*pointer)(int *,int *);        // 声明一个函数指针

上述代码声明了一个函数指针 pointer ,该指针指向一个函数,函数具有两个 int * 类型的参数,且返回值类型为 int。下面的代码演示了函数指针的用法:

#include "stdio.h"
#include "string.h"// 声明一个函数 str_comp,该函数有两个 const char 类型的指针,函数的返回值为 int 类型
int str_comp(const char *m,const char *n); // 声明一个函数 comp ,注意该函数的第三个参数,是一个函数指针                            
void comp(char *a,char *b,int (*prr)(const char *,const char*));       int main()
{char str1[20];      // 声明一个字符数组char str2[20];
// 声明并初始化一个函数指针,且返回值为 int 类型int (*p)(const char *,const char *) = str_comp;            gets(str1);         // 使用 gets() 函数从 I/O 读取一行字符串    gets(str2);comp(str1,str2,p);  // 函数指针 p 作为参数传给 comp 函数return 0;
}int str_comp(const char *m,const char *n)
{// 库函数 strcmp 用于比较两个字符串if(strcmp(m,n) == 0) return 0;elsereturn 1;
}/* 函数 comp 接受一个函数指针作为它的第三个参数 */
void comp(char *a,char *b,int (*prr)(const char *,const char*))
{if((*prr)(a,b) == 0)printf("str1 = str2\n");elseprintf("str1 != str2\n");
}

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈


嵌入式Linux

微信扫描二维码,关注我的公众号 

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

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

相关文章

Matplotlib——创建散点图

入门: 导入所用到的包 import numpy as np import matplotlib.pyplot as plt as 是对包起一个名字,便于后边程序的编写无颜色差别 figplt.figure() #建立一个画布 axfig.add_subplot(111) #在画布中建立图表,fig.add_subplot(…

vmwaretools安装

vmwaretools安装 宗旨:技术的学习是有限的,分享的精神是无限的。 vmware的作用:windows和linux之间文件拖文件很方便,但是我一般是使用samba服务器,后面介绍samba服务器。 (1)单击菜单栏上的”…

Android - Glide4.4.0使用

错误信息 java.lang.NoSuchMethodError: No static method getFont(Landroid/content/Context;ILandroid/util/TypedValue;ILandroid/widget/TextView;)Landroid/graphics/Typeface; in class Landroid/support/v4/content/res/ResourcesCompat; or its super classes (declara…

渐入“衰”境:警惕企业的六大老人病

1.组织惯性成员的思维、行为惯性容易汇聚成为阻碍组织前行的组织惯性。在企业管理的实施过程中,组织成员特别是老人有时并不按照新的架构流程和制度规范去工作。这不全是因为大家不接受新的系统,而是一工作就回到原先习惯的状态—我们把这个现象称为组织…

机器学习——支持向量机主要思想

概念:支持向量运算的分类器,在数据上应用基本形式的SVM分类器就可以得到低错误的结果,能够对训练集以外的数据点做出很好的分类决策。 名词: 支持向量:离分离超平面最近的那些点,需要找到最大化支持向量到分…

哇、、、、C++ 实现单向链表

之前相关的文章 C语言,链表 Linux内核链表 #什么是链表 链表是一种基本的数据结构,之前我在C语言里面写过链表的知识,现在延申到C,不管是什么语言,链表表示的是一种数据结构,跟语言没有强相关性的。 如果我…

vmware与windows共享文件夹

vmware与windows共享文件夹 宗旨:技术的学习是有限的,分享的精神是无限的。 虚拟工具安装好之后,我们就可以在windows和linux设置一个共享目录了,继续看图干活。 设置好共享目录以后,打开终端输入以下命令,就可以再…

设计模式之一

设计模式设计出来就是为了让后期维护代码更容易,增加代码的强壮性等好处! 策略模式 自己的理解:定义一个算法族,分别分装起来,使他们能互相替换且算法的变换与使用算法的对象相互独立。简单来说是将变化的和不变得分离,对接口编程…

asp.net如何取得纯客户端控件的值

例一&#xff1a;纯客户端控件 <input name"edisundong"type"text">在服务器端取得的方法 stringstrvalueRequest.Form.Get("edisundong");例二&#xff1a;纯客户端控件 <input type"radio"name"sex"value"…

C++ const限定符和auto类型说明符

const限定符# 1.限定常量有时我们希望定义这样的变量&#xff1a;它的值不能被改变。为了满足这一要求&#xff0c;我们使用const对变量的类型加以限定&#xff1a;const int bufSize 512;这样就把bufSize定义成了一个常量&#xff0c;它的值不能再发生变化。所以这也就意味着…

vmware虚拟机中ubuntu上网问题

虚拟机linux上网问题 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、VMware中虚拟机网络的三种设置 第一种&#xff1a;桥接&#xff08;bridged&#xff09; 第二种&#xff1a;NAT 第三种&#xff1a;Host only 。该模式下仅主机可以上网&…

Flume框架基础

* Flume框架基础 框架简介&#xff1a; ** Flume提供一个分布式的&#xff0c;可靠的&#xff0c;对大数据量的日志进行高效收集、聚集、移动的服务&#xff0c;Flume只能在Unix环境下运行。 ** Flume基于流式架构&#xff0c;容错性强&#xff0c;也很灵活简单&#xff0c;主要…

tensorflow的安装

安装好adaconda软件&#xff0c;打开 adaconda prompt anaconda search -t conda tensorflow 查看conda create -n tensorflow python3.5 配置python3.5环境选择 yes 进行安装activate tensorflow 激活tensorflowpip install tensorflow 安装 然后打开adaco…

昨晚三巨头聚餐,顺便聊了这三个问题

今天老何找我们吃饭&#xff0c;我和老何还有老墨是邻居&#xff0c;三年前我们就认识了&#xff0c;而且关系还不错&#xff0c;但是今年疫情的原因我们都没聚过&#xff0c;上周六本来说好要聚一下&#xff0c;但又因为周末带娃的原因又没聚成&#xff0c;今天我在微信群上说…

谈“80后”程序员为什么找不到工作? [转]

所谓“80后”&#xff0c;是指22~27岁之间、受过高等教育、刚刚毕业走向社会或者拥有几年工作经验年轻的一代。 不可否认&#xff0c;“80后”已成为职场上迅速成长的中竖力量&#xff0c;尤其是在国内的研发领域。每个时代都有自己的特点&#xff0c;如果用几个比较典型的正…

决策树 算法原理及代码

决策树可以使用不熟悉的数据集合&#xff0c;并从中提取出一系列的规则&#xff0c;这是机器根据数据集创建规则的过程&#xff0c;就是机器学习的过程。用一个小案例分析&#xff1a;通过No surfacing 和 flippers判断该生物是否是鱼&#xff0c;No surfacing 是离开水面是否…

Linux九阳神功

Linux九阳神功 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 一、基础命令&#xff1a; 1.用户管理类命令 &#xff08;1&#xff09;useradd libang 添加用户 —— 在home目录下生成同级的目录 &#xff08;2&#xff09;userdel -r libang 删除用…

jpa 总结

转&#xff1a;http://blog.csdn.net/linzhiqiang0316/article/details/52639265 先来介绍一下JPA中一些常用的查询操作&#xff1a; [java] view plaincopy //And --- 等价于 SQL 中的 and 关键字&#xff0c;比如 findByHeightAndSex(int height,char sex)&#xff1b; pub…

深度好文|面试官:进程和线程,我只问这19个问题

# 干了这碗鸡汤&#xff01;我急切地盼望着可以经历一场放纵的快乐&#xff0c;纵使巨大的悲哀将接踵而至&#xff0c;我也在所不惜。-- 太宰治 《人间失格》大家好&#xff0c;这里是周日凌晨4点&#xff0c;仍在笔耕不辍的程序喵大人。下面隆重推出我呕心沥血&#xff0c;耗时…

IIS7报“假”正式版随Server 2008发布

用过Vista的用户都知道&#xff0c;在该系统已经集成了IIS7.0版本&#xff0c;应该有很多用户已经用该版本建立了站点。可是你是否感觉到它的功能并没有传说中的哪么强大呢?这个问题终于在TechEd 2007的“Windows Server 2008中的IIS 7&#xff0c;挑战示来互联网”课程中&…