10分钟重温c++语法

发布时间:2024年01月14日

c++语法基础

?#include <iostream>
??
?using namespace std;
??
?int main(){
? ? ?cout << "Hello World" << endl;
? ? ?
? ? ?return 0;
?}

入门及简单的顺序结构

1、变量的定义

变量类型:

类型关键字大小
布尔型bool1btye
字符型char1btye
整型int4btye
浮点型(单精度)float4btye
双浮点型(双精度)double8btye

变量必须先定义,才可以使用。不能重名。

?int main() {
? ? ?int a, b = 2, c = b;
? ? ?float d = 1.5, f = 1.23e2;
? ? ?bool g = true;
? ? ?char j = 'a';
??
? ? ?return 0;
?}
2、输入输出
?int main() {
? ? ?int a, b;
? ? ?cin >> a >> b;//输入
? ? ?cout << a + b <<' '<< a * b << endl;//输出,endl是回车
? ? ?//先输出a+b,再输出空格,再输出a*b,最后回车
??
? ? ?return 0;
?}
?int main() {
? ? ?int a, b;
? ? ?scanf("%d%d", &a, &b);
? ? ?printf("%d %d", a + b, a * b);
? ? ?printf("a+b=%d\na*b=%d", a + b, a * b);
? ? ?float c;
? ? ?scanf("%f",&c);
? ? ?printf("a+c=%.2f",a+c);
??
? ? ?return 0;
?}
?/*
? * int:%d
? * float:%f
? * double:%lf
? * char:%c
? * long long:%lld
? * bool:用0和1表示
? */

scanf会把空格读入,cin不会读入空格。

printf拓展:

  • Float, double 等输出保留若干位小数时用:%.4f, %3lf

  • %8.3f, 表示这个浮点数的最小宽度为 8,保留 3 位小数,当宽度不足时 在前面补空格。

  • %-8.3f,表示最小宽度为 8,保留 3 位小数,当宽度不足时在后面补上空 格

  • %08.3f, 表示最小宽度为 8,保留 3 位小数,当宽度不足时在前面补上 0

3、表达式
?int main() {
? ? ?int a = 6 + 3 * 4 / 2 - 2;
? ? ?cout << a << endl;
? ? ?int b = a * 10 + 5 / 2;
? ? ?cout << b << endl;
? ? ?cout << 23 * 56 - 78 / 3 << endl;
? ? ?
? ? ?cout << 5 % 2 << endl;//1 ? 正数取模为正
? ? ?cout << -5 % 2 << endl;//-1 ? 负数取模为负
??
? ? ?return 0;
?}
4、变量类型转化
?int main() {
? ? ?float x = 123.12;
? ? ?int y = (int) x;
? ? ?cout << x << ' ' << y << endl;//向下取整
??
? ? ?return 0;
?}

隐式转换:低精度转为高精度

5、常量的定义
?#include <iostream>
?#include <cstdio>
??
?using namespace std;
??
?const double eps=1e-6; ? //定义常量
??
?int main() {
?    ……
? ? ?return 0;
?}?
浮点数的比较
?int main() {
? ? ?if (sqrt(3) * sqrt(3) - 3 <= eps) {
? ? ? ? ?printf("equal");
? ?  } else {
? ? ? ? ?printf("no");
? ?  }
??
? ? ?return 0;
?}

判断结构

1.基本 if-else 语句:

当条件成立时,执行某些语句;否则执行另一些语句。else语句可以省略

?int main() {
? ? ?int score;
? ? ?cin >> score;
? ? ?if (score >= 60) {
? ? ? ? ?cout << "及格" << endl;
? ?  } else {
? ? ? ? ?cout << "不及格" << endl;
? ?  }
??
? ? ?return 0;
?}
2.if-else if-else语句:
?int main() {
? ? ?int score;
? ? ?cin >> score;
? ? ?if (score >= 85) cout << 'A' << endl;
? ? ?else if (score>=60) cout << 'B' << endl;
? ? ?else cout<<'C'<<endl;
? ? ?
? ? ?return 0;
?}
3.条件表达式

与 && 或 || 非 !

循环

1.while循环
?int main() {
? ? ?int i=1,sum=0;
? ? ?while(i<=100){
? ? ? ? ?sum+=i;
? ? ? ? ?i++;
? ?  }
? ? ?printf("%d",sum);
? ? ?return 0;
?}

求斐波那契数列的第 n 项。f(1)=1, f(2)=1, f(3)=2, f(n)=f(n-1) + f(n-2)。

?int main() {
? ? ?int n;
? ? ?cin>>n;
? ? ?int a=1,b=1,i=1;
??
? ? ?while(i<n){
? ? ? ? ?int c=a+b;
? ? ? ? ?a=b;
? ? ? ? ?b=c;
? ? ? ? ?i++;
? ?  }
? ? ?cout<<a<<endl;
??
? ? ?return 0;
?}
2.do while
?int main() {
? ? ?int n=10,i=1,sum=0;
? ? ?do{
? ? ? ? ?sum+=i;
? ? ? ? ?i++;
? ?  }while(i<=10);
??
? ? ?printf("%d",sum);
? ? ?return 0;
?}
3.for循环

