题目描述
晨晨对小写字母情有独钟,特别喜欢小写字母,而且她很喜欢统计一个单词里面有多少个小写字母, 所以现在她想请你帮忙编写一条程序,专门统计一个单词里面小写字母的个数。
输入
一个单词(长度不大于 20) 。
输出
一个整数,代表单词里面小写字母的个数。
样例输入 Copy
Day
样例输出 Copy
2
程序
#include<iostream>
#include<fstream>
#include<algorithm>
using
namespace
std;
string er;
int
h;
int
main()
{
cin>>er;
for
(
int
i=0;i<er.size();i++)
{
if
(er[i]>=
'a'
&&er[i]<=
'z'
)h++;
}
cout<<h;
return
0;
}
小知识:return
返回类型和return语句
return 语句的作用是终止一个函数的执行,结束当前代码块中return后的语句,即return后的语句不再执行,从当前位置退出函数。return语句有两种形式:
return;
return expresion;
1
2
无返回值函数
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为这类函数的最后一句后面会隐式执行return。
// example
void swap(int &v1, int &v2){
// 如果两个值是相等的,则不需要交换,直接退出
if(v1 == v2){
return ;
}
// 如果执行到了这里,说明还需要继续完成某些功能
int tmp = v2;
v2 = v1;
v1 = tmp;
// 这里无须显示的return语句
}
1
2
3
4
5
6
7
8
9
10
11
12
强行另void函数返回其他类型的表达式将会产生编译错误!!
有返回值函数
return语句的第二种形式提供了函数的结果。只要函数的返回类型不是void,则该函数内的每条return语句必须返回一个值。return语句返回值类型必须与函数的返回类型相同,或者能够隐式地转换成函数的返回类型。
尽管编译器无法确保结果的正确性,但是可以保证每个return语句的结果类型正确。例如:
// 因为含有不正确的返回值 ,所以这段代码无法编译通过
bool str_subrange(const string &str1, const string &str2){
// 大小相同,此时用普通的相等性判断结果作为返回值
if(str1.size() == str2.size()){
return str1 == str; // 正确:==运算符返回的是布尔值
}
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 ; // 错误:没有返回值,编译器将会报告这一错误
}
// 错误:控制流可能尚未返回任何值就结束了函数的执行
// 编译器可能检查不出来这一错误!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意!含有return语句的循环后面应该也要有一条return语句,如果没有那么程序就是错误的!很多编译器都无法发现此类错误!
值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式完全一样:返回值是初始化调用点的一个临时量,该临时量就是函数调用的结果
string make_plural(size_t ctr, const string &word, const string &ending){
return (ctr > 1) ? word + ending : word;
}
1
2
3
该函数的返回类型是string,意味着和返回值将被拷贝到调用点。因此,该函数将返回word的副本或者一个未命名的临时string对象,该对象的内容是word和ending的和。
const string &shorterString(const string &s1, const string &s2){
return s1.size() <= s2.size() ? s1: s2;
}
1
2
3
其中形参和返回类型都是const string的引用,不管是调用函数还是返回结果都不会真正拷贝string对象!
不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也会随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域:
// 严重错误:这个函数试图返回局部对象的引用
const string &manip(){
string ret;
if(!ret.empty())
return ret; // 错误,返回局部对象的引用!
else
return "empty"; // 错误,"empty”是一个局部变量!
}
1
2
3
4
5
6
7
8
在第二条return语句中,字符串字面值转换成一个局部临时string对象,对于manip来说,该对象和ret一样都是局部的。
引用返回左值
函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数调用,特别是,能为返回类型是非常量引用的函数的结果赋值:
char &get_val(string &str, string::size_type ix){
return str(ix); // get_val假定索引值是有效的
}
int main(){
string s("a value"); // 输出a value
cout << s << endl; // 将s[0]的值改为A
get_value(s, 0) = 'A'; // 输出A value
cout << s << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
把函数调用放在赋值语句的左侧可能看起来会有点奇怪,但其实没什么特别的!返回值是引用,因此调用是个左值,和其他左值一样他也能出现在复制运算符的左侧!
列表初始化返回值
C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化;否则,返回的值有函数的返回类型决定。
vector<string> process(){
// ...
// expected actual是string对象
if(expected.empty()){
return {};
}
else if(expected == actual)
return {"functionX", "okay"};
else
return {"functionX", expected, actual};
}
1
2
3
4
5
6
7
8
9
10
11
如果函数返回的是内置类型,则花括号包围的列表最多包含一个值!而且该值所占空间不应该大于目标类型的空间!如果函数返回类型是类类型,由类本身定义初始值如何使用。
主函数main的返回值
如果函数的返回类型不是void,那么必须返回一个值。但是这条规定有个例外:我们允许main函数没有return语句直接结束。如果控制到达了main函数的结尾处而且没有return语句,编译器将隐式地插入一条返回0的return语句。
main函数的返回值可以看作是状态指示器。返回0表示执行成功,返回其他值表示执行失败,其中非0值的具体含义根据机器来定!
返回数组指针
因为数组不能拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用!要想定义一个返回数组的指针或者引用的函数比较繁琐,但可以使用类型别名来简化这一任务!
typedef int arrT[10]; // arrT是一个类型别名,表示的类型含有10个整数的数组
using arrT = int [10]; // arrT的等价声明
arrT* func(int i); // func返回一个指向含有10个整数的数组的指针
1
2
3
arrT是含有10个整数的数组别名。因为无法返回数组,所以将返回类型定义成数组的指针。因此,func函数接收一个int实参,返回一个指向包含10个整数数组的指针。
声明一个返回数组指针的函数
首先要区分以下的不同:
int arr[10]; //arr是一个含有10个整数的数组
int *p1[10]; //p1是一个含有10个指针的数组 数组中的值是指针
int (*p2)[10] = &arr; //p2是一个指针,它指向含有10个整数的数组
1
2
3
返回数组指针的函数形式如下所示:
Type (*function(parameter_list))[dimension];
// example
int (*func(int i))[10];
1
2
3
可以按照以下顺序来逐层理解该声明的定义:
func(int i) 调用func函数时需要int类型实参
(*func(int i)) 对函数调用的结果进行解引用操作
(*func(int i))[10] 解引用func的调用将得到一个大小是10的数组
int (*func(int i))[10]; 表示数组中的元素时int类型
#include<iostream>
using namespace std;
int (*func(int k))[10] {
int (*p)[10];
for(int i=0;i<k;i++){
(*p)[i] = i;
}
return p;
}
int main() {
int (*a)[10];
a = func(10);
for(int i=0; i<10; i++) {
cout << (*a)[i] << " ";
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用尾置返回类型
C++11新标准有一种能简化上述声明的方法,就是使用尾置返回类型。然和函数的定义都能使用位置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组或者数组的引用。为了表示函数真正的返回类型跟在形参列表之后,应该要在本应该出现返回类型的地方放置一个auto。
// func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数据
auto func(int i) -> int(*)[10];
1
2
把函数的返回类型放在形参列表之后,可以清楚地看到func函数返回是一个指针,并且该指针指向了含有10个整数的数组。
使用decltype
假设一直到函数返回的指针指向哪个数组,就可以使用decltype关键字声明返回类型。例如,下面的函数返回一个指针,该指针根据参数i的不同指向两个已知数组的某一个:
int odd[] = {1,3,5,7,9};
int even[] = {2,4,6,8,10};
// 返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPtr(itn i){
return (i%2) ? &odd : &even;
}
1
2
3
4
5
6
arrptr使用关键字decltype表示它的返回类型是个指针,并且该指针所指向的对象与odd类型一致。
需要注意的是,decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,想要表示arrptr返回指针,还必须在函数声明时加一个*符号。