C++Primer第五版 第6章 函数

6.1 函数基础

练习6.1:实参和形参的区别是什么?

形参是在函数参数列表中声明的局部变量,他们由每个函数调用中提供的实参初始化;实参是函数调用中提供的值,用于初始化函数的形参;
实参是形参的初始值;实参和形参之间存在一一对应关系;
实参的类型必须与对应的形参类型匹配(可以被强制类型转换也可以);
函数调用规定:实参数量应和形参数量一致;

练习6.2:请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?

(a) int f() {
          string s;
          // ...
          return s;
    }
(b) f2(int i) { /* ... */ } 
(c) int calc(int v1, int v1) { /* ... */ }
(d) double square (double x) return x * x;

(a) 返回值类型不匹配;
(b) 没有返回类型;修改 void f2(int i) { /* ... */ }
© 任意两个形参不能同名;int calc(int v1, int v2) { /* ... */ }
(d) 函数体需要使用{ }

练习6.3:编写你自己的fact函数,上机检查是否正确。

#include <iostream>

int fact(int i)
{
    int ret = 1;
    while(i > 1){
		ret *= i--
	}
    return ret;
}

int main()
{
    std::cout << fact(5) << std::endl;
    return 0;
}

练习6.4:编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。

#include <iostream>
int fact(int i)
{
    int ret = 1;
    while(i > 1){
		ret *= i--;
	}
    return ret;
}

int main()
{
    int inNum;
    std::cout << "Please input a number within [1, 13)" << std::endl;
   	while(std::cin >> inNum){
		if(inNum < 1 || inNum > 12) {
			std::cout << "invaild! Out of range, please try again" << std::endl;
			continue;
		}
		 int ans = fact(inNum);
    	 std::cout << ans << std::endl;
	} 
    return 0;
}

练习6.5:编写一个函数输出其实参的绝对值。

#include <iostream>
int abs(int i)
{
    return i > 0 ? i : -i;
}
int main()
{
    std::cout << abs(-70) << std::endl;
    std::cout << abs(5) << std::endl;
    return 0;
}

6.1.1 局部对象

练习6.6:说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。

//形参是一种局部变量;形参定义在函数体作用域之内,所以函数一旦终止,形参也就被销毁了;
//局部变量,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁他;
//局部静态变量,在程序的执行路径第一次警告对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对他有什么影响;
#include <iostream>
size_t count_calls(int n)
{
	static size_t ctr = 0;
	ctr += n;
	return ctr;
}
int main()
{
	for(size_t i = 0; i != 10; ++i)
		std::cout << count_calls(i) << " ";
	std::cout << std::endl;
	return 0;
}
//输出为 0 1 3 6 10 15 21 28 36 45 


练习6.7:编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1

#include <iostream>
size_t count_calls()
{
	static size_t ctr = 0;
	return ctr++;
}
int main()
{
	for(size_t i = 0; i != 10; ++i)
		std::cout << count_calls() << " ";
	std::cout << std::endl;
	return 0;
}
//输出为 0 1 2 3 4 5 6 7 8 9

6.1.2 函数声明

练习6.8:编写一个名为Chapter6.h的头文件,令其包含6.1节练习(第184页)中的函数声明

//Chapter6.h
int fact(int);//求阶乘
int abs(int);//求绝对值
/*
template <typename T> 
T abs(T i)
{
    return i >= 0 ? i : -i;
}
*/

6.1.3 分离式编译

练习6.9:编写你自己的fact.cc和factMain.cc,这两个文件都应该包含上一小节的练习中编写的Chapter6.h头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。

//fact.cc
#include <iostream>
#include Chapter6.h
int fact(int i)
{
    int ret = 1;
    while(i > 1)
		ret *= i--
    return ret;
}
//factMain.cc
#include <iostream>
#include Chapter6.h
int main()
{
    std::cout << fact(5) << std::endl;
    return 0;
}

6.2 参数传递

6.2.1 传值参数

练习6.10:编写一个函数,使用指针形参交换两个整数的值。在代码中调用该函数并输出交换后的结果,以此验证函数的正确性。