for(初始化语句;条件语句;表达式)语句;

?int main() {
? ? ?int i,sum;
? ? ?for(i=1,sum=0;i<=100;i++){
? ? ? ? ?sum+=i;
? ?  }
? ? ?printf("%d",sum);
? ? ?return 0;
?}
4.continue语句
?int main() {
? ? ?int i,sum;
? ? ?for(i=1,sum=0;i<=100;i++){
? ? ? ? ?if(i%2==0){
? ? ? ? ? ? ?continue;
? ? ? ?  }
? ? ? ? ?sum+=i;
? ?  }
? ? ?printf("%d",sum);
? ? ?return 0;
?}
5.break语句
?int main() {
? ? ?int i,sum;
? ? ?for(i=1,sum=0;i<=100;i++){
? ? ? ? ?if(sum>2000){
? ? ? ? ? ? ?break;
? ? ? ?  }
? ? ? ? ?sum+=i;
? ?  }
? ? ?printf("%d",sum);
? ? ?return 0;
?}

数组

1.一维数组
1.1数组初始化
?int main() {
? ? ?int a[10],b[20];
? ? ?float f[33];
? ? ?double d[123];
? ? ?char c[21];
??
? ? ?return 0;
?}
1.2数组初始化
?int main() {
? ? ?int a[3]={0,1,2};
? ? ?int b[]={0,1,2};
? ? ?int c[5]={0,1,2}; ? ? ? ? ? //等价于c[]={0,1,2,0,0},默认值为0
? ? ?char d[3]={'a','b','c'}; ? ?//字符数组初始化
??
? ? ?int f[10]={0}; ? ? ? ? ? ? ?//将数组全部初始化为0的写法
? ? ?return 0;
?}

函数内部定义数组有长度限制,如果数组太长运行报错(超出栈长),可以将数组定义在函数外(堆)。

函数内部定义的变量(局部变量)为随机值,函数外面定义的变量(全局变量)都是0。

1.3数组下标引用
?int main() {
? ? ?int a[3] = {0, 1, 2};
? ? ?cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl;
? ? ?a[0] = 5;
? ? ?cout << a[0] << endl;
??
? ? ?return 0;
?}
1.4翻转数组
?//头文件
?#include <algorithm>
?//使用方法
?reverse(a, a+n);//n为数组中的元素个数
??
?reverse(a,a+n);//翻转整个数组
?reverse(a,a+k);//翻转前k个元素
?reverse(a+k,a+n);//反转后n-k个元素

1.5高精度计算
?#include <iostream>
?#include <cstdio>
??
?using namespace std;
??
?int main() {
? ? ?int a[10000] = {1};
? ? ?int n, size = 1;
? ? ?cin >> n;
? ? ?while (n--) {
? ? ? ? ?int t =0;
? ? ? ? ?for (int i = 0; i < size; i++) {
? ? ? ? ? ? ?t+=a[i]*2;
? ? ? ? ? ? ?a[i]=t%10;
? ? ? ? ? ? ?t/=10;
? ? ? ?  }
? ? ? ? ?if(t) a[size++]=1;
? ?  }
? ? ?for (int i = size - 1; i >= 0; i--) {
? ? ? ? ?cout << a[i];
? ?  }
??
? ? ?return 0;
?}

2.多维数组
1.1数组定义
?int main() {
? ? ?int a[3][4];
? ? ?int arr[10][20][30]={0};
? ? ?int b[3][4]={
? ? ? ? ? ?  {0,1,2,3},
? ? ? ? ? ?  {4,5,6,7},
? ? ? ? ? ?  {8,9,10,11}
? ?  };
? ? ?for(int i=0;i<3;i++){
? ? ? ? ?for(int j=0;j<4;j++){
? ? ? ? ? ? ?cout<<b[i][j]<<' ';
? ? ? ?  }
? ?  }
??
? ? ?return 0;
?}
1.2初始化数组
?#include <iostream>
?#include <cstring>//头文件
??
?using namespace std;
??
?int main() {
? ? ?int a[10];
? ? ?memset(a,0,40);//数组的名字,初始化内容(把每一个字节赋值为0或其他),初始化的长度(以字节为单 ? ? ? ? ? ? ? ? ? ?  位,一个int占4个byte)
? ? ?memset(a,-1,sizeof a);
? ? ?
? ? ?for (int i = 0;i<10;i++){
? ? ? ? ?cout<<a[i]<<' ';
? ?  }
??
? ? ?return 0;
?}
1.3复制数组
?#include <iostream>
?#include <cstring>
??
?using namespace std;
??
?int main() {
? ? ?int a[10],b[10];
??
? ? ?for (int i=0;i<10;i++){
? ? ? ? ?a[i]=i;
? ?  }
? ? ?
? ? ?memcpy(b,a,sizeof a);//目标数组,原数组,长度
? ? ?
? ? ?return 0;
?}

字符串

1.字符与整数的联系——ASCII 码

每个常用字符都对应一个-128~127 的数字,二者之间可以相互转化:

?int main() {
? ? ?char c = 'a';
? ? ?cout << (int) c << endl;
??
? ? ?cout << (char) 97 << endl;
??
? ? ?return 0;
?}

