6.1
实参和形参的区别?
形参在函数定义的形参表中进行定义,是一个变量,其作用域为整个函数。
而实参出现在函数调用中,是一个表达式。
进行函数调用时,用传递给函数的实参对形参进行初始化。
6.2
(a)函数所定义的返回值类型是 int类型,而实际上return返回的表达式的类型是string
类型,两个类型不同,而string类型又不能隐式转换为int类型。
修改:
string f()
{
string s;
//......
return s;
}
(b) 因为该函数定义中没有指定的返回值类型,在标准C++中,定义函数时不指定返回值类型
是非法的。
修改:
int f2(int i) {/*......*/ }
(c) 任意两个形参都不能同名,而且缺少左花括号。
修改:
int calc(int v1, int v2) {/*.....*/ }
(d)缺少一对括住函数体的花括号
修改:
double square(double x) { return x * x; }
6.3
#include
using namespace std;
int fact(int s,int s2)
{
s += s2;
s *= s2;
return s;
}
int main()
{
cout << "输入两个整数:" << endl;
int num1, num2;
cin >> num1 >> num2;
int j = fact(num1, num2);
cout << "j = " << j << endl;
return 0;
}
#include
using namespace std;
int fact(int val)
{
int ret = 1;
while (val>1)
{
ret *= val--;
}
return ret;
}
int main()
{
cout << "Enter a number : " << endl;
int num = 0;
cin >> num;
int j = fact(num);
cout << num << "的阶乘为: " << j << endl;
return 0;
}
6.5
#include
using namespace std;
int abs(int val)
{
if (val >=0)
{
return val;
}
else
{
return 0 - val;
}
}
int main()
{
cout << "输入任意一个整数: " << endl;
int num1 = 0;
cin >> num1;
int num2 = abs(num1);
cout << "|" << num1 << "|" << "=" << num2 << endl;
return 0;
}
6.6
解释形参,局部变量和静态变量的差别。
解答:
从本质上说,三者均属于局部作用域中的变量,其中,局部变量又可分为普通(非静态)局部变量和静态局部变量。它们的差别在于:
(1)形参的作用域为整个函数,而普通局部变量和静态局部变量的作用域为:从定义处到包含该变量定义的块的结束处。
(2)形参由调用函数时所传递的实参初始化;而普通局部变量和静态局部变量通常用初始化式进行初始化,且均在程序执行流程第一次经过该对象的定义语句时进行初始化。静态局部变量的初始化在整个程序执行的过程中只进行一次。
(3)形参和普通局部变量均属于自动变量,在每次调用函数是创建,并在函数结束时撤销;而静态局部变量的生命周期跨越了函数的多次调用,它在创建后直到程序结束时才撤销。
//例如 如果需要连续输出1到X之间的所有整数的阶乘 可用如下程序:
#include
using namespace std;
//用于辅助求阶乘的函数
int fac(int x)
{
static int result = 1; //静态变量
result *= x;
return result;
}
int main()
{
int upLmt; //upLmt为普通局部变量
cout <<"Enter value of upper limit : "<> upLmt;
//依次输出1到X之间的所有阶乘
for(int i = 1;i <= upLmt; ++i)
cout <
#include
using namespace std;
bool fct()
{
static int cnt = 0;
++cnt;
/*if (cnt>1)
{
return 1;
}
else
{
return 0;
}*/
bool i = cnt > 1 ? 1: 0;
return i;
}
int main()
{
int i = 0;
int cnt2 = 1;
while (i <3)
{
if (fct()== 1)
{
cout << "函数调用 " << ++cnt2 << "次" << endl;
}
else
{
cout << "函数调用 1 次" << endl;
}
++i;
}
return 0;
}
6.10
#include
using namespace std;
int swp(int* p1, int* p2) //该函数交换p1,p2所指的项
{
int a = *p1;
*p1 = *p2;
*p2 = a;
return 0;
}
int main()
{
cout << "输入两个整数:" << endl;
int num1, num2;
cin >> num1 >> num2;
swp(&num1, &num2);
cout << "num1 = " << num1 << "num2 = " << num2 << endl;
return 0;
}
6.11
#include
using namespace std;
int reset(int & r) //将数字重置为0
{
r = 0;
return r;
}
int main()
{
int val = 0; //要重置的数字
int preVal = 0; //用于保存输入的数字
cout << "Enter a number :" << endl;
cin >> val;
preVal = val; //保存重置前的数字
int val1 = reset(val); //
cout << preVal << "重置后: " << val1 << endl;
return 0;
}
#include
using namespace std;
int swp(int &p1, int &p2) //该函数交换p1,p2所指的项
{
int a = p1;
p1 = p2;
p2 = a;
return 0;
}
int main()
{
cout << "输入两个整数:" << endl;
int num1, num2;
cin >> num1 >> num2;
swp(num1, num2);
cout << "num1 = " << num1 << "num2 = " << num2 << endl;
return 0;
}
/*
使用引用更容易使用些,容易理解。
*/
void f(T)
void f(&T)
说明一下两种函数声明的区别:
前者声明的是T类型的形参,在f中修改形参的值 不会影响调用f时所传递的实参的值。
后置声明的是T类型的引用形参。在f中修改形参的值实际上相当于修改调用f时所传递的实参的值。
6.14
什么时候将形参定义为引用形参?
(1)如果希望通过函数调用修改实参的值,就应该将形参定义为引用类型。
例如:
void swap(int &v1,int &v2)
{
int tmp = v2;
v2 = v1;
v1 = tem;
}
(2)除了以上这种函数外,为了一次函数调用获得多个结果值,也可以用引用形参。
(3)另外,在向函数传递大型对象时,为了避免复制实参,以提高效率,以及无法复制的类类型(其复制构造函 数为 private的类类型)作为形参类型时,也应该将形参定义为引用形参。但这时候使用形参的目的是为了避 免复制实参,所以应该将形参定义为const引用。
什么时候不应该将形参定义为引用形参?
如果不需要通过函数调用修改实参的值,就不应该将形参定义为引用类型。
例如,在求绝对值的函数abs中。形参就不宜定义为引用类型。
6.16
bool is_empty(string& s) { return s.empty(); }
其局限在于:此处用引用形参可以避免复制实参。但是没有将
形参定义为const引用,从而导致不能使用字符串字面值来调用
该函数。(因为非const引用形参只能与完全同类型的非const
对象关联)。
更正为:
bool is_empty(const string& s){ return s.empty(); }
6.17
#include
#include
#include
using namespace std;
bool capital(const string& str) //函数判断字符串中是否含有大写字母
{
bool rember_bool = false;
for (auto &c :str) //用范围for遍历字符串
{
if (1 == isupper(c))
{
rember_bool = 1;
break;
}
}
return rember_bool;
}
string lwr(string& str) //函数将大写字母转化为小写字母
{
for (auto &c : str)
c = tolower(c);
return str; //返回string类型
}
int main()
{
string s1 ;
cout << "Enter a string : " << endl;
cin >> s1;
cout << ((capital(s1) == 1 )? "字符串中含有大写字母" : "字符串不含有大写字母" )<< endl;
cout << s1 << " 大写转化成小写后的形式: " << lwr(s1) << endl;
return 0;
}
#include
#include
using namespace std;
//比较两个MaMatrix类型变量的大小关系
bool compare(Matrix &,Matrix&);
//改变相应的变量的数值
vector::iterator change_val(int, vector::iterator);
int main()
{
return 0;
}
6.19
double calc(double);
int count(const string&, char);
int sum(vector::iterator, vector::iterator, int);
vector vec(10);
(a)calc(23.4, 55.1);
(b)count("abcde", 'a');
(c)calc(66);
(d)sum(vec.begin(), vec.end(), 3.8);
(b)(c)(d)都合法。
(a)不合法。cala函数只有一个形参,而调用该函数值却传递了两个实参
引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们设为普通引用,会发生什么情况?
一:(1)如果函数无需改变引用形参的值,最好将其定义为常量引用。
(2)如果使用常量引用形参,函数所能接受的实参类型将加大。例如:可以把
const对象,字面值,或者需要类型转换的对象传递给常量引用形参。
二: 把函数不会改变的形参定义成普通引用,会给函数的调用者一种误导,即函数可以修改它的实参的值。此 外,使用引用而非常量引用也会极大地限制函数所能接受的实参的类型。
例如:不可以把const对象,字面值,或者需要类型转换的对象传递给普通的引用形参。
6.21
int compare(int val, const int *pval)
{
return ((val > *pval) ? val : *pval;)
}
/*该函数无需修改指针形参所指向的值,因此,为了保护指针形参
所指向的值,将指针形参定义为指向const对象的指针。*/
6.22
void swap(int * x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
#include
using namespace std;
//void print( int[]);
//void print( int[2]);
int print( int* val)
{
return *val * 2;
}
int main()
{
int i = 0;
int j[2] = { 0, 1 };
int k = print(&i);
int l = print(j);
cout << "i = " << k<< endl;
cout << "j[0] =" << l << endl;
return 0;
}
6.24
描述下面这个函数的行为。如果代码存在问题,请指出并改正
void print(const int ia[10])
{
for (size_t i = 0; i != 10; i++)
{
cout << ia[i] << endl;
}
}
如:j[2] = { 0, 1 };
此时,调用print(j)函数就会错误。,因为实参不是含有十个整数的数组。
6.26
#include
using namespace std;
int main(int argc, char **argv)
{
cout << "arguments passed to main(): " << endl;
for (int i = 0; i != argc;++i)
{
cout << argv[i] << endl;
}
return 0;
}
6.27
#include
#include
using namespace std;
int sum( initializer_list i) //函数对所有元素进行相加
{
int cnt = 0;
for (auto &c:i)
{
cnt += c;
}
return cnt;
}
int main()
{
initializer_list int_list{1, 2, 3, 4,5};
cout << "列表中所有元素的和为: " << sum(int_list) << endl;
return 0;
}
6.28
const int &类型变量
6.29
在范围for循环中使用initializer_list对象时,应该将循环变量声明成引用类型。
这是因为考虑到复制实参的问题,声明成引用,可以避免复制实参,提高了效率。
6.30
#include
#include
#include
using namespace std;
bool str_subrange(const string &str1,const string &str2)
{
if (str1.size() == str2.size())
{
return str1 == str2;
}
auto size = (str1.size() < str2.size()) ? str1.size() : str2.size();
for (decltype(size) i = 0; i != size; ++i)
{
if (str1[i] != str2[2])
return; //没有返回值,编译器报告这一错误
}
}
int main()
{
return EXIT_SUCCESS;
}
6.31
什么情况下返回的引用无效,什么情况下返回常量的引用无效?
解答:
(1)当返回局部对象的引用时,返回的引用无效。因为,当函数结束时,局部对象和临时对象所占用的空间也 就随之释放掉了,所以返回引用指向一个不再可用的内存空间。
(2)当我们希望返回的对象被修改时,返回的常量引用无效。
6.32
#include
#include
using namespace std;
int &get(int *arry, int index) //函数合法
{
return arry[index];
}
int main()
{
int ia[10];
for (int i = 0; i != 10; ++i) //修改返回的对象的值
{
get(ia, i) = i;
}
for (int i = 0; i != 10; ++i)
{
cout << ia[i] << endl;
}
return EXIT_SUCCESS;
}
6.33
#include
using namespace std;
//递归函数,输出vector对象的内容
vector vec(const vector& vec_str,vector::iterator iter)
{
if (iter != vec_str.end())
{
cout << *iter << endl;
return vec(vec_str, iter + 1);
}
return{}; //返回一个空vector对象
}
int main()
{
vector str{"Hello","World"};
vec(str, str.begin());
return EXIT_SUCCESS;
}
6.34
如果函数factorial的终止条件为:
if(val != 0)
会发生什么情况?
解答:
会出现这样问题,如果实参为负数X,则理论上该函数会求得X*(X-1)*....*(int型能表示的最小负数)*(int型能表示 的最大正数)*(int型能表示的最大正数-1)*....*1这样的结果,但实际上运行时会因为递归函数调用次数过多而 发生程序栈溢出。使得程序无法继续执行。
而当实参为负数时,原factorial函数求得的结果应该为1.
6.35
//当传入的值是 val--时,程序运行错误。
//出现死循环。
//如:相当于val = val-- ;
// val的值保持不变。
//
#include
using namespace std;
int factorial(int val)
{
if (val >1)
{
return factorial(val--) * val;
}
return 1;
}
int main()
{
int num = 0;
cin >> num;
cout << factorial(num) << endl;
return 0;
}
6.36 不知道如何写 求告诉
6.37
#include
#include
#include
using namespace std;
string arr[10];
//string(&arrRef)[10] = arr;
//类型别名
string (& func())[10];
//尾置返回类型
auto fun()->string (&)[10];
//decltype关键字
decltype(arr) &f();
int main()
{
return EXIT_SUCCESS;
}
6.38
#include
#include
using namespace std;
//返回数组的引用
int odd[] = { 1, 3, 5, 7, 9 };
int even[] = { 0, 2, 4, 6, 8 };
decltype(odd) &arrPtr(int i)
{
return (i % 2) ? odd : even;
}
//decltype(odd) *arrPtr(int i)
//{
// return (i % 2) ? &odd : &even; //返回一个指向数组的指针
//}
int main()
{
return EXIT_SUCCESS;
}
6.39
(a) int calc(int, int);
//计算两个数的值,double *reset(double *);
6.40
下面的哪个声明是错误的?为什么?
(a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char backgrnd);
(b)的声明有错误
原因:一旦某个形参被赋予了默认值,它其后的所有形参都必须有默认值
6.41
//char *init(int ht, int wd = 80, char backgrnd = ' ');
//(a) init();
//(b) init(24,10);
//(c) init(14,'*');
//
//(a)不合法,因为调用函数时必须显式指定至少一个实参
//(b)调用合法,也完全符合程序猿的初衷
//(c)调用合法,但是不符合程序员的初衷。‘*’是一个char类型,可以转化成 int类型在传递给形参wd.
6.42
#include
#include
using namespace std;
//如果ctr不为1则返回word的复数版本
string make_plural(size_t ctr, const string &word,
const string &ending = "s")
{
return (ctr = 1) ? word : word + ending;
}
int main()
{
cout << "Singular version: " << make_plural(1, "success", "es")
<< "\t\t plural version: "
<< make_plural(0, "success", "es") << endl
<< "Singular version: " << make_plural(1, "failure")
<< "\t\t plural version: "
<< make_plural(0, "failure") << endl;
return 0;
}
6.43
你会把下面哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
(a) inline bool eq(const BigInt&, const BigInt&){.....};
(b) void putValues(int *arr,int size);
二者均应放在头文件中。
(b)是函数声明,适合放在头文件中。
(a)虽然是一个函数定义,但这是一个内联函数定义,应该放在头文件中。因为:内联函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开函数的代码,这样一来,仅有函数的原型是不够的。而且内联函数有可能在程序中定义不止一次,这时必须保证在所有源文件中,其定义是完全相同的。把内联函数的定义放在头文件中,可以确保在调用函数时所使用的定义是相同的,并且保证在调用点该函数的定义对编译器是可见的。
6.44
#include
using namespace std;
inline bool isShorter(const string &s1, const string&s2)
{
return s1.size() < s2.size();
}
int main()
{
return 0;
}
什么是候选函数?什么是可行函数?
解答:
候选函数是指调用点上其声明是可见的而且与被调函数同名的函数。
可行函数是指从候选函数中选出的函数,必须满足下列条件:函数的形参数目与该函数调用实参数目相同,每个实参类型必须与对应形参的类型匹配,或者可以隐式转换成对应形参的类型。
6.50
(a)可行函数是void f (int,int)和 void f(double,double = 3.14).该调用不合法。存在二义性。即可将2.56转换为 int 型而调用前者,亦可将42转换为 double型而调用后者。
(b)可行函数是 void f(int) 和 void f(double, double = 3.14).该调用合法。最佳匹配函数是 void f(int)。
(c)可行函数是 void f(int ,int )和 void f(double, double = 3.14).调用合法,最佳匹配函数是void f(int ,int )
(d)可行函数是 void f(int ,int )和 void f(double, double = 3.14).调用合法,最佳匹配函数是void f(double, double = 3.14)
6.51
#include
using namespace std;
void f()
{
cout << "调用函数为 void f(); " << endl;
}
void f(int)
{
cout << "调用函数为 void f(int); " << endl;
}
void f(int, int)
{
cout << "调用函数为 void f(int , int); " << endl;
}
void f(double, double = 3.14)
{
cout << "调用函数为 void f(double, double = 3.14); " << endl;
}
int main()
{
/*f(2.56, 42);*/
f(42);
f(42, 0);
f(2.56, 3.14);
return 0;
}
6.52
(a)通过类型提升(实参由char 型转换为 int 型)实现匹配
(b)通过标准转换(实参由 double 型转换为 int 型)实现匹配
6.53
//(a)(b第二个声明的效果是:声明了一个重载的calc函数
(a) int calc(int&, int&);
int calc(const int&, const int&);
(b) int calc(char*, char*);
int calc(const char*, const char*);
//(c)中第二个的声明是对第一个的重复声明。因为当形参以副本传递(即按值传递)
// 时,不能基于形参是否为const来实现函数重载
(c) int calc(char*, char*);
int calc(char* const, char* const);
#include
#include
using namespace std;
int faction(int, int);
int main()
{
using pf = int(*)(int, int);
vector vect;
return 0;
}
6.55-----6.56
#include
#include
using namespace std;
//加法函数
int count_add(int val1, int val2)
{
/*cout << "进行加法运算,输入两个整数: " << endl;
cin >> val1 >> val2;*/
return val1 + val2;
}
//减法函数
int count_sub(int val1, int val2)
{
return val1 - val2;
}
//乘法函数
int count_mul(int val1, int val2)
{
return val1*val2;
}
//除法函数
int count_div(int val1, int val2)
{
return val1/val2;
}
int main()
{
using pf = int(*)(int, int); //pf 是指针类型
vector vect{ count_add, count_sub, count_mul, count_div };
for (auto &c : vect)
{
cout << c(4,2) << endl;
}
return 0;
}