#include <iostream>
int swap(int *p, int *q) //指针形参
{
	int temp;
	temp = *p;
	*p = *q;
	*q = temp;
}
int main()
{
	int a = 10, b = 5;
	swap(&a, &b);
	std::cout << "a = " << a << " b = " << b << std::endl;
	return 0;
}
//输出 a = 5 b = 10

6.2.2 传引用参数

练习6.11:编写并验证你自己的reset函数,使其作用于引用类型的参数。

#include <iostream>
void reset(int &i){ i = 0; }
int main(){
	int j = 43;
	reset(j);
	std::cout << "j = " << j << std::endl;
	return 0;
}
//输出 j = 0

练习6.12:改写6.2.1节中练习6.10(第188页)的程序,使用引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?

#include <iostream>
int swap(int &p, int &q) 
{
	int temp = p;
	p = q;
	q = temp;
}
int main()
{
	int a = 10, b = 5;
	swap(a, b);
	std::cout << "a = " << a << " b = " << b << std::endl;
	return 0;
}
//输出 a = 5 b = 10
//交换两个整数的值,两种形参类型方法差不多方便;

练习6.13:假设T是某种类型的名字,说明以下两个函数声明的区别:一个是void f(T),另一个是void f(&T)。

`void f(T)` pass the argument by value. 函数对形参做的所有操作都不会影响实参.
`void f(T&)` pass a reference, 引用形参绑定其初始化对象,函数对引用形参的操作既是对实参的操作;

练习6.14:举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。

#include <iostream>
//形参应该是引用类型
int swap(int &p, int &q) //引用传递
{
	int temp = p;
	p = q;
	q = temp;
}
int swap2(int p, int q) //值传递
{
	int temp = p;
	p = q;
	q = temp;
}
int main()
{
	int a = 10, b = 5;
	swap(a, b);
	std::cout << "a = " << a << " b = " << b << std::endl;//输出 a = 5 b = 10
	
	int a2 = 10, b2 = 5;
	swap2(a2, b2);
	std::cout << "a2 = " << a2 << " b2 = " << b2 << std::endl;//输出 a2 = 10 b2 = 5
	return 0;
}
//不需要引用形参
void print(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
        for (std::vector<int>::iterator iter = begin; iter != end; ++iter)
                std::cout << *iter << std::endl;
}

练习6.15:说明find_char函数中的三个形参为什么是现在的类型,特别说明为什么s是常量引用而occurs是普通引用?为什么s和occurs是引用类型而c不是?如果令s是普通引用会发生什么情况?如果令occurs是常量引用会发生什么情况?

无需改变字符串s的值,可以将其声明为常量引用;occurs用来记录字符c出现的次数,需要被改变,所以不应声明为常量;
字符c可能是一个临时变量;
如果s是普通引用,字符串s的值可以被更改;occurs声明为常量,不能改变occurs的值,对occurs赋值是错误的语句;

6.2.3 const形参和实参

练习6.16:下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。

bool is_empty(string& s) { return s.empty(); }

使用常量引用,修改后:

bool is_empty(const string& s) { return s.empty(); }

练习6.17:编写一个函数,判断string对象中是否含有大写字母。编写另一个函数,把string对象全都改成小写形式。在这两个函数中你使用的形参类型相同吗?为什么?

#include <iostream>
#include <string>
using std::string;
//判断string对象中是否含有大写字母。
bool is_Uppercase(const string& s){
    for (auto ch : s){
        if(isupper(ch)) return true;
    }
    return false;
}
//把string对象全都改成小写形式
void to_Lowercase(string& s){
    for (auto &ch : s){
        if(isupper(ch)) ch = tolower(ch);
    }
}
int main(){
    string s1 = "hello";
    string s2 = "heLLo";
    std::cout << is_Uppercase(s1) << "\n" 
              << is_Uppercase(s2) << std::endl;
    to_Lowercase(s2);
    std::cout << s2 << std::endl;
    return 0;
}
//判断string中是否有大写字母,使用常量引用形参,因为实参的值不需要被改变;
//将string对象全都改成小写形式,使用普通引用形参,因为需要改变实参的值;

练习6.18:为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。