常用 ASCII 值:’A’-‘Z’ 是 65~90,’a’-‘z’是 97-122,’0’-‘9’是 48-57。 字符可以参与运算,运算时会将其当做整数:

?int main() {
? ? ?int a='B'-'A';
? ? ?int b='A'*'B';
? ? ?char c='A'-2;
??
? ? ?cout<<a<<endl;
? ? ?cout<<b<<endl;
? ? ?cout<<c<<endl;
??
? ? ?return 0;
?}

2.字符数组

字符串就是字符数组加上结束符’\0’。 可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个’\0’字符,因此字符数组的长度至少要比字符串的长度多 1!

?int main() {
? ? ?char a1[]={'C','+','+'};
? ? ?char a2[]={'C','+','+','\0'};
? ? ?char a3[]="C++"; ? ? ? //a[2],a[3]等价
? ? ?char a4[6]="abcdef"; ? //错误:没有空间存放空字符
? ? ?
? ? ?return 0;
?}
2.1字符数组输入输出
?int main() {
? ? ?char str[100];
? ? ?cin>>str; ? ? ? ? ? ? ? ? ? //输入时,遇到空格或回车就会停止
? ? ?char str[100];
? ? ?scanf("%s",str); ? ? ? ? ? ?//注:不加&
? ? ?
? ? ?cout<<str<<endl; ? ? ? ? ? 
? ? ?printf("%s\n",str); ? ? ? ? //输出时,遇到空格或回车不会停止
? ? ?puts(str);
??
? ? ?return 0;
?}

读入一行字符串,包括空格:

?int main() {
? ? ?char str[100];
? ? ?fgets(str,100,stdin); ? //最大长度为100 ? //会读入回车(不建议使用)
? ? ?cin.getline(str,100);
? ? ?
? ? ?cout << str << endl;
? ? ?
? ? ?string s;
? ? ?getline(cin,s); ? ? ? ? //必须是string类型
? ? ?cout<<s<<endl;
??
? ? ?return 0;
?}

从某个位置开始输出:

?int main() {
? ? ?char str[] = "abcdefgh";
??
? ? ?cout << str << endl; ? ? ? ?//从a开始输出
? ? ?cout << str + 1 << endl; ? ?//从b开始输出(从下标1开始输出)
??
? ? ?return 0;
?}

数组从下标[1]开始:

?int main() {
? ? ?char str[100];
? ? ?scanf("%s",str+1);
? ? ?cin>>str+1;
??
? ? ?cout<<str<<endl;
? ? ?return 0;
?}
2.2 字符数组的常用操作

下面几个函数需要引入头文件:

#include <string.h><cstring> (1) strlen(str),求字符串的长度

(2) strcmp(a, b),比较两个字符串的大小,a < b 返回-1,a == b 返回 0,a > b 返回 1。这里的比较方式是字典序!

(3) strcpy(a, b),将字符串 b 复制给从 a 开始的字符数组。

3.3遍历字符数组中的字符:
?int main() {
? ? ?char a[100]="hello world";
??
? ? ?for(int i=0;i<strlen(a);i++){ ? ? ? //双层循环,每次strlen都要遍历一次
? ? ? ? ?cout<<a[i]<<endl;
? ?  }
??
? ? ?for(int i=0,len=strlen(a);i<len;i++){ ? ? //优化
? ? ? ? ?cout<<a[i]<<endl;
? ?  }
? ? ?
? ? ?return 0;
?}

3.标准库类型string

可变长的字符序列,比字符数组更加好用。需要引入头文件: #include <string>

3.1 定义和初始化
?int main() {
? ? ?string s1; ? ? ? ? //默认初始化,s1是一个空字符串
? ? ?string s2=s1; ? ? ?//s2是s1的副本
? ? ?string s3="hello"; //s3是该字符串字面值的副本
? ? ?string s4(10,'c'); //s4的内容是cccccccccc
??
? ? ?return 0;
?}
3.2 string 上的操作

(1)输入输出操作

?int main() {
? ? ?string s1, s2;
? ? ?cin >> s1 >> s2;
? ? ?cout << s1 << ' ' << s2 << endl;
? ? ?printf("%s\n",s1.c_str());
??
? ? ?return 0;
?}

注意:不能用scanf输入;不能用 printf 直接输出 string,需要写成:printf(“%s”, s.c_str())

(2)使用 getline 读取一整行:

?int main() {
? ? ?string s1, s2;
? ? ?getline(cin, s1);
? ? ?cout << s1 << endl;
??
? ? ?return 0;
?}

(3)string 的 empty 和 size 操作(注意 size 是无符号整数,因此 s.size()<= -1 一定成立):

?int main() {
? ? ?string s1, s2 = "abc";
? ? ?cout << s1.empty() << endl;
? ? ?cout << s2.empty() << endl;
??
? ? ?cout << s2.size() << endl;
? ? ?return 0;
?}

(4)string 的比较: 支持 > < >= <= == !=等所有比较操作,按字典序进行比较。

(5)两个 string 对象相加:

?int main() {
? ? ?string s1="hello ",s2="world\n";
? ? ?string s3 = s1 + s2;
? ? ?string s4 = s1 + "," + s2 + '\n';
? ? ?
? ? ?cout<<s3<<endl;
??
? ? ?return 0;
?}

