C++自学 | 3 函数运算 & 数组 & 字符串
本内容仅属于个人自学记录,欢迎交流和指正,请勿转载或发表不当言论。
主要学习网址:https://www.runoob.com/
3.1 Cplus/C++ 函数
1. 主函数 main() :
在逻辑上,划分通常是根据每个函数执行一个特定任务来划分的,有时也称为方法,子例程或程序等。
函数声明告诉编译器函数的名称,返回类型和参数,函数定义提供了函数的实际主体。
return_type function_name( parameter list ); //函数声明
return_type function_name( parameter list )
{
body of the function
}
return_type:函数返回值的数据类型,不需要返回值的函数关键字可以写为 void。
function_name:函数实际名称,函数名和参数列表一起构成了函数签名。
parameter list:类似占位符。
2. 调用函数:
当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
3. 函数参数:
如果函数要使用参数,则必须声明接受参数值的变量,这些变量称为函数的形式参数。
形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。
调用类型 | 描述 |
---|---|
传值调用 | 该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 |
指针调用 | 该方法把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
引用调用 | 该方法把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
默认情况下,C++使用传值调用来传递参数,即函数内的代码不能改变用于调用函数的参数。
3. 参数的默认值:
定义一个函数时,可以为参数列表中后边的每一个参数指定默认值,当调用函数时,如果实际参数的值留空,则会使用这个默认值,若有指定的传递值,则会忽略默认值。
4. Lambda 函数和表达式:
C++11提供了对匿名函数的支持,称为 Lambda 函数,可以把函数看作对象,也可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
表达式具体形式如下:
[capture](parameters) -> return-type{body}
- [capture]:捕捉列表。捕捉列表总是出现在 lambda 表达式的开始处。事实上,[] 是 lambda 引出符。编译器根据该引出符判断接下来的代码是否是 lambda 函数。捕捉列表能够捕捉上下文中的变量供 lambda 函数使用。
- (parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号 () 一起省略。
- mutable:mutable 修饰符。默认情况下,lambda 函数总是一个 const 函数,mutable 可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
- ->return_type:返回类型。用追踪返回类型形式声明函数的返回类型。出于方便,不需要返回值的时候也可以连同符号 -> 一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
- {statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
在 lambda 函数的定义式中,参数列表和返回类型都是可选部分,而捕捉列表和函数体都可能为空,C++ 中最简单的 lambda 函数只需要声明为:
[] {}
其他声明例如:
[](int x, int y)-> int {int z = x+y; return z + x ;}
若没有返回值可表达为,其返回类型可被完全忽略:
[capture] (parameters) {body} //例如: []{ ++global_x; }
在 Lambda 表达式内可以访问当前作用域的变量,这被称为闭包(Closure)行为。C++中变量传递有传值和传引用的区别,可以通过[]来指定:
[] // 沒有定义任何变量。使用未定义变量会引发错误。 [x, &y] // x以传值方式传入(默认),y以引用方式传入。 [&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。 [=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。 [&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。 [=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
其中,传值方式捕捉的变量,函数调用运算符的重载方法是 const 属性,如果想对此进行改动的话,就可以使用 mutable :
int main() { int x = 10; auto add_x = [x](int a) mutable { x *= 2; return a + x; }; // 复制捕捉x cout << add_x(10) << endl; // 输出 30 return 0; }
此外,对于 [=] 和 [&]的形式,表达式可以直接使用 this 指针,但对于 [] 的形式,如果要使用 this 指针,必须显示传入:
[this]() { this -> someFunc() ;} ();
【实例 1:
std::vector<int> v = { 1, 2, 3, 4, 5, 6 }; //val取该范围内的值。 int even_count = 0; for_each(v.begin(), v.end(), [&even_count](int val){ if(!(val & 1)){ //利用 & 来判断 val的值 ++ even_count; } }); std::cout << "The number of even is " << even_count << std::endl;
【实例 2:
// 指明返回类型 auto add = [](int a, int b) -> int { return a + b; }; // 自动推断返回类型 auto multiply = [](int a, int b) { return a * b; }; int sum = add(2, 5); // 输出:7 int product = multiply(2, 5); // 输出:10
5. 递归函数:
函数直接或间接调用函数本身。
【实例:求n的阶乘。
#include <iostream> using namespace std; // 本程序用递归算法求n的阶乘 int counter = 1; int fac(int n) { cout << "第" << counter << "次调用:" << n << " * " << "fac(" << n - 1 << ")" << endl; if (n == 1) { return 1; } else { counter++; return n * fac(n - 1); // 断点1 } } int main() { int n; cout << "请输入求n的阶乘的n值:"; cin >> n; cout << "结果:fac(" << n << ")=" << fac(n) << endl; }
3.2 Cplus/C++ 数学运算
1. 内置数学函数:
常用的内置数学函数,包含在<cmath>头文件中。
序号 | 函数 & 描述 |
---|---|
1 | double cos(double); 该函数返回弧度角(double 型)的余弦。 |
2 | double sin(double); 该函数返回弧度角(double 型)的正弦。 |
3 | double tan(double); 该函数返回弧度角(double 型)的正切。 |
4 | double log(double); 该函数返回参数的自然对数。 |
5 | double pow(double, double); 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 |
6 | double hypot(double, double); 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 |
7 | double sqrt(double); 该函数返回参数的平方根。 |
8 | int abs(int); 该函数返回整数的绝对值。 |
9 | double fabs(double); 该函数返回任意一个浮点数的绝对值。 |
10 | double floor(double); 该函数返回一个小于或等于传入参数的最大整数。 |
2. 随机数:
随机数生成器有两个相关的函数:
rang():
返回一个伪随机数,生成随机数之前必须先调用 srand() 函数。
Windows 下 RAND_MAX 为 0x7fff=2^15-1=32767;linxu下的RAND_MAX为:2^31-1=2147483647。
srand() 函数
//原型 void srand(unsigned seed);
用法:需要提供一个种子,这个种子会对应一个随机数,如果使用相同的种子,其后 rand 会出现一样的随机数(多此执行的结果一样)。
为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time 函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将 time_t 型数据转化为(unsigned)型再传给 srand 函数,即: srand((unsigned) time(&t));
还有一个经常用法,不需要定义time_t型t变量,即: srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的t数据。
【实例 1:使用 time() 函数来获取系统时间的秒数,通过调用 rand() 函数来生成随机数:
#include <iostream> #include <ctime> //time函数也可以通过<time.h> (更多在C语言中使用)定义 #include <cstdlib> using namespace std; int main () { int i,j; // 设置种子 srand( (unsigned)time( NULL ) ); /* 生成 10 个随机数 */ for( i = 0; i < 10; i++ ) { // 生成实际的随机数 j= rand(); cout <<"随机数: " << j << endl; } return 0; }
【实例 2:”模除+加法“的方式取 0 到指定区间的(伪)随机数,但0-1000内每个数的出现概率不同。
//取0-1000内的随机数: srand((unsigned)time(NULL)); int i = rand()%1000;
【实例 2扩展:产生 [range_min/n, range_max/m] 范围内的随机数
int num = rand()%(n-m+1)+m; //区间内数的个数 + 区间起点值
int u = (double)rand() / (RAND_MAX + 1) * (range_max - range_min)+ range_min //RAND_MAX是C++内有的字。
【实例 3:使用随机数模拟一个抢红包的过程。
1 #include<iostream> 2 #include<ctime> 3 using namespace std; 4 5 int main() 6 { 7 int money = 0; 8 int member = 0; 9 10 cout << "请输入红包金额:" << endl; 11 cin >> money; 12 13 cout << "请输入红包个数(小于100):" << endl; 14 cin >> member; 15 16 17 //随机数种子 18 srand((unsigned)time(NULL)); 19 float a[100] = { 0 }; //最多分给100个人 20 float dismoney[100] = { 0 }; 21 int best; //手气最佳 22 int max = 0; 23 float suma = 0; //随机数总和 24 float sumb = 0; //红包总和 25 26 for (int i = 0; i < member; i++) 27 { 28 a[i] = rand()%100; 29 30 if (a[i] == 0) 31 { 32 a[i] = rand() % 100; 33 } 34 35 if (a[i]>max) 36 { 37 max = a[i]; 38 best = i; 39 } 40 41 suma += a[i]; 42 } 43 44 for (int i = 0; i < member-1; i++) 45 { 46 dismoney[i] = a[i] / suma * money; //按随机数百分比计算每个人获得的实际金额。 47 sumb += round(dismoney[i] * 100) / 100.0; 48 49 cout << "第" << i + 1 << "个人的红包是:" << round(dismoney[i] * 100) / 100.0; 50 51 if (best == i) 52 { 53 cout << "(手气最佳)" << endl; 54 } 55 else 56 { 57 cout << endl; 58 } 59 } 60 61 //最后一人的红包金额等于总金额减去前面的金额总和 62 cout << "第" << member << "个人的红包是:" << round((money - sumb) * 100) / 100.0; 63 if (best == member-1) 64 { 65 cout << "手气最佳" << endl; 66 } 67 else 68 { 69 cout << endl; 70 } 71 72 return 0; 73 }
实例输出结果:
请输入红包金额: 100 请输入红包个数(小于100): 10 第1个人的红包是:8.96 第2个人的红包是:17.35(手气最佳) 第3个人的红包是:1.31 第4个人的红包是:14.93 第5个人的红包是:7.09 第6个人的红包是:13.81 第7个人的红包是:15.86 第8个人的红包是:7.84 第9个人的红包是:7.84 第10个人的红包是:5.01
3.3 Cplus/C++ 数组
1. 概念:
数组数据结构是一个 可以存储一个固定大小的相同类型元素的顺序集合,所有的数组都是由连续的内存位置组成,最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组是声明一个数组变量,数组中的特定元素可以通过索引访问。
2. 声明数组
type arrayName [ arraySize ];
其中,arraySize 必须是一个大于0的整数常量。
3. 初始化数组
double balance[5] = { 1000.0, 2.0, 3.4, 7.0, 50.0 }; double balance[]={ 1000.0, 2.0, 3.4, 7.0, 50.0 }; //省略数组大小,数组大小将默认与后面数字的个数相同。 double balance[5] = { 1000.0, 2.0 }; //数组中[0]为1000.0,[1]为2.0,[2][3][4]均为0。
【实例
#include <iostream> using namespace std; #include <iomanip> using std::setw; int main () { int n[ 10 ]; // n 是一个包含 10 个整数的数组 // 初始化数组元素 for ( int i = 0; i < 10; i++ ) { n[ i ] = i + 100; // 设置元素 i 为 i + 100 } cout << "Element" << setw( 13 ) << "Value" << endl; // 输出数组中每个元素的值 for ( int j = 0; j < 10; j++ ) { cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl; } return 0; }
其中,setw(int n) 函数的作用是格式化输出(只对紧随其后的输出有效),n为设置的输出域宽度,当后面的输出字段长度小于n时,在该字段前面用空格补齐;当输出字段长度大于n时,全部整体输出,其头文件为#include<iomanip>。
执行上述代码时,会产生下列结果。
Element Value //宽度分别为 7 和 5 0 100 1 101 2 102 3 103 4 104 5 105 6 106 7 107 8 108 9 109
【实例:获取数组长度
#include<iostream> using namespace std; template<class T> int length(T& arr) { //cout << sizeof(arr[0]) << endl; //cout << sizeof(arr) << endl; return sizeof(arr) / sizeof(arr[0]); } int main() { int arr[] = { 1,5,9,10,9,2 }; // 方法一 cout << "数组的长度为:" << length(arr) << endl; // 方法二 //cout << end(arr) << endl; //cout << begin(arr) << endl; cout << "数组的长度为:" << end(arr)-begin(arr) << endl; system("pause"); return 0; }
4. 多维数组:
1)二维数组:
数组的声明及排列方式
type arrayName [x][y]; //x行 y列
数组的初始化:
int a[3][4] = { {0, 1, 2, 3}, //初始化索引号为0的行 {4, 5, 6, 7}, //初始化索引号为1的行 {8, 9, 10, 11} //初始化索引号为2的行 };
上述方法也等同于:
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
二维数组一般使用嵌套循环进行处理:
#include <iostream> using namespace std; int main () { // 一个带有 5 行 2 列的数组 int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}}; // 输出数组中每个元素的值 for ( int i = 0; i < 5; i++ ) for ( int j = 0; j < 2; j++ ) { cout << "a[" << i << "][" << j << "]: "; cout << a[i][j]<< endl; } return 0; }
3.4 Cplus/C++ 字符串
1. C风格字符串:
该字符串实际时使用 null 字符 '\0' 终止的一维字符数组。
该字符串的声明和初始化如下所示,两种方式得到的数组相同:
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; //由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。 char greeting[] = "Hello";
处理以 null 字符 '\0' 结尾的字符串常用函数:
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。 |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
2. C++标准库中的String类型