(a) 名为compare的函数,返回布尔值,两个参数都是matrix类的引用
(b) 名为change_val的函数,返回vector<int>的迭代器,有两个参数:一个是int,另一个是vector<int>的迭代器
(a) bool compare(matrix &m1, matrix &m2);
(b) vector<int>::iterator change_val(int, vector<int>::iterator);

练习6.19:假定有如下声明,判断哪个调用合法、哪个调用不合法。对于不合法的函数调用,说明原因。

double calc(double);
int count(const string &, char);
int sum(vector<int>::iterator, vector<int>::iterator, int);
(a) calc(23.4, 55.1); (b)count("abcda",'a'); 
(c)calc(66); (d)sum(vec.begin(), vec.end(), 3.8)

(a) illegal,calc只能传递一个参数;
(b) legal;
© legal;
(d) legal;

练习6.20:引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?
答:当函数无需修改引用形参的值时,最好将其声明为常量引用;
形参的值可能会被改变、

6.2.4 数组形参

练习6.21:编写一个函数,令其接受两个参数:一个是int型的数,另一个是int指针。函数比较int的值和指针所指的值,返回较大的那个。在该函数中指针的类型应该是什么?

#include <iostream>
int compare(const int i, const int* const j){
	return (i > *j) ? i : *j;
}
int main(){
    int j = 10;
    std::cout << compare(5,&j) << std::endl;
    return 0;
}

练习6.22:编写一个函数,令其交换两个int指针。

#include <iostream>
void swap(int * &i, int * &j){
	int *temp = j;
	j = i;
	i = temp;
}
int main(){
    int a = 5, b = 10;
    int *i = &a, *j = &b;
    swap(i,j);
    std::cout << *i << " " << *j << std::endl;
    return 0;
}

练习6.23:参考本节介绍的几个print函数,根据理解编写你自己的版本。依次调用每个函数使其输入下面定义的i和j:int i = 0, j[2] = {0,1};

#include <iostream>
using std::cout; using std::endl; using std::begin; using std::end;

void print(const int *pi)
{
    if(pi)
        cout << *pi << endl;
}

void print(const char *p)
{
    if (p)
        while (*p) cout << *p++;
    cout << endl;
}

void print(const int *beg, const int *end)
{
    while (beg != end)
        cout << *beg++ << " ";
    cout << endl;
}

void print(const int ia[], size_t size)
{
    for (size_t i = 0; i != size; ++i) {
        cout << ia[i] << " ";
    }
    cout << endl;
}

void print(int (&arr)[2])
{
    for (auto i : arr)
        cout << i << " ";
    cout << endl;
}

int main()
{
    int i = 0, j[2] = { 0, 1 };
    char ch[5] = "pezy";
    
    print(ch);					//pezy
    print(begin(j), end(j));	//0 1 
    print(&i);					//0
    print(j, end(j)-begin(j));	//0 1 
    print(j);					//0 1 

    return 0;
}

练习6.24:描述下面这个函数的行为。如果代码中存在问题,请指出并改正。

void print(const int (&ia)[10])
{
	for(size_t i = 0; i != 10++i)
		cout<< ia[i] << endl;
}

6.2.5 main:处理命令行选项

练习6.25:编写一个main函数,令其接受两个实参。把实参的内容连接成一个string对象并输出出来。
练习6.26:编写一个程序,使其接受本节所示的选项;输出传递给main函数的实参的内容

#include <iostream>
#include <string>

int main(int argc, char **argv)
{
    std::string str;
    for (int i = 1; i != argc; ++i)
        str += std::string(argv[i]) + " ";

    std::cout << str << std::endl;
    return 0;
}

6.2.6 含有可变形参的函数

练习6.27:编写一个函数,它的参数是initializer_list<int>类型的对象,函数的功能是计算列表中所有元素的和。

#include <iostream>
#include <initializer_list>
using std::cout; using std::endl;
using std::initializer_list;
void add(initializer_list<int> nums)
{
	int sum = 0;
	for(int i : nums)
		sum += i;
	cout << sum << endl;
}
int main()
{
	add({10,5,46,28,16,15});
	add({45,168,61,6});
	return 0;
}