当把 string 对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是 string: string s4 = s1 + “, “; // 正确:把一个 string 对象和有一个字面值相加 string s5 = “hello” +”, “; // 错误:两个运算对象都不是 string string s6 = s1 + “, “ + “world”; // 正确,每个加法运算都有一个运算符是 string string s7 = “hello” + “, “ + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的

3.3 处理 string 对象中的字符

可以将 string 对象当成字符数组来处理:

?int main() {
? ? string s="hello world";
? ? for(int i=0;i<s.size();i++){
? ? ? ? cout<<s[i]<<endl;
? ? }
? ? ?return 0;
?}
?int main() {
? ? ?string s = "hello world";
? ? ?for (char c:s) cout << c << endl;
? ? ?for (char &c:s) c = 'a'; ?//改变原内容需要加&
? ? ?for (auto c:s) cout << c << endl; ?//auto:编译器自己猜类型
? ? ?
? ? ?return 0;
?}
3.4取字符串的某一段
?int main() {
? ? ?string s1;
? ? ?getline(cin,s1);
? ? ?cout<<s1.substr(0,5); //参数:(起止位置,长度)(长度省略一直到结尾)
??
? ? ?return 0;
?}
3.5字符串流

导入库函数 #include <sstream>

把一个字符串初始化成类似cin的东西,得到我们任意想要的格式

?int main() {
? ? ?string s;
? ? ?getline(cin, s);
? ? ?stringstream ssin(s);
??
? ? ?int a, b;
? ? ?string str;
? ? ?double c;
??
? ? ?ssin >> a >> str >> b >> c;
? ? ?cout << a << endl << str << endl << b << endl << c << endl;
??
? ? ?return 0;
?}
3.5字符串最后一位处理
?int main() {
? ? ?string s;
? ? ?cin>>s;
? ? ?cout<<s.back()<<endl; ? ? //s.back()返回最后一位字符
? ? ?s.pop_back(); ? ? ? ? ? ? // s.pop_back()删去最后一位字符
??
? ? ?return 0;
?}

函数

1. 函数基础

一个典型的函数定义包括以下部分:返回类型、函数名字、由 0 个或多个形参组成的列表以及函数体。

1.1 编写函数
?//函数声明
?int func(int n);
??
?//函数定义
?int func(int n) {
? ? ?int res = 1;
? ? ?for (int i = 1; i <= n; i++) {
? ? ? ? ?res *= i;
? ?  }
??
? ? ?return res;
?}
??
?//调用函数
?int main() {
? ? ?int t = func(5);
? ? ?cout << t << endl;
??
? ? ?return 0;
?}
1.2形参和实参

实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,依次类推。形参和实参的类型和个数必须匹配。 fact(“hello”); // 错误:实参类型不正确 fact(); // 错误:实参数量不足 fact(42, 10, 0); // 错误:实参数量过多 fact(3.14); // 正确:该实参能转换成 int 类型,等价于 fact(3); 形参也可以设置默认值,但所有默认值必须是最后几个。当传入的实参个数少于形参个数时,最后没有被传入值的形参会使用默认值。

1.3函数的形参列表

函数的形参列表可以为空,但是不能省略。 void f1() {/ .... /} // 隐式地定义空形参列表 void f2(void) {/ ... /} // 显式地定义空形参列表 形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声 明。即使两个形参的类型一样,也必须把两个类型都写出来: int f3(int v1, v2) {/ ... /} // 错误 int f4(int v1, int v2) {/ ... /} // 正确

1.4局部变量、全局变量与静态变量

局部变量只可以在函数内部使用,全局变量可以在所有函数内使用。当局部 变量与全局变量重名时,会优先使用局部变量。

静态变量:(等价于在函数内部开了一个只有该函数能用的全局变量)

?int output(){
? ? ?static int cnt=0; ?//静态变量:无论调用函数多少次,都使用同一个变量,初始化只在第一次调用执行
? ? ?cnt++;
? ? ?cout<<cnt<<"times"<<endl;
?}
??
?int main() {
? ? ?output();
? ? ?output();
? ? ?output();
? ? ?output();
??
? ? ?return 0;
?}
2.参数传递
2.1 传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量。此时,对变量的改动不会影响初始值。

?int func(int x){
? ? ?x=5;
? ? ?return x;
?}
??
?int main() {
? ? ?int x=10;
? ? ?int t=func(x);
? ? ?cout<<x<<' '<<t<<endl; ? //10 5
? ? ?
? ? ?return 0;
?}
2.2 传引用参数

当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。

?int func(int &x){
? ? ?x=5;
? ? ?return x;
?}
??
?int main() {
? ? ?int x=10;
? ? ?int t=func(x);
? ? ?cout<<x<<' '<<t<<endl; ? ?//5 5 
??
? ? ?return 0;
?}
2.3 数组形参

在函数中对数组中的值的修改,会影响函数外面的数组。

一维数组形参的写法: // 尽管形式不同,但这三个 print 函数是等价的 void print(int *a) {/ ... /} void print(int a[]) {/ ... /} void print(int a[10]) {/ ... /}

