[C/C++基础--笔试突击] 6.函数

概述:

  函数是有名字的计算单元,对程序的结构话至关重要。

  C++中,函数原型就是函数的声明,要加上分号。

  这一部分还是比较轻松的~~

  

6.1 参数传递

形参:出现在函数定义中,在整个函数体内都可以使用,函数体结束后被收回。

实参:主函数中调用,进入被调函数后,实参不能使用。

形参和实参的功能是数据传送,发生函数调用时,主调函数把实参的值传给被调函数的形参,从而实现主调函数向被调函数的数据传送。

函数调用时,C里面的两种传递:

1)值传递

2)指针传递(严格来说也属于值传递,只不过传递的是地址)

函数调用时,C++里面的三种传递:

1)值传递

2)指针传递

3)引用传递

给函数传递实参遵循变量初始化的规则。非引用类型的形参以相应实参的副本(值)初始化。对(非引用)形参的任何修改仅作用域局部副本,并不影响实参本身。为了避免传递副本的开销,可将形参指定为引用类型。对引用形参的任何修改都会直接影响实参本身。应将不需要修改响应实参的引用形参定义为const引用。

传递指针的引用

假设我们想编写一段代码实现两个指针的交换。已知需要*定义指针,用&定义引用。现在问题在于如何将这两个操作符结合起来以获得指向指针的引用,下面给出一个例子:

void pswap(int *&v1, int *&v2) {  // int *&v1 从右至左理解:v1是一个引用,与之相int型对象的指针相关联
                      //也就是说,v1是传递进pswap函数的任意指针的别名
    int *tmp = v2;
    v2 = v1;
    v1 = tmp;
}    

再写一个笔试题,下面程序打印的结果是什么:

void swap_int(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
void swap_str(char *a, char *b) {
    char* temp = a;
    a = b;
    b = temp;
}

int main(void) {
    int a = 10;
    int b = 5;
    char* str_a = "hello world";
    char* str_b = "world hello";
    swap_int(a, b);
    swap_str(str_a, str_b);
    printf("%d  %d  %s  %s\n", a, b, str_a, str_b);
    return 0;
}

解答:10  5  hello world  world hello。

首先a, b是值传递,不会修改实参,a、b的值不变;

在调用swap_str前,str_a和str_b的指向情况如下:

然后传参,使得a与str_a,b与str_b指向相同:

然后函数体内交换的结果:

可见,swap_str函数仅仅交换了a和b两个指针的指向,但实参的指向并没有改变,如果题目中改成指针的就可以交换实参的内容了。

6.2 内联函数

内联函数一般在代码中用inline修饰,内联函数有两种:

1)成员函数成为内联函数

2)普通函数成为内联函数

通常编译时,调用内联函数的地方,将不进行函数调用,而是使用函数体替换调用处的函数名,形式类似宏替换,这种替换成为内联扩展。

如我们将下面的shortString定义为内联函数:

inline const string &shortString(const string &s1, const string &s2) {
    return s1.size() < s2.size() ? s1 : s2 ;
}

则在调用的时候,如果是:

cout<<shortString(s1, s2)<<endl;

则在编译时可能将展开为:

cout<< ( s1.size() < s2.size() ? s1 : s2 ) <<endl;

从而消除了把shortString写成函数的额外执行开销。

注:一般来说,内联机制适用于优化下的、只有几行的而且经常被调用的函数。大多数的编译器都不支持递归函数的内联。

宏定义与内联函数的区别:

1)宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码;

2)宏定义没有类型检查,内联函数有类型检查。

6.3 默认参数

在函数声明或定义时,直接对参数赋值,这就是默认参数。在函数调用时没有指定与形参相对应的实参时候,就自动调用默认参数。

int mal(int a, int b = 3, int c = 6, int d = 8);
mal(1); // 相当于 mal(1, 3, 6, 8);

注意以下几点:

1)默认参数只可在函数声明中设定一次。只有在无函数声明时,才可以在函数定义中设定。

2)默认参数定义的顺序为自右到左。即如果一个参数设定了默认值,其右边的参数都要有默认值。

3)默认参数调用时,遵循参数调用顺序,自左到右逐个调用。

4)默认值可以使全局变量、全局常量,甚至是一个函数,但不可以是局部变量。因为默认参数的调用是在编译时确定的,而局部变量位置与默认值在编译时无法确定。

可变参数

用三个点(...)做参数占位符,下面一个例子用可变参数的函数实现多个数的相加:

int add(int num, ...) { // num为加数的个数
    int sum = 0;
    int index = 0;
    int* p = (int*)&num + 1;
    for(; index < num ; ++index)
        sum += *p++;
    return sum;
}

int main(void) {
    int i = 1, j = 2, k = 3;
    cout<<add(3, i, j, k);
    return 0;
}

6.4 函数重载

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。

进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同。不能够仅仅基于不同的返回类型而实现重载。

下面的笔试题:

在C++语言中,若类C中定义了一个方法 int f(int a, int b),那么方法(A)不能与该方法同时存在与类C中。

A. int f(int x, int y)                  B. int f(float a, int b)
C. float f(int x, float y)              D. int f(int x, float y)

6.5 函数模板与泛型

泛型编程:独立于任何特定类型的方式编写代码。

模板:创建类或函数的蓝图或公式。

6.5.1 函数模板

模板定义以关键字template开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,不能为空。

template<typename T> T fun(const T &a, const T &b) {
    ....
}

模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。模板形参选择的名字没有本质含义。可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类型形参。如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值。模板非类型形参是模板定义内部的常量值。

6.5.2 类模板

