C++基础入门教程(八):函数指针

最近事情比较多,其实并不忙,就是事情比较影响思绪,所以都没心思写文章了。
今天主要说说函数的一些基本情况吧,同时也解释一下新手最容易迷糊的——什么时候要用指针参数?

一、函数原型和函数定义

大家都知道,C++定义函数之前,还需要声明函数原型,对于习惯Java等其他高级语言的朋友来说,真心觉得这很烦人。
如下代码:

代码如下:

// 声明函数原型
void startGame(int param);
// 函数定义
void startGame(int param)
{
    // 各种逻辑
}

函数原型主要是给编译器用的,在编译的时候会通过函数原型来检查函数返回值、参数数量、参数类型等。
总而言之,方便编译器,编译器爽了,我们才能更爽。

但实际中,函数原型也方便我们快速理解某个类的功能。

这些很简单,就不多唠叨了。

二、const限定符与指针

之前也有简单介绍过const,比如 const int num = 10; 那么num就是常量,不可再次进行赋值操作了。
如果把const用在指针上呢?

代码如下:

int num = 10;
    const int *p = #
 
    // 编译会报错
    *p = 100;

如上代码,编译的时候就会报错,因为指针p指向一个const int类型,这是一个常量。
所以*p的值是一个常量,不能被修改。

再来理一理,不然等会会混乱的:
1.p是一个指针
2.p指向一个内存地址,这个地址里存放的是一个const int类型的值
3.*p代表是p指向的内存地址里存放的那个值
4.所以,*p就是一个const int类型的值
5.综上所述,*p不能再次被赋值。

这里要区分p和*p,这是两个概念,一个是指针,一个是指针指向的值。
p是可以被再次赋值的,但是*p是不能被赋值的。

三、函数的指针参数

先来看看下面的代码:

代码如下:

void notChangeNum(int num);
void changeNum(int* num);
int _tmain(int argc, _TCHAR* argv[])
{
    int num = 10;
  
    // 这个函数不会改变num的值
    notChangeNum(num);
    cout << num << endl;
    // 这个函数会改变num的值
    int* p = &num;
    changeNum(p);
    cout << num << endl;
    return 0;
}
void notChangeNum(int num)
{
    // 参数不是指针
    num = 999;
}
void changeNum(int* num)
{
    // 参数是指针
    *num = 999;
}

这里有两个函数,一个是普通参数(值传递),一个是指针参数(地址传递)。

第一个notChangeNum函数是不会改变num的值的,因为num传递给函数时,是拷贝了一份新的值,原来的num是不受影响的。

当离开notChangeNum函数后,函数的num参数会被释放。

第二个changeNum函数的参数是指针,我们都知道,指针是指向某个内存地址的,所以,函数的参数指向的内存地址就是num的内存地址。

直接修改内存地址上的值,会影响原来的num,所以,离开changeNum函数后,num的值也会被改变,最终值是999.

这就是指针参数的作用,某些情况下,我们希望函数里对参数的修改能够真正产生影响。

四、为什么要使用指针参数

为什么要用指针作为参数呢?因为指针可以直接指向内存地址,可以直接在函数里修改值,并且离开函数后仍然生效。
说是这么说,但,肯定还有人会迷糊,为什么呢?为什么要这样呢?

比如,我们的函数参数是某个类:

代码如下:

void play(Sprite* sp) {
}

Sprite和Value都是Cocos2d-x常用的,这里的参数为什么是指针?
因为值引用的参数是会拷贝一份的,这样才不会影响原本的值,拷贝一份就会有额外的开销。
一般类的开销都比较大(相对于int、float等基本类型而言),所以拷贝一份不太合适。
而且我们通过都需要在函数里改变Sprite的坐标、大小等属性,如果使用值传递的话,就无法修改了(修改的只是拷贝的那一份)。

当然,这个还要看具体项目的情况,我不唠叨了,太深入不好吹水。

五、不想拷贝,又不想值被修改,怎么办?

拷贝开销大,使用指针参数又很可能在函数被修改了值,怎么办呢?
这时候就要用const限定符了,如下代码:

代码如下:

void play(const Sprite* sp) {
}

这样在函数内部既不会修改sp指向的值,又可以避免值传递的额外开销。

六、函数内部的变量离开函数时就会被释放

我们之前说过,只要不是new出来的变量,那么,在离开作用范围后,就会被自动释放。
但是,来看看这个函数:

代码如下:

int getNum() {
    int num = 10;
    return num;
}

既然变量离开作用范围后会被释放,那么,num在离开getNum函数后,就会被释放。
这时候return num的意义何在呢?getNum函数真的能成功获取到数字10吗?
答案是肯定的。

