在这一章中要学习以下内容:

  • 函数基础
  • 函数原型
  • 通过value向函数传递参数
  • 设计处理数组的函数
  • 使用const指针参数
  • 设计函数处理文本字符串
  • 设计函数处理结构体
  • 设计函数处理string类型的对象
  • 函数的递归
  • 指向函数的指针(函数指针)

C++有一个庞大的有用的函数库(standard ANSI C library + 几个C++类),但是解决实际问题还需要定义我们自己的函数。

当然为了提高效率也需要STL和BOOST这些C++库。

函数回顾

为了使用C++函数,你需要做以下的事情:

  • 提供一个函数定义(function definition)
  • 提供一个函数原型(function prototype)
  • 调用函数(call the function)

下面提供了一个简单的例子

#include <iostream>

// function prototype
void simple();

int main() {
    using namespace std;
    cout << "main() will call the simple() function:\n";
    // function call
    simple();
    cout << "main() is finished with the simple() function.\n";
    ;
}

// function definition
void simple() {
    using namespace std;
    cout << "I'm but a simple function.\n";
}

在main函数和simple函数中都放了一个using指令,是因为每个函数都使用了cout。

定义一个函数

函数可以分为两类:

  • 没有返回值的
  • 有返回值的

没有返回值的相下面这样

void functionName(parameterList) {
    statement(s)
    // return是可选的
    return;
}

parameterList指定了传递给函数的参数的类型和个数。

可选的返回语句标志着函数的结束。

函数的返回值不能是数组,但可以是数组的指针。

函数是如何返回值的?

一般来说,函数是通过复制返回值到指定的CPU寄存器或者内存位置来返回函数值的。

然后调用者会检查那个位置。

调用函数和被调用函数必须对那个位置上返回类型达成一致。

原型和函数调用

函数的原型一般都放在include文件中。

#include <iostream>

// 无返回值的函数原型
void cheers(int);
// 有返回值的函数原型
double cube(double x);

int main() {
    using namespace std;
    // 函数调用
    cheers();
    cout << "Give me a number: ";
    double side;
    cin >> side;
    // 函数调用
    double volume = cube(side);
    cout << "A " << side << "-foot cube has a volume of ";
    cout << volume << " cubic feet.\n";
    cheers(cube());
    ;
}

void cheers(int n) {
    using namespace std;
    ; i < n; i++)
        cout << "Cheers! ";
    cout << endl;
}

double cube(double x) {
    return x * x * x;
}

为什么C++需要函数原型呢?

函数原型向编译器描述了函数接口。

第一,原型告诉了编译器函数应该接受几个以及什么类型的参数。

第二,编译器需要知道要几个字节以及何种类型来解释返回值。没有这些信息,编译器只能靠猜。

为什么C++编译器不去文件后面找函数定义呢?

原型符号(Prototype Syntax)

函数原型是一条语句,所以必须以分号结尾。

函数原型只需要类型信息就够了,参数名是什么、要不要都无所谓,参数名只是起到占位符的作用。

函数原型为你做了什么

函数原型极大地减少了程序出错的几率。

函数原型可以保证:

  • 编译器可以正确地处理返回值
  • 编译器可以检查函数调用的参数是否正确
  • 编译器检查是否使用了正确类型的参数

函数原型的检查发生在编译器,被称为静态类型检查(static type check)。

通过值传递函数参数

C++一般通过值来传递参数(by value)。

数值类型的值会传递给一个新的变量。

通过值传递的参数称为formal argument或者formal parameter。

而传递给函数的值称为actual argument或者actual parameter。

为了简化一点,C++用argument指实际参数,parameter称为形式参数。

在函数中声明的变量,包括parameters,都是函数私有的。

当函数调用的时候,计算机便为这些变量分配内存;当函数结束时,会释放这些变量。

多参数

传递给函数的多个参数用逗号分隔。

#include <iostream>

