C++使用指针,动态数组,指针做参数需要注意的问题等总结

在这里插入图片描述

一. 前言

指针是什么,有些萌新一听到指针就开始畏惧了,这种畏惧并不是来自指针给你的,而是他人给你的。我相信基本所有人都会在刚学习的时候百度:C语言最难的是什么?包括我。大多数答案告诉你指针最难,实话实说,指针确实难,但是是相对于其他知识点来说。通过网络上得到的一些信息,在没有学习指针之前,你便已经开始对指针存满了畏惧,在这里我想说,只有自己经历了才有资格去评价它其实单讲指针其实也没什么,前面学习的基本变量,整形,浮点型,字符型,等等,现在多了一个指针形,你只需要知道指针类型是用来存储地址的。地址也是一种数值,所以这没有什么难的,千万不要道听途说,要自己去证明。

二. 进入正题

1. 声明指针

//这就是指针的声明方式,对应的可以有如下几种形式,但不是全部:

 int * p_1; 
 long * P_2;
 char * P_3;
 cout << sizeof(p_1) << " " << sizeof(P_2) << " " << sizeof(P_3);

大家认为上述三种指针类型各占多少字节?
答案是不论是什么类型的指针,都是占4个字节,因为地址用4个字节来存储是足够的,不要问为什么,我不知道。有时候,你只管像一个傻子一样去做事就好了。记住这句话。那有人又开始问了,既然都是4个字节,还分什么类型,只用一种类型就可以了啊。
一个最基本的答案是:地址虽然足以用4字节存储,但是数值不是,之所以有不同类型的指针,是为了让编译器知道从地址开始位置读取几个字节。
如果是char 则只读取1个字节,如果是int,就读去4个字节。现在明白了吗。
大家一定见过这样的例子:

 int num = 123;
 int * p;
 p = &num;
 //*p 等于 num,这时候萌新又有问题了 到底 int * p中,*是跟哪边一起的,是int* p,还是int *p,
 //不知道你们是否有此问题,反正当初我是有的

我现在告诉你,是前者,但是两种写法都是合法的,我只是帮你理解指针,如果代码写成这样,会不会更容易理解点?

 int num = 123;
 int * p = &num;
 p = &num;

这里有一个坑,需要注意:

 int * P_1, P_2;//P_2是不是指针呢?答案是不是的,P_2只是普通的int类型
 int *P_3, *P_4;//这里的P_4是指针类型,如果按照这种写法,又会觉得*p是一起的。

所以总结起来就是定义一个指针,int* p, int p, intp三种写法都是合理的。
但是如果连续定义多个指针,应将*和变量名挨在一起。

2. int * pa 和 int pa[ ]的区别

接着来说 int * pa 和 int pa[]的区别,这也是众多初学者的疑问,不管是数组或者指针,大多数情况下不可能存在于全局变量,讲之前再补充一点知识:

我们写的代码代码存储在包括栈区,堆区,数据区,代码区的地方,而全局变量存储在数据区,我们不做讨论
不使用指针声明的数组被称为静态数组,局部静态数组被存储在栈区
而使用指针声明的数组被称为动态数组,局部动态数组被存储在堆区
栈区(stack)由编译器自动分配释放,存放函数的参数值、局部变量的值等。
堆区(heap)用于动态内存分配。一般由程序员分配和释放,若程序员不释放,结束程序时有可能由OS回收。

int pa_1[10] = { 1,2,3,4,5,6,7,8,9,10 }; 
//静态数组 编译之前要确定数组的大小,所以有时候会浪费内存,有时候会栈溢出。
 int * pa_2; //这仅仅是一个int类型的指针,并不是动态数组,想要成为动态数组,我们需要申请内存
 pa_2 = new int;//申请一个int类型的变量,未初始化
 int * pa_3;
 pa_3 = new int(1);//申请一个int类型变量并初始化为1
 int * pa_4;
 pa_4 = new int[10];//未初始化动态数组
 int * pa_5;
 pa_5 = new int[10]();//初始化动态数组为0
 //测试:
 cout << *pa_2 << endl;
 cout << *pa_3 << endl;
 cout << *pa_4 << endl;
 cout << *pa_5 << endl;

运行结果:

在这里插入图片描述

3. 访问数组和指针

访问静态数组和动态数组的不同,说之前再补一点知识,嘿嘿:

在Windows下,栈是高向低地址扩展的数据结构,是一块连续的内存的区域。
堆是向高地址扩展的数据结构,是不连续的内存区域,你们可以自行测试一下!

 //访问静态数组:
 cout << pa_1[0] << endl;
 cout << pa_1[1] << endl;
 cout << pa_1[2] << endl;
 //下面的操作访问静态数组正确吗?
 cout << pa_1[0]++ << endl;//正确,将访问pa_1[0]后并自增为2,并不是访问pa_1[1],要注意。
 cout << pa_1[0] << endl;
 //cout << pa_1 << endl; pa_1是数组名也是地址,不正确
 //cout << pa_1++ << endl; pa_1++无法运行 
 //pa_1是数组名,是一个常量,而pa_1[0]可以认为是一个变量名
 //自增运算符的操作对象要求是可修改的左值,但是数组名是常量,不是可修改的左值,所以不可进行自增或自减。
 //然后是动态数组:
 cout << *(pa_5++) << endl;//向右移动一个单位(一个单位指指针类型大小,int为4)
 cout << (*pa_5)++ << endl;//访问pa_5[0]并加自增
 cout << *pa_5 << endl;//显示自增结果