?void output(int n,int a[3]){ ? //output(int n,int *a) ? output(int n,int a[])
? ? ?for(int i=0;i<n;i++){
? ? ? ? ?cout<<a[i]<<endl;
? ?  }
?}
??
?int main() {
? ? ?int a[3]={1,2,3};
? ? ?output(3,a);
??
? ? ?return 0;
?}

多维数组形参的写法: // 多维数组中,除了第一维之外,其余维度的大小必须指定 void print(int (*a)[10]) {/ ... /} void print(int a[][10]) {/ ... /}

?void output(int n, int m, int a[][3]) {
? ? ?for (int i = 0; i < n; i++) {
? ? ? ? ?for (int j = 0; j < m; j++) {
? ? ? ? ? ? ?cout << a[i][j] << endl;
? ? ? ?  }
? ?  }
?}
??
?int main() {
? ? ?int a[3][3] = {
? ? ? ? ? ?  {1, 2, 3},
? ? ? ? ? ?  {4, 5, 6},
? ? ? ? ? ?  {7, 8, 9},
? ?  };
? ? ?output(3, 3, a);
??
? ? ?return 0;
?}

3.返回类型和 return 语句

return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方 。 return 语句有两种形式: return; return expression;

3.1有返回值的函数

只要函数的返回类型不是 void,则该函数内的每条 return 语句必须返回一个值。return 语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。

3.2无返回值函数

没有返回值的 return 语句只能用在返回类型是 void 的函数中。返回 void 的函数不要求非得有 return 语句,因为在这类函数的最后一句后面会隐式地执行return。 通常情况下,void 函数如果想在它的中间位置提前退出,可以使用 return 语句。return 的这种用法有点类似于我们用 break 语句退出循环。

?void swap(int &v1, int &v2)
?{
?// 如果两个值相等,则不需要交换,直接退出
? ? ?if (v1 == v2)
? ? ? ? ?return;
?// 如果程序执行到了这里,说明还需要继续完成某些功能
? ? ?int tmp = v2;
? ? ?v2 = v1;
? ? ?v1 = tmp;
?// 此处无须显示的 return 语句
?}
4.函数递归

在一个函数内部,调用函数本身。

?using namespace std;
??
?int fact(int n){
? ? ?if(n<=1) return 1;
? ? ?return n*fact(n-1);
?}
??
?int main() {
? ? ?int n;
? ? ?cin>>n;
??
? ? ?cout<<fact(n)<<endl;
??
? ? ?return 0;
?}

类、结构体、指针、引用

1.类与结构体
1.类

类中的变量和函数被统一称为类的成员变量。

private 后面的内容是私有成员变量,在类的外部不能访问;public 后面的内容是公有成员变量,在类的外部可以访问。

?class Person{
?private:
? ? ?int age,height;
? ? ?double money;
? ? ?string books[100];
??
?public:
? ? ?string name;
? ? ?void say(){
? ? ? ? ?cout<<"i am "<<name<<endl;
? ?  }
??
? ? ?int get_age(){
? ? ? ? ?return age;
? ?  }
??
? ? ?void add_money(double x){
? ? ? ? ?money+=x;
? ?  }
??
?}; ? //类后面要加分号
??
??
?int main() {
? ? ?Person c;
? ? ?c.name="hutao";
? ? ?//c.age=18; ? ? ? ? ?  //错误!age是私有变量
? ? ?c.add_money(1000);
? ? ?cout<<c.get_age()<<endl;
??
? ? ?return 0;
?}

2.结构体

结构体和类的作用是一样的。不同点在于类默认是 private,结构体默认是 public。

?struct Person{
?private:
? ? ?int age,height;
? ? ?double money;
? ? ?string books[100];
??
?public:
? ? ?string name;
? ? ?void say(){
? ? ? ? ?cout<<"i am "<<name<<endl;
? ?  }
? ? ?int set_age(int a){
? ? ? ? ?age=a;
? ?  }
??
? ? ?int get_age(){
? ? ? ? ?return age;
? ?  }
??
? ? ?void add_money(double x){
? ? ? ? ?money+=x;
? ?  }
??
?} person_a,person_b,persons[100]; ? //定义完直接声明

(习惯上把只有数据的、函数比较少的定义为结构体;函数麻烦的、多的打包为类)

3.构造函数
?struct Person{
? ? ?int age,height;
? ? ?double money;
??
? ? ?Person(){} ? ? ? //没有参数的构造函数
??
? ? ?Person(int _age,int _height,double _money){ ? ? //构造函数
? ? ? ? ?age =_age;
? ? ? ? ?height=_height;
? ? ? ? ?money=_money;
? ?  }
?//构造函数也可以写成 
?Person(int _age,int _height,double _money) : age(_age),height(_height),money(_money){}
? ? ?
?};
??
??
?int main() {
? ? ?Person p1(18,185,100.0);
? ? ?Person p2={20,186,200.5};
? ? ?cout<<p1.age<<' '<<p2.money<<endl;
??
? ? ?return 0;
?}
2.指针与引用

指针指向存放变量的值的地址。因此我们可以通过指针来修改变量的值。