long double probability(unsigned numbers, unsigned picks);
int main() {
    using namespace std;
    double total, choices;
    cout << "Enter the total number of choices on the game card and\n"
        "the number of picks allowed:\n";
    while((cin >> total >> choices) && choices <= total) {
        cout << "You have one chance in ";
        cout << probability(total, choices);
        cout << " of winning.\n";
        cout << "Next two numbers (q to quit): ";
    }
    cout << "bye\n";
    ;
}

long double probability(unsigned numbers, unsigned picks) {
    long double result = 1.0;
    long double n;
    unsigned p;
    ; n--, p--)
        result = result * n / p;
    return result;
}

这里要注意的cin >> total为啥输入q就退出了呢?

更多数组函数的例子

使用数组范围的函数

一般处理数组的函数都需要传入起始地址和数组的size,但是另一种方式是传入起始和结束指针就可以了。

指针和const

;
// 不能修改指针指向的对象的值, 但是可以改变指向
const int * ps = &sloth;
// 不能改变指针的指向, 但是可以修改指向的对象的值
int * const finger = &sloth;
// 既不能改变指向元素的值也不能改变元素的指向
const int * const me = &sloth;

下面这种双const还是可以用的呀

#include <iostream>
void test(const int * const p);
int main() {
    ;
    test(&a);

}

void test(const int * const p) {
    std::cout << "p = " << *p << std::endl;
}

函数的指针

如果不提函数指针的话,讲C/C++中的函数是不完整的。

函数和数据一样也是有地址的。地址真的是个神奇的东西。函数的地址就是存储的机器代码在内存中开始的地方。

函数地址对你和用户来说都不重要,但是对于程序来说很重要。

可以用指向函数的指针去调用这个函数,注意指针已经是变量了,变量是可以搞事情的。

为了使用函数的指针,你需要做以下事情。

  • 获取一个函数的地址
  • 声明一个指针指向这个地址
  • 使用这个指针去调用这个函数

获取函数的地址

获取函数的值非常简单,只要函数名,不要函数后面的括号就行。

比如think()是一个函数,think就是函数的地址。

为了将函数作为变量传递到另一个函数中,只需要传递函数名就可以了。

要注意区分的是你是传递的函数名还是传递的函数的返回值。

// 传递think函数的指针
process(think);
// 传递的think函数的返回值
thought(think());

声明一个指向函数的指针

声明的函数指针类型应该和函数的返回值以及函数的签名一致。

// 函数原型
double pam(int);
// 对应的函数指针
double (*pf) (int);

其实这个步骤挺简单的,把函数原型拿过来,然后把函数名改称(*pf)就可以了。

但是要注意如下这种情况。

// 指向返回值为double类型的函数的指针
double (*pf) (int);
// 返回类型为double *类型的名为pf的函数
double *pf (int);

看来这里面的坑还挺多的。

double ned(double);
int ted(int);
double (*pf) (int);
// 参数类型不匹配
pf = ned;
// 返回值不匹配不匹配
pf = ted;

// 声明以函数指针为参数的函数
void estimate(int lines, double (*pf) (int));
// 调用以函数指针作为参数的函数
estimate(, pam);

使用指针去调用函数

double pam(int);
double (*pf) (int);
// 让pf指向函数pam
pf = pam;
// 通过函数名调用
);
// 通过函数指针调用
);

C++中也可以这么调用,把函数指针当作函数名。

);

函数指针例子

#include <iostream>

double betsy(int);
double pam(int);
void estimate(int lines, double (*pf) (int));

int main() {
    using namespace std;
    int code;

    cout << "How many lines of code do you need? ";
    cin >> code;
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);
    ;

}

double betsy(int lns) {
    return 0.05 * lns;
}

double pam(int lns) {
    return 0.03 * lns + 0.0004 * lns * lns;
}

void estimate(int lines, double (*pf) (int)) {
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
}

函数的指针是个神器呀!

函数指针其他主题