运行结果:

在这里插入图片描述

4. 释放内存

//还有一个要注意的地方,动态数组是我们自己申请的内存,在程序运行完毕应进行内存释放
 delete pa_5;//将只调用pa_5[0]的析构函数
 delete[]pa_5;//整个数组全部调用析构函数
 //析构函数我们现在不讲,只需要知道,对于基本指针类型,上面两种释放无异,但要是类对象,应该使用第二种。
 //补充一点,数组是可以赋值给指针的
 pa_5 = pa_1;

5. 指针或者数组做函数参数

//文章最后再来说一说指针或者数组做函数参数:
int fun_1(int a[])
{
}
int fun_2(int * a)
{
}
int fun_3(int a[10])
{
}
//在声明上,除了作为函数参数的数组名总是编译器转化成指针,上面三种写法作用相同下角标没有任何意义
//其他情况下,数组名就是数组名,指针就是指针,

6. 指针做参数需要注意的问题

//指针作形参,需要注意的问题。
int * end = NULL;
void func_1(int * end)
{
 int * p = new int;
 *p = 66;
 end = p; //如果试图通过传入一级指针在函数内修改一级指针,原指针不会任何变化
}
//解决办法有两种:一是指针作为函数值返回
int * func_2(int * end)
{
 int * p = new int;
 *p = 66;
 return p;
}
//二是通过二级指针来修改一级指针
 int ** end_2 = &end;
void func_2(int ** end_2)
{
 int * p = new int;
 *p = 66;
 end_2 = &p;
}

不知道读者有没有想到一个常见的例子,用一个函数交换两个变量的值,变量作参数,这是值传递,我们知道函数内对传递过来的值作任何操作,对原值没有任何影响,于是我们引入指针,引入变量地址来解决交换,现在也一样,我们想改变一级指针,自然就需要二级指针来解决问题,所以,你明白了吗。

关于指针的应用还有函数指针,和指针函数,这两部分内容等写到函数的时候再详谈,再见咯。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