?int main() {
? ? ?int a = 10;
? ? ?int *p = &a; ? ? //这里的*代表变量p是指针类型,可以看作int* p(定义int类型的指针,p为变量名称)
? ? ?*p += 5;
? ? ?cout << *p << endl; ? //求变量p的值,此处*为操作符
??
? ? ?return 0;
?}

数组名是一种特殊的指针。指针可以做运算:

?int main() {
? ? ?int a[5] = {1, 2, 3, 4, 5};
??
? ? ?for (int i = 0; i < 5; i++) {
? ? ? ? ?cout << *(a + i) << endl; ? //指针+1的长度加的是一个类型的长度
? ?  }
??
? ? ?return 0;
?}
?int main() {
? ? ?int a[5] = {1, 2, 3, 4, 5};
??
? ? ?int *p = &a[0], *q = &a[2];
? ? ?cout << q - p << endl; ? ? //2
??
? ? ?return 0;
?}

引用和指针类似,相当于给变量起了个别名。

?int main() {
? ? ?int a=10;
? ? ?int &p=a; ? ?//定义一个变量p,与a同一个地址。
? ? ?p+=5;
? ? ?cout<<p<<' '<<a<<endl;
??
? ? ?return 0;
?}
3.链表
?struct Node {
? ? ?int val;
? ? ?Node *next;
??
? ? ?Node(int _val) : val(_val), next(NULL) {}
?};
??
??
?int main() {
? ? ?Node node = Node(1);
? ? ?Node *p = &node; ? ? ? ? ?//一般习惯定义完放指针里
?//以上两步等价于一步
? ? ?Node *p=new Node(1);//没有new代表定义一个Node变量值是1;有new代表定义了一个Node变量返回值是地址
? ? ?Node *q = new Node(2);
? ? ?Node *o = new Node(3);
??
? ? ?p->next = q;//调用类里面成员变量时,如果变量是指针(地址)用’->‘;不是指针用变量名+‘.’
? ? ?q->next = o;
??
? ? ?Node *head = p;//说链表的头结点大部分情况下说的是第一个节点的地址,而不是值。
??
? ? ?for(Node* i=head;i;i=i->next){ ? ? ? ? ?//链表的遍历
? ? ? ? ?cout<<i->val<<endl;
? ?  } 
??
? ? ?return 0;
?}

STL

1.vector
?#include <vector>

vector 是变长数组,支持随机访问,不支持在任意位置 O(1)插入。为了保证效率,元素的增删一般应该在末尾进行。

?int main() {
? ? ?vector<int> a; ? ? ? //相当于一个长度动态变化的 int 数组
? ? ?vector<int> a({1,2,3}); 
? ? ?vector<int> b[233]; ?//相当于第一维长 233,第二维长度动态变化的 int 数组
??
? ? ?struct rec{
? ? ? ? ?int x,y;
? ?  };
? ? ?vector<rec> c; ? ? ? //自定义的结构体类型也可以保存在 vector 中
??
? ? ?a.size(); ? ? ? ? ? //返回 vector 的实际长度(包含的元素个数)
? ? ?a.empty(); ? ? ? ? ?//返回一个bool 类型,表明 vector 是否为空。
? ? ?a.clear(); ? ? ? ? ?//把 vector 清空。
? ? ?
? ? ?return 0;
?}

迭代器: 迭代器就像 STL 容器的“指针”,可以用星号“*”操作符解除引用。 一个保存 int 的 vector 的迭代器声明方法为:

? ? ?vector<int>::iterator it = a.begin();

vector 的迭代器是“随机访问迭代器”,可以把 vector 的迭代器与一个整数相加减,其行为和指针的移动类似。可以把 vector 的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。

begin/end: begin 函 数 返 回 指 向 vector 中 第 一 个 元 素 的 迭 代 器 。 例 如 a 是 一 个 非 空 的vector,则*a.begin()与 a[0]的作用相同。 所有的容器都可以视作一个“前闭后开”的结构,end 函数返回 vector 的尾部,即 第 n 个 元 素 再 往 后 的 “ 边 界 ” *a.end() 与 a[n] 都 是 越 界 访 问 , 其 中n=a.size()。

?//下面代码都遍历了 vector<int>a,并输出它的所有元素。
?for (int i = 0; i < a.size(); i ++) cout << a[i] << endl;
?for (vector<int>::iterator it = a.begin(); it != a.end(); it ++) cout <<*it << endl;
?for (auto it = a.begin(); it != a.end(); it ++) cout <<*it << endl;//用auto替代
??
?for(int x:a) cout<<x<<' ';//范围遍历,与string一样

front/back front 函数返回 vector 的第一个元素,等价于*a.begin() 和 a[0]。 back 函数返回 vector 的最后一个元素,等价于a[a.size() –1]。

?cout<<a.front()<<' '<<a[0]<<endl;
?cout<<a.back()<<' '<<a[a.size()-1]<<endl;

push_back() 和 pop_back() a.push_back(x) 把元素 x 插入到 vector a 的尾部。 a.pop_back() 删除 vector a 的最后一个元素。

2.queue
?#include <queue>

头文件 queue 主要包括循环队列 queue 和优先队列 priority_queue 两个容器。

