数组形参
数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响。这两个性质是:不允许拷贝数组 以及使用数组时通常会将其转换成指针。所以当我们传递一个数组时,实际上传递的是指向数组首元素的指针。
尽管不能以值传递的形式传递数组,但是我们可以把形参写成类似数组的形式。
void print(const int*);
void print(const int[]);
void print(const int[10]);//这里的维度表示我们期望数组含有多少元素,实际不一定
上面三条声明,尽管表现形式不同,但是是等价的。当编译器对print函数调用时,只检查传入的参数是否是const int*类型。
int i=0,j[2]={0,1};
print(&i);//正确,&i的类型是int*
print(j);//正确,j自动转换成int*并指向j[0]
因为数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切大小,为以防调用时数组越界,调用者应该为此提供一些额外信息。
管理指针形参有三种常用技术:
- 使用标记指定数组长度
要求数组本身有一个结束标记,典型实例是C风格字符串。C风格字符串存储在字符数组中,并且在最后一个字符后面跟着一个空字符。函数遇到空字符时停止。
void print(const char*cp)
{
if(cp)//若cp不是一个空指针
while(*cp)//只要指针指的不是空字符
cout<< *cp++;//输出当前字符并将指针向前移动一个位置
}
适用于那些有明显结束标记且该标记不会与普通数据混淆的情况,就是安全的。
- 使用标准库规范
传递指向数组首元素和尾后元素的指针,这种方法受到了标准库的启发
void print(const int *beg,const int *end)
{
while(beg!=end)
cout<<*beg++<<endl;//输出当前元素并将指针向前移动一个位置
}
//在这里使用标准库函数提供需要的指针
int j[2]={0,1};
print( begin(j) ,end(j) );
只要调用者能正确的计算指针所指的位置,就是安全的。
- 显式传递一个表示数组大小的形参
在C程序和过去的C++程序中常常使用这种方法
//size 表示数组的大小
void print(const int ia[],size_t size)
{
for(size_t i=0;i!=size;++i)
cout<<ia[i]<<endl;
}
int j[]={0,1};
print(j, end(j) - begin(j) );//第二个参数为数组大小的值
只要传递给函数的size值不超过实际数组的大小,就安全。
关于引用的讨论同样适用于指针,当函数不需要对数组元素执行写操作的时候,数组的形参应该是指向const的指针,只有当函数确实要改变元素值的时候,才把形参定义为指向非常量的指针。
数组引用形参
C++语言允许将变量定义为数组的引用,即(可以建立数组的引用,不能建立引用的数组),基于同样道理,形参也可以是数组的引用。
void print(int (&arr)[10])
{
for(auto elem : arr)
cout<<elem<<endl;
}
因为数组的大小是构成数组类型的一部分,所以这一种方法无形中限制了print函数的可用性,我们只能将函数作用于大小为10的数组。
int i=0,j[2]={0,1};
int k[10]={0,1,2,3,4,5,6,7,8,9};
print(&i);//错误,实参不是含有10个整数的数组
print(j);//错误,实参不是含有10个整数的数组
print(k);//正确
即不同于数组形参的只要是int*就可以,必须是大小为10的数组,可以使用模板函数来解决这一尴尬困境。
传递多维数组
void print(int(*matrix)[10],int rowsize)
matrix被声明成指向含有10个整数的数组的指针
返回类型与return语句
返回 void 的函数不要求非得有return语句,因为在这类函数的最后会隐式地执行retrurn 语句。void类型的函数可以有无返回值的return语句。不过通常安全起见,应写 return 0;通常情况下,void函数如果想在中间退出,可以使用return语句类似于break;。 .
尽量满足单一出口原则。
//检查两个string对象的对应字符是否相等
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[i])
return;// 错误一 :bool类型函数的return语句不应该无返回值
}
//错误二: 在含有return语句的循环后面应该也有一条return语句,因为不满足的话是不会退出的
}
不要返回局部对象的指针或引用
函数完成后,局部变量便被销毁
const string &manip()
{
string ret="hello world";
if(!ret.empty())
return ret;//错误,ret是局部对象,而函数返回类型是局部对象的引用
else
return "Empty";//错误,“Empty”是一个局部临时量
返回一个值的方式和初始化一个变量或形参的方式一样,返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。所以上述代码两条return语句都指向了不可用的内存空间.
主函数main的返回值
如果函数的返回类型不是void,那么它必须返回一个值。但是我们允许main函数没有return语句直接结束,编译器将隐式地插入一条return 0;
main函数的返回值可以看做是状态指示器,返回0表示执行成功,返回其他值表示失败,非0值的含义由机器而定。但为了统一起见,cstdlib头文件定义了两个预处理变量,
int main()
{
if (.....)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;