练习6.28:在error_msg函数的第二个版本中包含ErrCode类型的参数,其中循环内的elem是什么类型?
答:const std::string&类型

练习6.29:在范围for循环中使用initializer_list对象时,应该将循环控制变量声明成引用类型吗?为什么?
答:当initializer_list对象的类型是POD数据类型时 ,不需要使用引用类型,因为POD类型拷贝很方便;其他时候则建议使用引用类型;

6.3 返回类型和return语句

6.3.1 无返回值函数

6.3.2 有返回值函数

练习6.30:编译第200页的str_subrange函数,看看你的编译器是如何处理函数中的错误的。

#include <iostream>
#include <string>
using std::cout; using std::endl;
using std::string;
bool str_subrange(const string &str1, const string &str2)
{
    //大小相同:此时用普通的相等性判断结果作为返回值
    if(str1.size() == str2.size())
        return str1 == str2;
    //得到较短的string对象的大小
    auto size = (str1.size() < str2.size()) 
                ? str1.size() : str2.size();
    //检查两个string对象的对应字符是否相等,以较短的字符串长度为限
    for(decltype(size) i = 0; i != size; ++i){
        if(str1[i] != str2[i]) return;
    }
}
int main()
{
	string s1("hello");
	string s2("hello!");
	cout << str_subrange(s1,s2) << endl;
	return 0;
}
//main.cpp: In function ‘bool str_subrange(const string&, const string&)’:
//main.cpp:15:32: error: return-statement with no value, in function returning //‘bool’ [-fpermissive]
//   15 |         if(str1[i] != str2[i]) return;
//      | 

练习6.31:什么情况下返回的引用无效?什么情况下返回常量的引用无效?
答: 函数返回的引用必须满足:引用的对象是一个在调用函数之前就存在的对象;返回局部对象的引用无效;

练习6.32:下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。

int &get(int *arry, int index) { return arry[index]; }
int main(){
	inr ia[10];
	for(inr i = 0; i != 10; ++i)
		get(ia,i) = i;
}

答:合法;

练习6.33:编写一个递归函数,输出vector对象的内容。

#include <iostream>
#include <vector>
using std::cout; using std::vector;
void print(vector<int>::const_iterator first, vector<int>::const_iterator last)
{
    if (first != last)
    {
        cout << *first << " ";
        print(++first, last);
    }
}
int main()
{
	vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
	print(vec.cbegin(), vec.cend());
	return 0;
}

练习6.34:如果factorial函数的停止条件如下所示,将发生什么情况?if(val != 0)
答:参数为正时,函数多执行一次求factorial(0)的值,对运算结果无影响;
参数为负时,递归将永远不会停止。因此,将发生堆栈溢出。
练习6.35:在调用factorial函数时,为什么我们传入的值是val-1而非val–?
答:val-1的实际执行顺序是先对val减去1,在执行factorial(x);
val–的实际执行顺序是执行factorial(x),这里x是未处理的val,在对val减去1;

6.3.3 返回数组指针

练习6.36:编写一个函数的声明,使其返回数组的引用并且该数组包含10个string对象。不要使用尾置返回类型、decltype或者类型别名。

string (&func(string s))[10];

练习6.37:为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用decltype关键字。你觉得哪种形式最好?为什么?

//使用尾置返回类型
auto func(string s) -> string(&)[10];
//使用decltype
decltype(vec) & func(string s)[10];//vec是存储string对象的已知数组
//使用类型别名
typedef string arrStr[10];//using arrStr = string[10];
arrStr & func(string s);
//我觉得使用类型别名形式最好。因为其一是见名知意,其二是这样看起来和定义内置类型相似。

练习6.38:修改arrPtr函数,使其返回数组的引用。

decltype(arrStr)& arrPtr(int i)
{
	 return (i % 2) ? odd : even;
}

6.4 函数重载

练习6.39:说明在下面的每组声明中第二条声明语句是何含义。如果有非法的声明,请指出来。

(a) int calc(int, int);
    int calc(const int, const int);
(b) int get();
    double get();
(c) int *reset(int *);
    double *reset(double *);

(a) 合法,重复的声明(没有定义)在C中是合法的++;
(b) 非法,只有返回类型不同;
© 合法,参数类型不同,返回类型也不同;