?queue<int> q;
?struct rec{
? ? ?int a,x,y;
?};
?queue<rec> q;//结构体rec的队列
??
?priority_queue<int> q; ? ? ? ? ? ? ? ? ? ? ? ? ? // 大根堆
?priority_queue<int, vector<int>, greater<int> q; // 小根堆
?priority_queue<pair<int, int>>q; ? ? ? ? ? ? ? ? //pair二元组
??
??
?struct rec {
? ? ? ? ?int a, b;
??
? ? ? ? ?bool operator<(const rec &t) const {
? ? ? ? ? ? ?return a < t.a;
? ? ? ?  }
? ?  };
? ? ?priority_queue<rec> q;//定义结构体类型的大根堆,结构体 rec 中必须定义小于号;小根堆定义大于号.

?循环队列 queue:
?queue<int> q;//队头插入,队尾弹出
?q.push(1);//在对头插入一个元素
?q.pop();//弹出队尾元素
?q.front();//返回队头
?q.back();//返回队尾
??
?优先队列 priority_queue:
?priority_queue<int> a;
?a.push(1);//插入一个数
?a.top();//取最大值
?a.pop();//删除最大值

队列没有clear,如果要清空队列,重新初始化即可

?q = queue<int>();

3.stack
?#include <stack>
?stack<int> stk;
??
?stk.push(1); ? ? //向栈顶插入
?stk.top(); ? ? ? //返回栈顶元素
?stk.pop(); ? ? ? //弹出栈顶元素

4.deque
?#include <deque>

双端队列 deque 是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是 vector 和 queue 的结合。与 vector 相比,deque 在头部增删元素仅需要 O(1)的时间;与 queue 相比,deque 像数组一样支持随机访问。

?deque<int> a;
??
?a.begin(),a.end(); ? ? ? ? ? ?//返回 deque 的头/尾迭代器
?a.front(),a.back(); ? ? ? ? ? //队头/队尾元素
?a.push_back(1); ? ? ? ? ? ? ? //从队尾入队
?a.push_front(1); ? ? ? ? ? ? ?//从队头入队
?a.pop_back(); ? ? ? ? ? ? ? ? //从队尾出队
?a.pop_front(); ? ? ? ? ? ? ? ?//从队头出队
?a.clear(); ? ? ? ? ? ? ? ? ? ?//清空队列

5.set
?#include <set>

头文件 set 主要包括 set 和 multiset 两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set 和 multiset 的内部实现是一棵红黑树,它们支持的函数基本相同。

?set<int> a; ? ? ? ? //元素不能重复
?multiset<int> b; ? ?//元素可以重复
??
?struct rec {
? ? ?int x, y;
??
? ? ?bool operator<(const rec &t) const {
? ? ? ? ?return x < t.x;
? ?  }
?};
?set<rec> c;
?a.size();
?a.empty();
?a.clear();

迭代器: set 和 multiset 的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持”++”和--“两个与算术相关的操作。 设 it 是一个迭代器,例如 :

?set<int>::iterator it = a.begin();

若把 it++,则 it 会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在 it 下一名的元素。同理,若把 it--,则 it 将会指向排在“上一个”的元素。

?s.begin();//是指向集合中最小元素的迭代器。
?s.end();//是指向集合中最大元素的下一个位置的迭代器。因此--s.end()是指向集合中最大元素的迭代器。

insert/find:

?s.insert(x); //把一个元素 x 插入到集合 s 中,时间复杂度为 O(logn)。
?s.find(x); //在集合 s 中查找等于 x 的元素,并返回指向该元素的迭代器。若不存在,则返回 s.end()。时间 ? ? ? ? ? ? 复杂度为 O(logn)。
?if(s.find(x)==s.end())//来判断x是否存在

lower_bound/upper_bound:

这 两 个 函 数 的 用 法 与 find 类 似 , 但 查 找 的 条 件 略 有 不 同 , 时 间 复 杂 度 为O(logn)。

?s.lower_bound(x);//查找大于等于 x 的元素中最小的一个,并返回指向该元素的迭代器。
?s.upper_bound(x);//查找大于 x 的元素中最小的一个,并返回指向该元素的迭代器。

erase:

?s.erase(it);//从 s 中删除迭代器 it 指向的元素,时间复杂度为O(logn)
?s.erase(x);//从 s 中删除所有等于 x 的元素,时间复杂度为O(k+logn),其中 k 是被删除的元素个数。

count:

?s.count(x);//返回集合 s 中等于 x 的元素个数,时间复杂度为 O(k +logn),其中k 为元素 x 的个数。

6.map
?#include <map>

map 容器是一个键值对 key-value 的映射,其内部实现是一棵以 key 为关键码的红黑树。Map 的 key 和 value 可以是任意类型,其中 key 必须定义小于号运算符。

?map<string,int> a;
?a["hello"]=2;
?cout<<a["hello"]<<endl;
?map<string,vector<int>> a;
?a["hello"]=vector<int>({1,2,3,4});
?cout<<a["hello"][2]<<endl;

size/empty/clear/begin/end 均与 set 类似。

insert/erase:

?map<string,int> a;
?a.insert({"hi",3}); ? ? ? //插入一个二元组
?cout<<a["hi"]<<endl;
?cout << (a.find("hi") == a.end()) << endl;//a.find(x) 在变量名为 a 的 map 中查找 key 为 x 的二元组。