因为return 在返回num的时候,实际上是拷贝了一份的,返回的是拷贝的值,释放的是原来的变量。
这就是return的秘密了。

但是,指针就不行了,看看下面的代码:

代码如下:

// 假设有这样一个结构体
struct People {
   int age;
};
People* getNewPeople() {
    People nPeople;
    nPeople.age = 20;
    return &nPeople;
}

这个函数返回的是一个指向People结构体类型的内存地址。
按照return的规则,返回的时候实际上是拷贝了一份,但这个时候拷贝的只是一个指针,也就是一个内存地址。
这个内存地址仍然指向函数内部的nPeople变量。

所以即使getNewPeople函数成功返回了一个指针,但这个指针指向的内存地址上的值仍然是被释放了。
也就是说,我们获取的只是一个野指针。

七、结束

好了,这篇写得有点糟糕,太多内容了,我只是抽取部分来吹吹水~

(0)

相关推荐

  • c++传递函数指针和bind的示例

    复制代码 代码如下: #include <algorithm>class TestClass{public: int Sub(int x, int y) {  return y - x; } void InitAndTest() {  PrintWithClassMemberFunction(&TestClass::Sub);  PrintWithClassPointer(this); } // call: PrintWithClassMemberFunction(&TestC

  • 详解C++中的指针、数组指针与函数指针

    C++中一个重要的特性就是指针,指针不仅具有获得地址的能力,还具有操作地址的能力.指针可以用于数组.或作为函数的参数,用来访问内存和对内存的操作,指针的使用使得C++很高效,但是指针也非常危险,使用不当会带来比较严重的问题. 1.指针 程序中所有的变量和常量都存在一个内存地址中,当然,函数也有对应的内存地址,内存地址的不同会导致程序执行时有所不同. 指针就是用来控制和存储内存地址的变量,它指向单个对象的地址,除了void之外,指针的数据类型与所指向地址的变量数据类型保持一致. 2.如何定义指针.

  • c++函数指针使用示例分享

    需求假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都可以使用该函数. 对于所有的用户来说,estimate()中一部分代码都是相同的,但该函数允许每个程序员提供自己的算法来估算时间. 为实现目标,采用的机制是,将程序员要使用的算法函数的地址传递给estimate(). 实现代码如下 复制代码 代码如下: // funpointer.cpp : 定义控制台应用程序的入口点.//#include "stdafx.h"#include &

  • C++中的函数指针与函数对象的总结

    篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针

  • C++普通函数指针与成员函数指针实例解析

    C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般函数的函数指针使用的比较多,而对指向类成员函数的函数指针则比较陌生.本文即对C++普通函数指针与成员函数指针进行实例解析. 一.普通函数指针 通常我们所说的函数指针指的是指向一般普通函数的指针.和其他指针一样,函数指针指向某种特定类型,所有被同一指针运用的函数必须具有相同的形参类型和返回类型. int (*pf)(int, int); // 声明函数指针 这里,pf指向的函数类型是int (

  • c++回调之利用函数指针示例

    c++回调之利用函数指针示例 复制代码 代码如下: #include <iostream>using namespace std; /************************************************************************//*                下层实现: CALLBACK                                        *//**********************************

  • C++获取类的成员函数的函数指针详解及实例代码

    C++获取类的成员函数的函数指针详解 用一个实际代码来说明. class A { public: staticvoid staticmember(){cout<<"static"<<endl;} //static member void nonstatic(){cout<<"nonstatic"<<endl;} //nonstatic member virtualvoid virtualmember(){cout<

  • c++函数指针和回调函数示例

    1.函数指针 函数指针是一个指针,只是这个指针它不像普通的指针指向是是一个变量,此时它指向的是一个函数,也就是它存储的是一个函数的地址,如果我们改变它的值,让它所指向的地址由指向funA转变为指向funB,那么这个函数指针的作用就改变了. 2.回调函数 什么是回调函数呢?回调函数其实就是一个通过函数指针调用的函数!假如你把A函数的指针当作参数传给B函数,然后在B函数中通过A函数传进来的这个指针调用A函数,这就是回调机制.B函数就是回调函数. 3.函数指针的使用 3.1函数指针声明 typedef

  • Bootstrap零基础入门教程(三)

    什么是 Bootstrap? Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的. 历史 Bootstrap 是由 Twitter 的 Mark Otto 和 Jacob Thornton 开发的.Bootstrap 是 2011 年八月在 GitHub 上发布的开源产品. 写到这里,这篇从零开始学Bootstrap(3)我想写以下几个内容: 1. 基于我对Bootstrap的理解,做一个小小的总结.

  • Bootstrap零基础入门教程(二)

    什么是 Bootstrap? Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的. 历史 Bootstrap 是由 Twitter 的 Mark Otto 和 Jacob Thornton 开发的.Bootstrap 是 2011 年八月在 GitHub 上发布的开源产品. 本文重点给大家介绍Bootstrap零基础入门教程(二),具体详情如下所示: 过程中会频繁查阅资料的网站: http://www.

  • 一看就懂的ReactJs基础入门教程-精华版

    一.ReactJS简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了.由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单.所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具. ReactJS官网地址:http://facebook.github.io/re

  • Node.js基础入门之回调函数及异步与同步详解

    目录 回调函数 1. 什么是回调函数? 2. 回调函数实现机制 3. 回调函数用途 4. 回调函数示例 异步与同步 1. 什么是异步与同步? 2. 同步示例 3. 异步示例一 4. 异步示例二 异步的实现 1. 回调函数的同步示例 2. 异步事件示例 3. 异步示例截图 Promise基础 1. 什么是Promise ? 2. Promise特点 3. 异步的缺点 4. Promise保证异步顺序 经过前面两天的学习,已经对Node.js有了一个初步的认识,今天继续学习其他内容,并加以整理分享,

  • Android 基础入门教程——开发环境搭建

    现在主流的Android开发环境有: Eclipse + ADT + SDK Android Studio + SDK IntelliJ IDEA + SDK 现在国内大部分开发人员还是使用的Eclipse,而谷歌宣布不再更新ADT后,并且官网也去掉了集成Android开发环境的Eclipse下载链接,各种现象都表示开发者最后都终将过渡到Android Studio,当然这段过渡时间会很长,但如果你是刚学Android的话建议直接冲Android Studio着手:而且很多优秀的开源项目都是基于

  • MySQL基础入门教程之事务

    目录 引言 1.事务操作 1.1 未控制事务 1.2 控制事务一 1.3 控制事务二 2.事务的四大特性 3.并发事务问题 4.事务隔离级别 总结 引言 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败. 就比如: 张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000. 这一组操作就必须在一个事务的范围内,要么都成功,要么都失败. 正常情况: 转账这个操作, 需

  • C语言进阶教程之函数指针详解

    目录 一.函数指针 1.概念 1.2函数指针的使用方法 1.3练习巩固 1.4小结一下 二.阅读两段有趣的代码 1.( *(void( *)( ))0 )( ) 2.void (* signal(int,void( * )( int ) ) )(int) 附:函数指针的应用——函数回调 总结 一.函数指针 1.概念 函数指针:首先它是一个指针,一个指向函数的指针,在内存空间中存放的是函数的地址: 请看示例: int main(){ int a = 10; int*pa = &a; char ch

  • Leaflet 基础入门教程示例

    目录 什么是Webgis? 什么是Leaflet? 在Vue中安装Leaflet,与其他依赖 在App.vue中使用 初始化地图 chinaProvider地图瓦片 addControls使用工具集 attribution创建自定义版权 Marker创建点 创建线 Polygon创建三角形 Popup弹窗&Tooltip提示 Geojson区域描边 总结 什么是Webgis? webGis又称之为网络地理信息系统,GIS的全名是Geographic Information System,它是在计

  • C++基础入门教程(九):函数指针之回调

    在Java,要实现某个时间调用某段代码,是很简单的事情,那就是使用接口. 而在C++里,有一个比较高深的方式,那就是使用函数指针. 比如Cocos2d-x的定时器(schedule).消息订阅(NotificationCenter)都使用了函数指针来完成回调的功能. 这也是为什么我们总是能把某个函数作为参数传进去,然后在某个时刻这个函数会被调用. 一.函数的地址 要获取一个int变量的地址很简单,比如int num; 那么num的地址就是&num. 而获取函数的地址更简单,函数的名字就是函数的地

  • C++基础入门教程(八):函数指针

    最近事情比较多,其实并不忙,就是事情比较影响思绪,所以都没心思写文章了. 今天主要说说函数的一些基本情况吧,同时也解释一下新手最容易迷糊的--什么时候要用指针参数? 一.函数原型和函数定义 大家都知道,C++定义函数之前,还需要声明函数原型,对于习惯Java等其他高级语言的朋友来说,真心觉得这很烦人. 如下代码: 复制代码 代码如下: // 声明函数原型 void startGame(int param); // 函数定义 void startGame(int param) {     // 各

随机推荐