6.4.1 重载与作用域

6.5 特殊用途语言特性

6.5.1 默认实参

练习6.40:下面的哪个声明是错误的?为什么?

(a)int ff(int a, int b = 0, int c = 0);
(b)char *init(int ht = 24, int wd, char bckgrnd);

声明(b)是错误的,使用默认值的形参最好出现在形参列表的末尾;

练习6.41:下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?

char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a) init(); (b) init(24,10); (c) init(14,'*');

(a) 非法,形参ht没有默认实参,需要为其提供一个实参,否则没有与之对应的函数可以被调用;
(b) 合法;
© 合法,但与程序员的初衷不一致,实参使用时按其位置解析,'*'会被赋值给wd;

练习6.42:给make_plural函数(参见6.3.2节,第201页)的第二个形参赋予默认实参’s’,利用新版本的函数输出单词success和failure的单数和复数形式。

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

string make_plural(size_t ctr, const string& word, const string& ending = "s")
{
    return (ctr > 1) ? word + ending : word;
}

int main()
{
    cout << "singual: " << make_plural(1, "success", "es") << " "
         << make_plural(1, "failure") << endl;
    cout << "plural : " << make_plural(2, "success", "es") << " "
         << make_plural(2, "failure") << endl;

    return 0;
}

6.5.2 内联函数和constexpr函数

练习6.43:你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?

(a) inline bool eq(const BigInt&const BigInt&) {...}
(b) void putValues(int *arr, int size);

答:(a) 是内联函数的声明,放在头文件中,(b) 是声明,放在头文件中;

练习6.44:将6.2.2节(第189页)的isShorter函数改写成内联函数。

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

inline bool inShorter(const string &s1, const string &s2)
{
	return s1.size() < s2.size();
}
int main()
{
    cout << inShorter("hello", "hello!") << endl;
    return 0;
}

练习6.45:回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?如果是,将它们改写成内联函数;如果不是,说明原因。

练习6.46:能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数;如果不能,说明原因。

constexpr bool inShorter(const string &s1, const string &s2)
{
	return s1.size() < s2.size();
}
//调用
bool ans = inShorter("hello", "hello!");

答:不能,constexpr函数需要函数的返回类型及所有形参类型都是字面值类型;

6.5.3 调试帮助

练习6.47:改写6.3.2节(第205页)练习中使用递归输出vector内容的程序,使其有条件地输出与执行过程有关的信息。例如,每次调用时输出vector对象的大小。分别在打开和关闭调试器的情况下编译并执行这个程序。

#include <iostream>
#include <vector>
using std::vector; using std::cout; using std::endl;
void printVec(vector<int> &vec)
{
#ifndef NDEBUG
    cout << "vector size: " << vec.size() << endl;
#endif
    if (!vec.empty())
    {
        auto tmp = vec.back();
        vec.pop_back();
        printVec(vec);
        cout << tmp << " ";
    }
}

int main()
{
    vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    printVec(vec);
    cout << endl;
    return 0;
}

练习6.48:说明下面这个循环的含义,它对assert的使用合理吗?

string s;
while(cin >> s && s != sought) { } //空函数体
assert(cin);

答:这个循环一直输入单词,知道找到 sought存储的单词;
不好,因为如果用户开始输入一个单词,“cin”将始终具有内容。所以assert总是真的。这毫无意义。使用“assert(s==seed)”更好;

6.6 函数匹配

练习6.49:什么是候选函数?什么是可行函数?
答:候选函数:本次调用对应的重载函数集,一是与被调用的函数同名,而是其声明在调用点可见;
可行函数:能被这组实参调用的函数,一是其形参数量与本次提供的实参数量相等,而是每个实参的类型与对应的形参类型相同;

练习6.50:已知有第217页对函数f的声明,对于下面的每一个调用列出可行函数。其中哪个函数是最佳匹配?如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?

(a) f(2.56, 42) (b) f(42) (c) f(42, 0) (d) f(2.56, 3.14)

答: (a) f(2.56, 42) 调用具有二义性;
(b) f(42) 调用 f(int)
© f(42, 0) 调用 f(int, int)
(d) f(2.56, 3.14) 调用 f(double, double)