首先GDB是类unix系统下一个优秀的调试工具, 当然作为debug代码的工具, 肯定没有IDE来的直观了. 不过, 命令行自然有命令行的有点, 当你无法是使用IDE时, gdb有时会帮上很大的忙. 下面用1个例子来说明用法吧: 建立1个目录testgdb2 1. 编写c文件test.c可以见到在2
1. C语言定义1个数组的时候, 必须同时指定它的长度.例如:int a[5]={1,2,3,4,5}; //合法int b[6]; //合法int c[]; //错误 因为没有指定长度但是下面语句是正确, 它隐形定义了数组的长度, 就是赋值元素的个数int d[] ={4,5,6,7,8,9} /
C++的auto关键字在C+⬑新标准出来之前基本上处于弃用的状态,到C+⬑新标准发布之后,auto关键字被赋予了新的意义,并且功能也变得很强大,此篇文章重点介绍auto关键字的新功能、新用法,以及在C+⬔、C+⬗、C+⬠各版本中对它的使用缺陷和限制不断地修正和增强,当然auto关
const关键字是用于定义一个不该被改变的对象,它的作用是告诉编译器和其他程序员不允许修改这个对象的值或者状态。当程序员看到使用const修饰的代码时就知道不应该修改对应对象的值,而编译器则会强制实施这个约束,任何违反这个规定的代码会在编译期间报错。它可以用于任何函数或者类之后的全局或namespa
文章浏览阅读315次。之前用C语言编过链表,这几天突然想用C++编一下链表,搞了大半天才搞出来,所以就赶紧整理一下记录下来,省的万一时间长了找不到代码哈哈。一、链表代码1、Node.h文件代码#pragma onceclass Node{public: int ID; char alph; Node* next; Node(int ID,char alph); ~Node();private:..._if(current->id==id)
文章浏览阅读219次。碰到问题就要记录下来,防止遗忘吧。文章目录一、VS中的命令行参数二、内联函数和宏三、初始化和赋值一、VS中的命令行参数今天在运行代码的时候,碰都了下面的情况: // 解析命令行参数 if (pcl::console::find_argument (argc, argv, "-h") >= 0) { printUsage (argv[0]); return 0; }..._"if (pcl::console::find_argument(argc, argv, "-f") >= 0)怎么输入参数"
文章浏览阅读1.8k次,点赞11次,收藏37次。因为自己对决策树的机制非常的好奇,所以就研究了一下决策树的ID3算法,在这也做一篇笔记记录一下过程。文章目录一、什么是决策树?二、信息增益2.1信息熵2.1.1定义2.1.2演变2.2信息增益三、ID3算法实现四、小结一、什么是决策树?这个问题是我从一开始就有的疑问,什么是决策树?在看了一些资料之后,因为没有看到书上给出具体定义,所以按照我自己的理解决策树就是通过一个个“决策”而构建的一种树状结构,而且决策树的整个处理机制非常类似于我们人类在面临决策问题时的处理机制,这也可能就是其名字的由来。决_c++id3
文章浏览阅读492次。C++ 设计模式之策略模式
文章浏览阅读683次。我也算是个C++的小白,对于C++中的谓语我第一时间就想到了C#中的委托,但两者又不尽相同,所以想写一篇笔记记录一下。文章目录一、什么是谓语?二、使用谓语一、什么是谓语?谓语是一个可调用的表达式,其返回的结果可以作为条件的值,在C++中其实就是向算法传递函数。这和C#中的委托的概念其实是一样的,都是将函数作为参数进行传递。C++标准库中的谓语主要有两类:一元谓语和二元谓语,也就是有的算法只能..._谓语句 c++
文章浏览阅读225次。又看了一遍操作符的东西,感觉之前对操作符的理解还停留在很浅的认知上(仅仅会用哈哈),所以做一下笔记来加深一下印象。文章目录一、为什么会有操作符重载?二、操作符重载作用的对象一、为什么会有操作符重载?如果要回答这个问题,我们其实应该仔细想一下如果没有操作符重载会怎样呢?这其实很容易就联想到了C语言,因为他就没有操作符重载这一说。虽然C语言中没有类class这一概念,但是他有着和类及其相似的结构..._6-6 我的朋友 - c/c++ 操作符重载分数 15作者 海洋饼干叔叔单位 重庆大学实现frie
文章浏览阅读216次。因为之前碰到了很多关于C++上的问题,现在整理并记录一下。文章目录一、引用一、引用在C++中,引用就是给对象起了另一个名字,也就是“对象别名”。感觉和什么东西很相似,仔细一想不就是类型别名“typedef”吗哈哈。它其实是和原对象形成了一种绑定的一种关系,..._vc++6.0报错:returning address of local
文章浏览阅读565次。因为一直好奇预处理器的工作机制,所以就查了查书,做一下自己看完书之后的笔记。文章目录一、预处理器的作用一、预处理器的作用_c语言预处理器作用
文章浏览阅读1.8k次,点赞3次,收藏10次。最近特别查阅了一下关于C++文件的输入/输出的资料,整理了一下就写一下笔记。文章目录一、什么是流二、什么是缓冲区三、代码实现文件IO3.1 使用文件流对象读取数据3.2重定向一、什么是流当前的计算机具有很多种设备,但是无论是哪种设备都要与数据和信息进行打交道,所以这就牵扯到设备与数据之间的I/O操作。而每种设备又有着不同的特性和操作协议,由于过于复杂,所以我们一般是不会和这些通信细节打交道的..._c++ inpath
文章浏览阅读4.8k次,点赞6次,收藏29次。因为要使用到C++的动态链接库,所以就特意网上找了一下资料实现了一下。文章目录一、lib与dll文件二、创建dll文件三、dll隐式链接四、显式链接五、小结一、lib与dll文件之前我一直以为动态链接库就是指dll文件,这也是C#给我造成的一种印象,因为在C#中建立的类库文件都是dll文件,而且只要简单引用就可以了,但是C++却并不是这样的,这可能是因为C#隐藏了一些细节的缘故吧。在C++中共有两种库模式,一种是包含lib和dll两种文件,这种情况下其中的lib文件包含了函数所在的dll文件和dl_c++调用动态链接库
文章浏览阅读973次。因为遇到了一这个操作符的问题,所以记录一下出现的问题*~*。一、问题描述二、产生原因因为也是第一次出现这个问题,所以就到网上查了一些资料和书籍,现在倒也大概理解这个错误出现的原因了。有时候举个例子可能更容易理解为啥会出现这个错误,就拿一本书中的例子来说一下,如下所示:template<class T> class NamedObject { public: NamedObject(std::string& nameVal, const T objectVal) __copy_assign报错
C语言中的单向链表可以解决数组和结构体在使用时的内存连续性问题,同时还能动态地调整长度。本文介绍了单向链表的结构和基本操作,并给出了一个简单的示例代码。
文章浏览阅读2.3k次。区分'0'、"0"、0、''_0和
文章浏览阅读5.8k次,点赞4次,收藏8次。C语言函数指针详解,微剖本质_c语言指针函数
数组指针和指针数组是代码中常见的定义形式。虽然它们的语法类似,但含义完全不同。对于一维数组而言,数组名即为首元素的地址,不需要取址即可赋值给指针。而对于二维数组,数组名代表首行元素的地址,可以看作是一个指针数组,需要使用取址操作。
文章浏览阅读297次。总结刚入门的新同学C语言编程常见的低级错误