主题个屁,就是函数指针的坑。

其他主题太坑了,很容易阵亡,还是留到下一篇学吧。

Functions: C++'s Programming Modules的更多相关文章

  1. The Python Standard Library

    The Python Standard Library¶ While The Python Language Reference describes the exact syntax and sema ...

  2. C语言学习书籍推荐《Practical C++ Programming》下载

    下载链接 :点我 C++ is a powerful, highly flexible, and adaptable programming language that allows software ...

  3. Linux LSM(Linux Security Modules) Hook Technology

    目录 . 引言 . Linux Security Module Framework Introduction . LSM Sourcecode Analysis . LSMs Hook Engine: ...

  4. Functional programming idiom

    A functional programming function is like a mathematical function, which produces an output that typ ...

  5. ansible modules开发(一)

    一 模块说明 官方是否有提供的类似功能模块? 可从下面两个连接确定官方提供的模块,以免重复造轮子 官方已发布的模块 http://docs.ansible.com/ansible/modules.ht ...

  6. Returning Values from Bash Functions

    转自:https://www.linuxjournal.com/content/return-values-bash-functions Bash functions, unlike function ...

  7. [SCSS] Write Custom Functions with the SCSS @function Directive

    Writing SCSS @functions is similar to writing functions in other programming languages; they can acc ...

  8. Writing device drivers in Linux: A brief tutorial

    “Do you pine for the nice days of Minix-1.1, when men were men and wrote their own device drivers?”  ...

  9. Introspection in Python How to spy on your Python objects Guide to Python introspection

    Guide to Python introspection https://www.ibm.com/developerworks/library/l-pyint/ Guide to Python in ...

随机推荐

  1. PopUpWindow使用详解(二)——进阶及答疑

      相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

  2. appium简明教程(10)——控件定位基础

    狭义上讲,UI级的自动化测试就是让机器代替人去点来点去的过程. 但机器去点什么(点上面还是点左边),怎么点(是长按还是轻触),这些东西是必须由代码的编写者所指示清楚的. 控件定位就是解决机器点什么的问 ...

  3. Nginx启动/重启脚本详解

    Nginx手动启动 停止操作 停止操作是通过向nginx进程发送信号(什么是信号请参阅linux文 章)来进行的步骤1:查询nginx主进程号ps -ef | grep nginx在进程列表里 面找m ...

  4. ss 命令

    ss命令用来显示处于活动状态的套接字信息.ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比net ...

  5. 常用DC-DC;AC-DC电源芯片

    求推荐几个常用的开关电源芯片http://bbs.21ic.com/icview-1245974-1-1.html(出处: 21ic电子技术论坛) 1.1 DC-DC电源转换器 1.低噪声电荷泵DC- ...

  6. pm2 设置开机启动

    一.官方文档: 官方相关文档:http://pm2.keymetrics.io/docs/usage/startup/#generating-a-startup-script 二.具体操作过程如下: ...

  7. Mysql 导入CSV数据 语句 导入时出现乱码的解决方案

    1. 登陆mysql 2. use testdb 3. 执行导入语句 LOAD DATA LOCAL INFILE 'd://exportedtest2.csv' INTO TABLE usertab ...

  8. 关于 SqlParameter 必须知道的!

    有时候写 SqlParameter 映射 new SqlParameter("@RecordId", SqlDbType.BigInt, 0, "RecordId&quo ...

  9. JVM调优——之CMS GC日志分析

    最近在学习JVM和GC调优,今天总结下CMS的一些特点和要点,让我们先简单的看下整个堆年轻代和年老代的垃圾收集器组合(以下配合java8完美支持,其他版本可能稍有不同),其中标红线的则是我们今天要着重 ...

  10. nginx配置ssl双向证书

    CA根证书制作 # 创建CA私钥 openssl genrsa -out ca.key 2048 #制作CA根证书(公钥) openssl req -new -x509 -days 3650 -key ...