练习6.51:编写函数f的4个版本,令其各输出一条可以区分的消息。验证上一个练习的答案,如果你回答错了,反复研究本节的内容直到你弄清自己错在何处。

#include <iostream>
using std::cout; using std::endl;

void f()
{
    cout << "f()" << endl;
}

void f(int)
{
    cout << "f(int)" << endl;
}

void f(int, int)
{
    cout << "f(int, int)" << endl;
}

void f(double, double)
{
    cout << "f(double, double)" << endl;
}

int main()
{
    //f(2.56, 42); // error: 调用具有二义性
    f(42);
    f(42, 0);
    f(2.56, 3.14);
    
    return 0;
}

6.6.1 实参类型转换

练习6.52:已知有如下声明,请指出下列调用中每个类型转换的等级(参见6.6.1节,第219页)。

void manip(int, int);
double dobj;
(a)manip('a', 'z');
(b)manip(55.4, dobj);

答:(a)类型提升 (b)算术类型转换

练习6.53:说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。

(a) int calc(int&int&);
	int calc(const int&, const int&);
(b) int calc(char*, char*);
	int calc(const char*, const char*);
(c) int calc(char*, char*);
	int calc(char* const, char* const);

答: (a)第二条语句和第一条语句构成重载函数,如果实参是常量,那么会调用第二条语句;因为若形参是常量指针或者引用,那么取决于实参是否是常量而选择调用哪个函数;并且实参传递到形参的对象会忽略顶层const;
(b) 同(a);
© 声明不合法,两条语句不能构成重载函数,实参传递到形参的对象,会忽略掉顶层const,实际上两个函数声明其实一样,在调用时会进行精确匹配:向实参添加顶层const或者删掉顶层const都属于精确匹配

6.7 函数指针

练习6.54:编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。
练习6.55:编写4个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的vector对象中保存指向这些函数的指针。
练习6.56:调用上述vector对象中的每个元素并输出其结果。

#include <iostream>
#include <vector>
using std::cout; using std::endl;
using std::vector;

inline int f(const int, const int);
typedef decltype(f) fp;//函数类型

inline int NumAdd(const int n1, const int n2)  { return n1 + n2; }
inline int NumSub(const int n1, const int n2)  { return n1 - n2; }
inline int NumMul(const int n1, const int n2)  { return n1 * n2; }
inline int NumDiv(const int n1, const int n2)  { return n1 / n2; }

vector<fp*> v{ NumAdd, NumSub, NumMul, NumDiv };

int main()
{
    for (auto it = v.cbegin(); it != v.cend(); ++it)
        cout << (*it)(2, 2) << std::endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C Primer》是一本针对C语言初学者的经典教材,第五版的第六主要介绍了函数的基本概念和用法。 在第六中,教材首先介绍了函数的定义和调用。函数是具有独立功能的代码块,可以通过函数名来调用。函数函数头、参数、函数体和返回值组成。函数头包括函数类型、函数名和参数类型,参数用于接收传递给函数的值,返回值用于将结果返回给调用者。 接着,教材详细介绍了函数的参数传递方式,包括按值传递、按引用传递和按指针传递。按值传递是指将参数的值复制给形参,按引用传递是指将参数的引用传递给形参,而按指针传递是将参数的地址传递给形参。不同的传递方式在函数内部对参数进行操作时,会对应不同的结果。 此外,教材还讲解了函数的返回值和函数的调用顺序。函数的返回值类型由函数头中的类型确定,可以是任意类型。当函数需要返回多个值时,可以使用结构体或指针进行返回。函数的调用顺序决定了函数执行的顺序,即哪个函数先执行,哪个函数后执行。 在函数的实现过程中,教材介绍了函数的定义和声明、局部变量和全局变量、递归等内容。通过这些知识点的学习,读者可以了解到函数的具体细节和一些实践技巧。 总的来说,第五版的第六通过清晰的语言和丰富的例子,循序渐进地讲解了函数的基本概念和用法。学完这一,读者将能够理解函数的定义、调用、参数传递、返回值和实现等方面的知识,为之后的学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值