7.bitset
?#include <bitset>
?bitset<1000> a; ?//定义一个长度为1000的0、1串,默认为0
?a[0]=1;
??
?cout<<a.count();//返回1的个数
??
?a.set(3);//把第三位设成1
?a.reset(3);//把第三位设成0

位运算

& 与 0&0=0; 0&1=0; 1&0=0; 1&1=1; | 或 0|0=0; 0|1=1; 1|0=1; 1|1=1; ~ 非 ~0=1; ~1=0; ^ 异或 0^0=0; 1^1=0; 1^0=1; 0^1=1; >> 右移 a>>k(a右移k位) 等价于 a除以2的k次方 << 左移 a<<k(a左移k位) 等价于 a乘以2的k次方

常用操作: (1) 求 x 的第 k 位数字 x >> k & 1 (2) lowbit(x) = x & -x,返回 x 的最后一位 1 等价于a&(~a+1)

常用库函数

?#include <algorithm>
1.reverse 翻转

翻转一个 vector: reverse(a.begin(), a.end());

翻转一个数组,元素存放在下标 1~n: reverse(a + 1, a + 1 + n);

?int a[]={1,2,3,4,5};
?reverse(a,a+5);//取到最后一个元素的下一个

2.unique 去重

前提:相同元素必须挨在一起

返回去重之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。

把一个 vector 去重: int m = unique(a.begin(), a.end()) – a.begin(); 把一个数组去重,元素存放在下标 1~n: int m = unique(a + 1, a + 1 + n) – (a +1);

数组去重处理:

?vector<int> a({1, 1, 2, 2, 3, 3, 4});
?sort(a.begin(), a.end()); // 对向量进行排序
??
?a.erase(unique(a.begin(),a.end()),a.end());//去除重复的元素之后剩下的数组
?for (auto x:a) cout << x << ' ';

3.random_shuffle 随机打乱
?#include <algorithm>
?#include <ctime>
??
?int main() {
? ? ?vector<int> a({1, 2, 3, 4, 5});
??
? ? ?srand(time(0));//设置种子,通常传入时间作为随机
??
? ? ?random_shuffle(a.begin(),a.end());
??
? ? ?for(auto x:a) cout<<x<<' ';
??
? ? ?return 0;
?}

4.sort排序

对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义 大小比较的函数,或者重载“小于号”运算符。

?vector<int> a({4, 3, 2, 1, 5});
?sort(a.begin(),a.end());
?for(auto x:a) cout<<x<<' ';
?bool cmp(int a,int b){//a是否应该排在b的前面
? ? ?return a>b;
?}
??
?int main() {
? ? ?vector<int> a({4, 3, 2, 1, 5});
??
? ? ?sort(a.begin(),a.end());//从小到大排
? ? ?for(auto x:a) cout<<x<<' ';
??
? ? ?sort(a.begin(),a.end(),greater<>());//从大到小排
? ? ?for(auto x:a) cout<<x<<' ';
??
? ? ?sort(a.begin(),a.end(),cmp);//自定义排序
? ? ?for(auto x:a) cout<<x<<' ';
??
? ? ?return 0;
?}

比较结构体:

?struct Rec {
? ? ?int x, y;
?};//
??
?bool cmp(Rec a, Rec b) { ? ?//a是否应该排在b的前面
? ? ?return a.x < b.x;
?}
??
?int main() {
? ? ?Rec a[5];
??
? ? ?for (int i = 0; i < 5; i++) {
? ? ? ? ?a[i].x = -i;
? ? ? ? ?a[i].y = i;
? ?  }
??
? ? ?for (int i = 0; i < 5; i++) printf("(%d,%d)", a[i].x, a[i].y);
? ? ?cout<<endl;
??
? ? ?sort(a, a + 5,cmp);
??
? ? ?for (int i = 0; i < 5; i++) printf("(%d,%d)", a[i].x, a[i].y);
??
? ? ?return 0;
?}
5.lower_bound/upper_bound 二分

lower_bound 的第三个参数传入一个元素 x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于 x 的元素的位置的迭代器(指针)。 upper_bound 的用法和 lower_bound 大致相同,唯一的区别是查找第一个大于x 的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。

?int a[] = {1, 2, 4, 5, 6};
??
?int *p = lower_bound(a, a + 6, 3);
?cout << *p << endl; ? ? //返回值
??
?int t = lower_bound(a, a + 6, 3)-a;
?cout << t << endl; ? ? ?//返回下标
?vector<int> a{1,2,3,4,5,6};
??
?int t= lower_bound(a.begin(),a.end(),3)-a.begin();
?cout<<a[t]<<endl;
6.next_permutation()

next_permutation(start,end),和prev_permutation(start,end)。这两个函数作用是一样的,区别就在于前者求的是当前排列的下一个排列,后一个求的是当前排列的上一个排列。

当当前序列不存在下一个排列时,函数返回false,否则返回true;

?vector<vector<int>> res;
?do{
?    res.push_back(nums);
?}while(next_permutation(nums.begin(),nums.end()));
? ? ? ? 
?return res;

文章来源:https://blog.csdn.net/os_wine/article/details/135527710
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。