好消息好消息,今天起得挺早,早起冲刺(?)
坏消息,2天计划有点紧,but可以再狗一狗(?)
感谢大佬几款优秀的支持C、C++在线编译器
stage1——2天入门阶段
教程网站:C++ 教程
在线编译器:compile c++ gcc online
刷题网站:阶段1第一关:基本数据类型
day2 planA
教程9+4,刷题3(10)
plan B
教程(15-23)+7,刷题1+2
教程完成度25%,刷题完成度40%
主要原因:从15开始内容指数递增,细节比较多,需要实操复习,博客长度就是证明>>
1.函数
子函数如果在主函数后面定义,就要在main()前声明
声明主要要说明形参类型,比如int compare(int,int);
当您在一个源文件中定义函数且在另一个文件中调用函数时(跨文件调用),函数声明是必需的。
在这种情况下,您应该在调用函数的文件顶部声明函数。
调用函数的3种传递参数方式:
---------------------------------------------------------------------------------------------------
引用调用:
在声明和定义函数的时候,输入参数部分要带上&;
调用的时候直接用变量名;
在定义函数的时候,形参改变,调用的时候变量也会改变,因为隐含了带地址。
引用调用
主要是在声明和定义函数的时候,输入参数部分要带上&,比如int swap(int &a,int &b);
但其实这里的&说是取地址,在实际操作的时候感觉和直接传值是一样的
但是在定义函数的时候,形参改变,调用的时候变量也会改变,因为隐含了带地址
测试实例
#include <iostream>
using namespace std;
void swap(int &a,int &b);//声明需要带&
int main()
{
int a=1,b=2;
cout << "the origin answer is\t"<< a << "\tand\t" << b << endl;
cout << "the origin address is\t"<< &a << "\tand\t" << &b << endl;
swap(a,b);//调用的时候,因为形参不是指针,所以直接调用变量,或者说带地址的变量
cout << "the answer 1 is\t"<< a << "\tand\t" << b << endl;
cout << "the address 1 is\t"<< &a << "\tand\t" << &b << endl;
return 0;
}
void swap(int &a, int &b)//定义一样的格式
{
#if 1
int m;
m=a;
a=b;
b=m;
#endif
}
-------------------------------------------------------------------------------------------------
指针调用:
a为整型变量,取地址(获得指针)为&a
a为指针(即地址),指向int类型变量,取地址中的数(获得指针指向的数)为*a
指针说明:
int a;//a为整数变量
则取地址(获得指针)为&a
int *a;//a为指针(即地址),指向int类型变量
则取地址中的数(获得指针指向的数)为*a
指针调用测试实例
#include <iostream>
using namespace std;
void swap1(int *a,int *b);//指针调用,交换地址里的参数
void swap2(int *a,int *b);//指针调用,交换地址,隐含同时换地址里的参数,等于没换
int main()
{
int a=1,b=2;
cout << "the origin answer is\t"<< a << "\tand\t" << b << endl;
cout << "the origin address is\t"<< &a << "\tand\t" << &b << endl;
swap1(&a,&b);
cout << "the answer 1 is\t"<< a << "\tand\t" << b << endl;
cout << "the address 1 is\t"<< &a << "\tand\t" << &b << endl;
a=1,b=2;
swap2(&a,&b);
cout << "the answer 2 is\t"<< a << "\tand\t" << b << endl;
cout << "the address 2 is\t"<< &a << "\tand\t" << &b << endl;
return 0;
}
void swap1(int *a, int *b)
{
#if 1
int m;
m=*a;
*a=*b;
*b=m;
#endif
}
void swap2(int *a, int *b)
{
#if 1
int *t;
t=a;
a=b;
b=t;
#endif
}
输出---------------------------------
the origin answer is 1 and 2
the origin address is 0x7fff6e41dda0 and 0x7fff6e41dda4
the answer 1 is 2 and 1
the address 1 is 0x7fff6e41dda0 and 0x7fff6e41dda4
the answer 2 is 1 and 2
the address 2 is 0x7fff6e41dda0 and 0x7fff6e41dda4
-------------------------------------------------------------------------------------------------
2.设置参数默认值
定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。如果指定了值,则会忽略默认值,使用传递的值。
子函数定义带赋值一定要在main()前面,不能用声明int sum(int a, int b=20);
在函数定义的时候直接赋值,就会作为默认值
注意:子函数定义带赋值一定要在main()前面,不能用声明int sum(int a, int b=20);这样!!!!,不然调用的时候summ=sum(a);就会报错
#include <iostream>
using namespace std;
int sum(int a, int b=20)
{
int m;
m=a+b;
return m;
}
int main()
{
int a=1,b=2;
cout << "the origin answer is\t"<< a << "\tand\t" << b << endl;
//cout << "the origin address is\t"<< &a << "\tand\t" << &b << endl;
int summ;
summ=sum(a,b);
cout << "the answer 1 is\t"<< summ << endl;
summ=sum(a);
cout << "the address 2 is\t"<< summ << endl;
return 0;
}
3.Lambda函数
Lambda 表达式把函数看作对象。
Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
Lambda 表达式本质上与函数声明非常类似。
[]没有内容的话,可以省略后面的()参数列表,但如果有内容,尤其是this,就需要(),即使不说形参,且在{}中需要表示为this->xxx;
lambda函数本身在{}后是不用加;的,如果使用lambda函数赋变量,通常结合auto类型,且lambda结束在{}后加;,在{}中指令结束也要加;
目前看下来感觉就是一个小型简易函数,整体可以用做一个指令结构,也可以用于快速定义一个操作(也就是作为函数快速赋给一个变量)
!!!!>>> 注意 >>>
>>> 注意:[]没有内容的话,可以省略后面的()参数列表,但如果有内容,尤其是this,就需要(),即使不说形参,且在{}中需要表示为this->xxx;
>>> 注意:lambda函数本身在{}后是不用加;的,如果使用lambda函数赋变量,通常结合auto类型,且lambda结束在{}后加;,在{}中指令结束也要加;
Lambda 表达式具体形式如下
[capture](parameters) mutable ->return-type{body}
>>> 详细说明 >>>
· [capture]:捕捉列表。捕捉列表总是出现在 lambda 表达式的开始处。
事实上,[] 是 lambda 引出符。
编译器根据该引出符判断接下来的代码是否是 lambda 函数。捕捉列表能够捕捉上下文中的变量供 lambda 函数使用。
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。
与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:
常用符号示例:
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。
但是,对于[]的形式,如果要使用 this 指针,必须显式传入:
[this]() { this->someFunc(); }();
· (parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号 () 一起省略。
· mutable:mutable 修饰符。默认情况下,lambda 函数总是一个 const 函数,mutable 可以取消其常量性。
在使用该修饰符时,参数列表不可省略(即使参数为空)。
· ->return_type:返回类型。用追踪返回类型形式声明函数的返回类型。
出于方便,不需要返回值的时候也可以连同符号 -> 一起省略。
此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
· {statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
//eg.
[](int x, int y){ return x < y ; }
如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。
[capture](parameters){body}
//eg.
[]{ ++global_x; }
//复杂例子
[](int x, int y) -> int { int z = x + y; return z + x; }
//测试实例1
/*
判断偶数-->通过和1按位与操作!!!!!!!!!!!!!!!!!!!!
*/
//输出1-10中的偶数个数
#include <iostream>
//vector库
#include <vector>
//for_each的库
#include<algorithm>
using namespace std;
int main()
{
vector<int> gr = {1,2,3,4,5,6,7,8,9,10};
int count;
for_each(gr.begin(), gr.end(),[&count](int grr)->int{ if(!(grr & 1)) count++; });
cout << "there are " << count << " even numbers." << endl;
return 0;
}
Tip:
判断偶数–>通过和1按位与操作
//测试实例1
/*
快速定义一个函数
*/
#include <iostream>
using namespace std;
int main()
{
auto output=[]{cout << "hello world!" << endl;};//需要注意的是,lambda函数结束要加;,同时里面的指令结束也要加;
output();
return 0;
}
//测试实例2 plus
//快速定义求和,求积函数
#include <iostream>
using namespace std;
int main()
{
auto output=[]{cout << "hello world!" << endl;};
auto sum=[](int a ,int b)->int{return a+b;};
auto mul=[](int a ,int b)->int{return a*b;};
int m=1,n=2;
output();
int x = sum(m,n);
cout << "the answer of sum() is " << x << endl;
int y = mul(m,n);
cout << "the answer of mul() is " << y << endl;
return 0;
}
此外,对于使用this的方式进行赋值,可以结合下面的例子理解一下
#include <iostream>
using namespace std;
class test
{
public:
void out()
{
cout << "hello world!" << endl;
}
void use()
{
auto v = [this]() {this->out();}; // 这里 this 调用的就是 class test 的对象了。使用this指针指向out()函数
v();
}
};
int main()
{
test t;
t.use();
return 0;
}
4.数学运算
注意!!! >>> 需要引用数学头文件<cmath>
有一些注意事项,比如
1)求三角函数的,用的是弧度角
,需要换算
2)注意这些函数,大多参数类型为double
5.随机数
有两个相关的函数。一个是rand()
,该函数只返回一个伪随机数,范围在0
到RAND_MAX(32767)
之间(整数),也可以用rand(100)
生成0-100
的随机数。生成随机数之前可以先调用srand()
函数设置随机数种子(无符号整数),如果没有设置随机数种子,rand()函数在调用时,自动设计随机数种子为1。随机种子相同,每次产生的随机数也会相同。
注意!!! >>> 使用rand()或者srand()需要头文件cstdlib>
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main ()
{
int i,j;
// 设置种子
srand( (unsigned)time( NULL ) );//这里采用time()函数获取系统时间的秒数
/* 生成 10 个随机数 */
for( i = 0; i < 10; i++ )
{
// 生成实际的随机数
j= rand();
cout <<"随机数: " << j << endl;
}
return 0;
}
为了防止随机数每次重复,常常使用系统时间来初始化,即使用time
函数来获得系统时间,它的返回值为从00:00:00 GMT, January 1, 1970
到现在所持续的秒数,然后将time_t
型数据转化为(unsigned)
型再传给srand
函数,即srand((unsigned) time(&t))
;还有一个经常用法,不需要定义time_t
型t
变量,即srand((unsigned) time(NULL))
,直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的t
数据。
//复习一下
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main()
{
srand((unsigned) time(NULL));//如果time()参数为空(NULL),函数将只通过返回值返回现在的日历时间,即从1970年1月1日0时0分0秒到此时的秒数。
int c;
for(int i=0;i<10;i++)
{
c = rand();
cout << c << endl;
}
return 0;
}
6.数组
数组,存储一个固定大小的相同类型元素的顺序集合。
数组的声明并不是声明一个个单独的变量,比如number0
、number1
、…、number99
,而是声明一个数组变量,比如numbers
,然后使用numbers[0]
、numbers[1]
、…、numbers[99]
来代表一个个单独的变量。数组中的特定元素可以通过索引访问。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组
type arrayName [ arraySize ];
//eg.
int groupp[10];
初始化
int groupp[10] = {1,2,3,4,5,6,7,8,9,10};
也可以直接初始化而不说明数组长度,直接默认长度
int groupp[] = {1,2,3,4,5,6,7,8,9,10};
单独初始化或单独赋值
int groupp[2]={3};
>>> !!! 注意 >>>
> 所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1。
只声明,不初始化,那么就要说明长度;要不然就直接初始化,可以不声明
#include <iostream>
using namespace std;
int main()
{
//1
int g[10]={1,2,3,4,5,6,7,8,9,10};
int salary;
salary=g[9];
cout << salary << endl;
//2
int gg[10];
int salaryy;
gg[9]=10;
salaryy=gg[9];
cout << salaryy << endl;
return 0;
}
7.输出格式调节
之前调节格式提到过\t
,实际上就是Tab
缩进,但是这种调节不能完全保证列对齐,所以可以用setw()
,可以设置输出的字段宽度,简单理解就是右对齐
。
setw(n)
,当后面紧跟着的输出字段
长度小于n
的时候,在该字段前面用空格补齐,当输出字段长度大于n
时,全部整体输出。
\t
,实际上就是Tab
缩进;
setw()
设置右对齐宽度,setw()的头文件,#include <iomanip>
#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;
}
输出--------------------------------------
Element Value
0 100
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
//复习一下
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
//1
int g[10]={1,2,3,4,5,6,7,8,9,10};
cout << "first line" << setw(15) << "second line" << endl;
for(int i=0;i<10;i=i+2)
{
cout << setw(10) << g[i] << setw(15) << g[i+1] << endl;
}
return 0;
}
8.多维数组
多维数组声明的一般形式如下:
type name[size1][size2]...[sizeN];
//eg.
int zu[2][3];
数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标
初始化
可以通过在括号内为每行指定值来进行初始化。
int zu[2][3] = {{1,2,3},{4,5,6}};
或者
int zu[2][3] = {1,2,3;4,5,6};
注意,如果直接输入所有元素,只用,不要用; ,不然会报错
此外,输入的个数可以小于长度(所有元素个数),没有初始化的部分默认为0
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
//1
int g[2][3]={1,2,3,4,5,6};
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
{
cout << g[i][j] << "\t";
}
cout << endl;
}
return 0;
}
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。
9.指向数组的指针
数组名是指向数组中第一个元素的常量指针。
//eg.
double runoobAarray[50];
runoobAarray 是一个指向 &runoobAarray[0] 的指针,即数组 runoobAarray 的第一个元素的地址。
double *p;
double runoobAarray[10];
p = runoobAarray;//将首个元素的地址赋给p
也可以用地址直接访问数组的元素。
*(runoobAarray + 4) 是一种访问 runoobAarray[4] 数据的合法方式。//此时,runoobAarray就是一个double * 类型的数据,即指向double数据的指针
一旦您把第一个元素的地址存储在p
中,您就可以使用*p
、*(p+1)
、*(p+2)
等来访问数组元素。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
// 带有 5 个元素的双精度浮点型数组
double runoobAarray[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
double *p;
p = runoobAarray;
// 输出数组中每个元素的值
cout << "使用指针的数组值 " << endl;
for(int i=0;i<5;i++)
{
#if 0
cout << *p << endl;//2种地址更新方式
p++;
#endif
#if 1
cout << *(p+i) << endl;
#endif
}
return 0;
}
输出的时候还是要数组名加索引
。
10.字符串
字符串实际上是使用null
字符\0
终止的一维字符数组。
输出的时候可以直接用数组名或者string
类型的变量名。
- 第1种——字符数组表示
- 第2种——string表示
声明及初始化
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};//一维字符数组的初始化方式
>>> !!! 注意 >>>
> 一定要带 \0 ,用于和数字数组相区分
> 和数字数组相同,定义不初始化就要说长度,如char s[20];
char site[] = "RUNOOB";//隐含 \0
string s1="hello";
string s2;//可以不初始化
内存表示
常见字符串操作函数
char s1="s1";
char s2="s2";
char s3[10];
string s4="s4";
string s5="s5";
string s6;
strcpy(s1,s2);//把s2复制到s1
s6=s4;
strcat(s1,s2);//s2连接在s1后面
连接string可以用 + 号
string s6=s4+s5;
strlen(s1);//返回字符串长度,不包含\0 ,只输出字符的个数,和定义的长度无关!!!
s4.size();
strcmp(s1,s2);//如果s1和s2是相同的,则返回0;如果s1<s2则返回值小于0;如果s1>s2则返回值大于0。
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止,与长度无关
所以在测试s1="helloworld",s2="world"的时候,s1<s2.
注意!!!!!,这些函数似乎只能对字符数组操作,不能对string
操作。
char
数组进行字符串操作的函数,头文件<cstring>
string
类型变量,头文件<string>
。
>>> !!! 注意 >>>
> char数组进行字符串操作的函数,头文件<cstring>
> string类型变量,头文件<string>
对比一下:
#include <iostream>
#include <cstring>//主要是调用str相关函数
using namespace std;
int main ()
{
char str1[13] = "runoob";
char str2[13] = "google";
char str3[13];
int len ;
// 复制 str1 到 str3
strcpy( str3, str1);
cout << "strcpy( str3, str1) : " << str3 << endl;
// 连接 str1 和 str2
strcat( str1, str2);
cout << "strcat( str1, str2): " << str1 << endl;
// 连接后,str1 的总长度
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str1 = "runoob";
string str2 = "google";
string str3;
int len ;
// 复制 str1 到 str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// 连接 str1 和 str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// 连接后,str3 的总长度
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
//复习一下
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int main ()
{
char s1[15]="hello";
char s2[]="world";
char s3[10];
#if 1
cout << "length of s1 is " << strlen(s1) << endl;
strcpy(s3,s1);
cout << "now s3 is " << s3 << endl;
#endif
strcat(s1,s2);
cout << "now s1 is " << s1 << endl;//如果这里是s1长度设置为10,可能会出现缓存越界的问题
cout << "now s2 is " << s2 << endl;
int c=strcmp(s1,s2);
cout << c << endl;
#if 1
if(strcmp(s1,s2)>0)
cout << "s1 is bigger than s2." << endl;
else if(strcmp(s1,s2)<0)
cout << "s1 is smaller than s2." << endl;
else
cout << "s1 is the same as s2." << endl;
#endif
#if 1
string t1="try";
string t2="again";
string t3;
cout << "length of t1 is " << t1.size() << endl;
t3=t1;
cout << "now t3 is " << t3 << endl;
t3=t1+t2;
cout << "now t3 is " << t3 << endl;
#endif
return 0;
}
注意!!!!!
数字数组不可以用数组名直接输出整个数组;
字符数组和字符串可以用数组名或变量名直接输出整个字符串。
>>>!!! 注意 >>>
> 数字数组不可以用数组名直接输出整个数组;
> 字符数组和字符串可以用数组名或变量名直接输出整个字符串
//eg.
#include <iostream>
#include <string>
char s1[]="hi";
std::cout << s1 << std::endl;
string s2="hello";
std::cout << s2 << std::endl;