就像可以定义函数模板一样,也可以定义类模板。

template<class Type> class Queue {
public:
    Queue() ;  // 默认构造函数
    Type &front();
    const Type &front () const;
    void push(const Type &);
    void pop();
    bool empty() const;
private:
    //..
};

使用类模板时,必须为模板形参显示指定实参:

Queue<int> qi;

这一部分还是比较简单的,多用用就都能够理解了,欢迎一起讨论~~   0.0

返回目录 -> [C/C++基础--笔试突击] 概述

时间: 2024-07-31 19:32:50

[C/C++基础--笔试突击] 6.函数的相关文章

[C/C++基础--笔试突击] 7.指针与引用

概述: 比较抽象的但又很有用的东西 0.0 void*指针:可以保存任何类型对象的地址. 指向指针的指针 函数指针 7.1 指针 一个有效的指针必然是一下三种状态之一: 1)保存一个特定对象的地址: 2)指向某个对象后面的另一个对象 3)0值. 若指针保存0值,表明它不指向任何对象.未初始化的指针是无效的,直到给该指针赋值后,才可使用. 注:*p++和(*p)++不等价,单目运算符*的优先级比++高,故*p++先完成取值操作,然后对指针地址执行++操作,而(*p)++是首先执行屈指操作,然后对该

[C/C++基础--笔试突击] 5.C预处理器、作用域、static、const、内存管理

概述: C预处理器处理程序的源代码,在编译器之前运行,通常以符号#开头. 还会涉及到static.const的知识点...有的和java类同...有的容易混淆T.T. 本章很多以前都没有接触过,在笔试中见过...如果有什么错误,欢迎指正~~ 5.1 C预处理器 C语言的预处理主要有三个方面的内容: 1)宏定义与宏替换: 2)文件包含: 3)条件编译. 考点比较少,讲述一下对应需要注意的地方. 宏替换的本质很简单--文本替换.关于宏定义与宏替换请注意一下几点: 1)宏名一般用大写,宏名和参数的括号

[C/C++基础--笔试突击] 4.运算符及优先级

概述: 表达式,由操作数和运算符组成. 笔试中通常的考点有操作符的优先级.异或等关系运算. 4.1 赋值语句 赋值运算符"=",操作符左边代表着存储单元的地址,称为左值,右边带表着需要的值,称为右值. 注:赋值操作符的左操作数必须是非const的左值. int const& max(int const& a, int const& b) { return a > b ? a : b; } int& fun(int& a) { a += 5;

【C++基础 05】友元函数和友元类

友元是一种定义在类外部的普通函数或类,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend. 友元不是成员函数,但是它可以访问类中的私有成员. 友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员. 有两种形式的友元: (1)友元函数:普通函数对一个访问某个类中的私有或保护成员. (2)友元类:类A中的成员函数访问类B中的私有或保护成员. 友元函数: 在类声明的任何区域中声明,而定义则在类的外部. frien

[C/C++基础] C语言常用函数sprintf和snprintf的使用方法

Sprintf 函数声明:int sprintf(char *buffer, const char *format [, argument1, argument2, -]) 用途:将一段数据写入以地址buffer开始的字符串缓冲区 所属库文件: <stdio.h> 参数:(1)buffer,将要写入数据的起始地址:(2)format,写入数据的格式:(3)argument:要写的数据,可以是任何格式的. 返回值:实际写入的字符串长度 说明:此函数需要注意缓冲区buffer溢出,要为写入的arg

Java Script基础(三) 函数

Java Script基础(三) 函数 一.JavaScript中的函数 在JavaScript中,函数类似于Java中的方法,是执行特定功能的代码块,可以重复调用.JavaScript中的函数分为两种,一种是系统函数,另一种是自定义函数. 1.系统函数 系统函数有JavaScript提供,直接调用即可,常用的系统函数包括: parseInt():转换为整数. parseFloat() :转换为浮点数. isNaN():判断是否是非数字,为非数字返回true,数字返回false. eval():

[C/C++基础] C语言常用函数strlen的使用方法

函数声明:extern unsigned int strlen(char *s); 所属函数库:<string.h> 功能:返回s所指的字符串的长度,其中字符串必须以'\0'结尾 参数:s为字符串的初始地址 使用举例: 代码如下 编译运行结果 说明: 函数strlen比较容易理解,其功能和sizeof很容易混淆.其中sizeof指的是字符串声明后占用的内存长度,它就是一个操作符,不是函数:而strlen则是一个函数,它从第一个字节开始往后数,直到遇见了'\0',则停止. [C/C++基础] C

【javaScript基础】立即调用函数表达式

在javaScript中,每个函数被调用时,都会创建一个新的执行上下文.因为在一个函数里面定义的变量和函数只能在里面访问,在外面是不行的,上下文提供了一种很容易的方法来创建私有性. //makeCounter函数返回另外一个匿名函数,这个匿名函数能够访问到"私有"变量i, 好像有一点"特权"性. function makeCounter() { // i只能在makeCounter的里面被访问到 var i = 0; return function() { cons

【javascript基础】2、函数

原文:[javascript基础]2.函数 前言 我在上一篇[javascript基础]基本概念中介绍了javascript的一些基本概念,多谢大家的阅读和意见,自己写的东西可以被大家阅读,真心高兴,刚开始发布的时候我一直盯着阅读人数,虽然知道大家可能就是点开一下而已,但是还是给我一些继续写下去的信心.那今天写一些关于javascript函数的一些知识,帮助大家熟悉或者复习一些函数的基本知识. PS:最近jQuery源码交流群( 239147101)加了不少热新人,希望大家还是以学习为主,尽量少