基本语法:
cin >> a;
cout << "文字提示" << 变量或表达式 << endl;
这里 endl
相当于回车在使用 cin
和 cout
时,要在源文件最上方加上 #include <iostream>
和 using namespace std;
分为单行注释和多行注释:
//
/* */
定义为数据类型+变量名+赋初值
定义方式有两种
x1
2
3int main()
4{
5 const int month = 12; //const定义的常量
6}
两种定义的常量都是不能被修改的,否则会报错。
在给变量和常量起名字时,不要用关键字作变量名。
给变量起名字时,最好做到见名知意。
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short(短整型) | 2字节 | -215到215-1 |
int(整型) | 4字节 | -231到231-1 |
long(长整型) | win下4字节、Linux下4字节(32位)、8字节(64位) | -231到231-1 |
long long(长长整型) | 8字节 | -263到263-1 |
如果超出范围会发生一些有意思的事,比如:short a=32768;实际上a打印出来为-32768,从上限反转到下限。
其中int最为常用。
sizeof(数据类型/变量),返回是字节数
分为 float 和 double ,单精度占4个字节、双精度占8个字节,区别就是有效数字位数不同,不管是单精度还是双精度在打印时只显示6位有效数字
科学计数法:3e2 意思是3*10^2=300,e之后的数字表示10的多少次方。
字符型变量占用1个字节空间,字符型变量并不是把字符本身放到内存中去存储,而是将对应的 ASCII 编码放入存储单元,想要打印 ASCII 的值可以用 (int)a 来转换。可以直接用 ASCII 给字符型变量赋值如:ch = 97;
常见的一些错误如下:
常用的几个如下:
两种风格
#include<string>
bool类型只占用一个字节的空间,只有两个值 true 或 false 本质分别为1和0。
主要语法用到 cin >> 变量
,cin可以为整型、浮点型、字符型,字符串型、bool型(只要是输入非0的值,都会赋值成1)赋值。
加减乘除,应当注意的是除法运算,两个整型数相除结果依然是整型,0不能作除数否则报错。
取模运算即取余数使用 % ,应当注意的是 0不能作为取余运算的右操作数,且两个小数不能作取余运算。
前置递增和后置递增,都可以让变量加1,但是它们的区别是:前置递增是先让变量+1,再进行表达式的运算,后置递增是先进行表达式的运算,再让变量+1。前置递减和后置递减也是同样的道理。
举例如下:
xxxxxxxxxx
81int a = 10;
2
3a = 100; //普通赋值
4a += 2; //等价于a = a+2
5a -= 3; //等价于a = a-3
6a *= 4; //等价于a = a*4
7a /= 5; //等价于a = a/5
8a %= 6; //等价于a = a%6
主要有大于、小于、等于、不等于、大于等于、小于等于。比较运算表达式的结果为0或1,0代表逻辑 false ,1代表逻辑 true 。
注意:
xxxxxxxxxx
31//有个优先级问题
2int a = 10,b = 20;
3cout << (a == b) << endl; // cout << a == b << endl; 是错误的,必须在比较运算表达式外括一个括号
主要有与(&&)、或(||)、非(!)三种。
1.单行 if 语句,语法如下:
xxxxxxxxxx
41if (条件)
2{
3 要执行的语句
4}
注意的是 if(条件)
后面不要加分号,否则不管条件满足与否都会执行后面语句。
2.多行 if 语句,语法如下:
xxxxxxxxxx
81if (条件)
2{
3 条件满足下要执行的语句
4}
5else
6{
7 条件不满足要执行的语句
8}
3.多条件的 if 语句,语法如下:
xxxxxxxxxx
161if (条件1)
2{
3 条件1满足要执行的语句
4}
5else if(条件2)
6{
7 条件1不满足,但条件2满足要执行的语句
8}
9else if(条件3)
10{
11 条件1不满足、条件2不满足,但条件3满足要执行的语句
12}
13else
14{
15 以上条件都不满足要执行的语句
16}
4.此外 if 语句还支持嵌套,让条件判断更加精细
语法为:表达式1 ? 表达式2 : 表达式3;
如果表达式1的值为真,执行表达式2,并返回表达式2的结果;如果表达式1的值为假,执行表达式3,并返回表达式3的结果。在C++中三目运算符返回的是变量,可以继续赋值,举例如下:
xxxxxxxxxx
71int a = 10;
2int b = 20;
3int c = 0;
4
5c = (a > b ? a : b); //相当于c = b
6
7(a > b ? a : b) = 100; //在C++中三目运算符返回的是变量,可以继续赋值,执行后a的值为10,b的值为100
通过上面例子可以知道,三目运算符表达式既可以作左值也可以作右值。
语法如下:
xxxxxxxxxx
71switch (表达式)
2{
3 case 1:执行语句1;break;
4 case 2:执行语句2;break;
5 ……
6 default:执行语句;break;
7}
case 里面如果不写 break 程序会一直向下执行
与 if 的区别:
语法:while (循环条件) {循环语句}
只要循环条件为真,就一直执行循环内的语句,要检查是否有跳出循环的出口,除非有的时候我们确实需要死循环。
做一个猜数字的案例,随机生成一个1到100的随机数,让玩家去猜,每次猜系统要提示玩家大了小了,直到玩家猜对为止,游戏结束。
生成一个1到100的随机数,代码为:
xxxxxxxxxx
91//生成随机数必须要包含的头文件
2
3int main()
4{
5 //添加随机数种子,利用系统当前时间生成随机数
6 srand((unsigned int)time(NULL));
7 //生成1到100的随机数
8 int num = rand() % 100 + 1;
9}
语法:do {循环语句} while(循环条件);
与 while 循环语句不同的是 do while 至少保证代码执行一次。
可以写一个水仙花数案例,找出所有的水仙花数,水仙花数是指一个三位数,它各位数字的三次幂之和等于它本身的数。
语法:for (起始表达式;条件表达式;末尾循环体) {循环语句}
注意 for 循环中的表达式要用分号分隔
可以尝试打印9*9乘法表
用于
用于在循环语句中,跳过本次循环中余下的未执行语句,继续执行下一次循环。例如可以充当一个筛选条件,打印0到100之间所有的奇数。
可以无条件的跳转代码,语法是 goto 标记;
这个标记一般用大写的单词来定义,例如:FLAG。在所需要跳转到地方写一个FLAG:
然后当程序执行到 goto 语句的时候就能实现跳转。不过由于 goto 语句过于强大,不推荐使用,容易造成程序流程混乱。示例如下:
xxxxxxxxxx
111int main()
2{
3 cout << "1" << endl;
4 goto FLAG;
5 cout << "2" << endl;
6 cout << "3" << endl;
7 cout << "4" << endl;
8 FLAG:
9 cout << "5" << endl;
10 //程序运行结果是只打印1和5
11}
数组两个特点
一维数组定义的三种方式
1.数据类型 数组名[数组长度];
2.数据类型 数组名[数组长度] = {值1,值2……};
定义的时候赋初值,如果初始化的时候没有全部填写完会用0来填充剩余的数据比如:
xxxxxxxxxx
11int arr[5] = {10,20,30}; //那么arr[3]和arr[4]就都为0了
3.数据类型 数组名[] = {值1,值2……};
初始化几个值,数组长度就为几,不允许不赋值如:int arr[];
错误!
需要注意的是一旦采用第一种方式,后面就不能群体赋值了
一维数组名称的用途:
sizeof(数组名);
,返回结果为字节数。如果要看每个元素占用的大小代码为 sizeof(数组名[0]);
如果要看元素个数两个相除就可以。xxxxxxxxxx
41cout << "数组首地址为:" << arr << endl;
2cout << "数组中第一个元素的地址为:" << &arr[0] << endl; //取地址符号很重要
3
4cout << "数组首地址为:" << (int)arr << endl; //把十六进制的地址转化成十进制
数组名一旦定义就成为一个常量,不能再对其进行赋值操作。
冒泡排序是一个比较重要的排序算法,下面为升序的冒泡排序代码:
xxxxxxxxxx
181//对一个已知的数组 arr 做冒泡排序
2int i,j,temp;
3int len;
4len = sizeof(arr)/sizeof(arr[0]);
5
6//开始冒泡排序,i表示轮数,从第0轮开始一直到第(len-2)轮,第i轮需要进行(len-i-1)次比较
7for(i=0;i<len-1;i++)
8{
9 for(j=0;j<len-i-1;j++)
10 {
11 if(arr[j] > arr[j+1])
12 {
13 temp = arr[j];
14 arr[j] = arr[j+1];
15 arr[j+1] = temp;
16 }
17 }
18}
二维数组定义的四种方式
数据类型 数组名[行数][列数];
数据类型 数组名[行数][列数]={{数据1,数据2},{数据3,数据4}};
数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4};
数据类型 数组名[][列数]={数据1,数据2,数据3,数据4};
需要注意的是一旦采用第一种方式,后面就不能群体赋值了
这里比较推荐第二种定义方式,直观简单。
一维数组名称的用途:
查看二维数组所占内存空间。可以统计整个数组在内存中的大小 sizeof(数组名);
,返回结果为字节数。如果要看每一行占用的大小代码为 sizeof(数组名[0]);
,如果要看每一个元素占用内存的大小代码为sizeof(数组名[0][0])
。
计算二维数组行数代码为sizeof(数组名)/sizeof(数组名[0]);
,计算二维数组元素个数代码为 sizeof(数组名)/sizeof(数组名[0][0]);
获取二维数组首地址。示例如下:
xxxxxxxxxx
51cout << "数组首地址为:" << arr << endl;
2cout << "数组第一行的首地址为:" << arr[0] << endl;
3cout << "数组第一个元素的地址为:" << &arr[0][0] << endl;
4
5cout << "数组首地址为:" << (int)arr << endl; //把十六进制的地址转化成十进制
成绩统计,每个人有多门课程成绩,需要用到二维数组
在存储人名的时候可以用一个字符串数组,如下:
xxxxxxxxxx
31//用字符串型的数据 string 必须加的头文件
2
3string names[3] = {"张三","李四","王五"};
作用:将一段经常使用的代码封装起来,减少重复性代码。
语法如下:
xxxxxxxxxx
61返回值类型 函数名 (参数列表)
2{
3 函数体语句;
4
5 return表达式;
6}
语法为:函数名 (参数);
值传递的时候,形参发生任何改变都不会影响实参。可以从内存分配的角度来理解。
常见有四种类型:
一般情况下,自定义的函数要写在main函数的前面,如果自定义函数写在main函数之后,需要用到函数声明,语法为:
返回值类型 函数名 (参数列表);
而且声明可以写多次,但是定义只能定义一次。
步骤为:
注意:在上述的源文件中,开头要写 #include "头文件名.h"
这个就代表了头文件和源文件它俩之间是配套的(即有关联的) ;在上述的头文件中,要包含源文件用到的一些标准库,比如源文件中用到cout、cin,那么头文件开头就要写 #include<iostream>
和 using namespace std;
最后,在main函数所在的源文件上方写 #include "头文件名.h"
。
指针的作用:可以通过指针间接访问内存
指针变量定义的语法为:数据类型 *变量名;
下面为示例:
xxxxxxxxxx
101int a = 10;
2int *p; //指针的定义
3p = &a; //指针变量赋值,也可以定义时一步搞定 int *p = &a;
4
5cout << "变量a的地址为:" << &a << endl;
6cout << "指针p的值为:" << p << endl; //两个应该是同样的值
7
8*p = 20; //指针的解引用,通过*p来修改a的值,利用指针变量将a的值修改为20
9cout << "a的值为:" << a << endl;
10cout << "*p = " << *p << endl; //通过解引用的方式对变量进行修改和读取
在32位操作系统下,指针都是占4个字节大小空间,不管是什么数据类型;在64位操作系统下,指针都是占8个字节大小空间,不管是什么数据类型。
空指针作用:用于给指针变量进行初始化,空指针是不可以进行访问的,空指针NULL是0值,而0到255之间的内存编号是系统占用的,因此不可以访问。
野指针:指向非法的内存空间,示例如下:
xxxxxxxxxx
21int *p = (int *)0x1100; //将一个十六进制数强转为地址
2cout << *p << endl; //访问野指针报错
空指针和野指针都不是我们申请的内存空间,因此不要去访问。
分为以下三种:
const修饰指针 ---常量指针
定义为:const int *p = &a;
特点是:指针的指向可以修改,但是指针指向的值不可以改。*p = 20;
错误。p = &b;
正确。
const修饰常量 ---指针常量
定义为:int * const p = &a;
特点是:指针的指向不可以修改,但是指针指向的值可以改。*p = 20;
正确。p = &b;
错误。
const既修饰常量又修饰指针
定义为:const int * const p = &a;
特点是:指针的指向和其指向的值都不可以修改。*p = 20;
和p = &b;
均错误。
利用指针来访问数组中的元素,下面为用指针遍历数组的示例代码:
xxxxxxxxxx
91int arr[10] = {1,2,3,4,5,6,7,8,9,10};
2int i;
3int *p = arr;
4
5for(i=0;i<10;i++)
6{
7 cout << *p << endl;
8 p++; //每次往后偏移4个字节地址
9}
利用指针作函数参数,可以修改实参的值,示例代码如下:
xxxxxxxxxx
231void swap1(int a,int b) //值传递不会改变实参
2{
3 int temp = a;
4 a = b;
5 b = temp;
6}
7
8void swap2(int *p1,int *p2) //地址传递会改变实参
9{
10 int temp = *p1;
11 *p1 = *p2;
12 *p2 = temp;
13}
14
15int main()
16{
17 int a = 10 , b = 20;
18 swap1(a,b);
19 cout << "a和b的值分别为:" << a << b << endl; //这一步输出a和b的值并没有发生交换
20
21 swap2(&a,&b);
22 cout << "a和b的值分别为:" << a << b << endl; //这一步a和b的值就真的发生了交换
23}
这就是值传递与地址传递的不同之处。
做一个综合案例,封装一个函数,利用冒泡排序实现对一个数组的升序排序。数组为 int arr[10] = {4,3,6,9,1,2,10,8,7,5};
xxxxxxxxxx
361void bubbleSort(int *arr,int len)
2{
3 int i,j,temp;
4 for(i=0;i<len-1;i++)
5 {
6 for(j=0;j<len-i-1;j++)
7 {
8 if(arr[j] > arr[j+1])
9 {
10 temp = arr[j];
11 arr[j] = arr[j+1];
12 arr[j+1] = temp;
13 }
14 }
15 }
16}
17
18void printarr(int *arr,int len)
19{
20 for(int i=0;i<len;i++)
21 {
22 cout << arr[i] << endl;
23 }
24}
25
26int main()
27{
28 int arr[10] = {4,3,6,9,1,2,10,8,7,5};
29 int len = sizeof(arr)/sizeof(arr[0]);
30
31 bubbleSort(arr,len);
32 printarr(arr,len);
33
34 system("pause");
35 return 0;
36}
通过这个案例可以知道,当我们要向一个函数传递一个数组的时候,往往要用指针去接收它,而且为了方便一般还要传入数组的长度。
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
语法:struct 结构体名 {结构体成员列表};
通过结构体创建变量的方式有三种:
struct 结构体名 变量名;
struct 结构体名 变量名 = {成员1的初值,成员2的初值……};
xxxxxxxxxx
181struct student
2{
3 string name;
4 int age;
5 int score;
6}s3; //第三种创建方式,定义结构体时顺便创建变量,之后像第一种一样利用.来对其属性赋值
7
8int main()
9{
10 //第一种创建方式,之后想要赋值就要用.来访问其属性
11 struct student s1; //这里可以省去struct,但是在结构体定义时struct不能省
12 s1.name = "张三";
13 s1.age = 18;
14 s1.score = 90;
15
16 //第二种创建方式,创建时就赋初值
17 struct student s2 = {"李四",19,95};
18}
语法:struct 结构体名 数组名[元素个数] = {{},{}……};
结构体数组与其他数组一样,可以在创建的时候赋初值,也可以只创建,之后用.号来赋属性的值。
利用操作符->
可以通过结构体指针访问结构体属性。
xxxxxxxxxx
41struct student s = {"张三",18,90};
2struct student *p = &s;
3
4p->name = "李四"; //与其他数据类型的指针不同
在结构体中可以定义另一个结构体作为成员,形成结构体嵌套结构体。
xxxxxxxxxx
161struct teacher
2{
3 string name;
4 int age;
5 struct student stu;
6};
7
8int main()
9{
10 struct teacher t;
11 t.name = "老王";
12 t.age = "30";
13 t.stu.name = "小王";
14 t.stu.age = 18;
15 t.stu.score = 90; //利用多个.号访问作为成员的结构体的属性
16}
将结构体作为参数向函数中传递,传递方式有两种:
比如说,向子函数传一个学生的结构体,要求子函数打印学生的信息,代码如下:
xxxxxxxxxx
191void printstu1(struct student s)
2{
3 //s.name = "李四";回到main函数在,实参值不变,还为原来
4 cout << "学生姓名:" << s.name << "成绩:" << s.score << endl;
5}
6
7void printstu2(struct stduent *p)
8{
9 //p->name = "李四";回到main函数中,实参的值会真的被修改
10 cout << "学生姓名:" << p->name << "成绩:" << p->score << endl;
11}
12
13int main()
14{
15 struct student s = {"张三",18,90};
16
17 printstu1(s); //值传递
18 printstu2(&s); //地址传递
19}
值传递和地址传递的不同在于:值传递不能改变实参的值,而地址传递可以。
作用:用 const 来限定只读操作,防止误修改
应用场景的产生:在向子函数传递一个结构体时,如果采用值传递,那么会在子函数中复制出一个新的副本,当结构体十分庞大时会占用比较多的内存资源,所以我们采用地址传递(只传入地址4个字节节省内存空间),但是地址传递可以修改实参的值,容易造成误修改。此时就有了使用 const 的必要 ,加入 const 之后一旦有修改的操作就会报错,可以防止误操作。
xxxxxxxxxx
121void printstu(const struct student *p)
2{
3 //p->age = 20; 加了const后这句代码就会报错,从而避免了实参值的误修改
4 cout << p->name << p->age << p->score << endl;
5}
6
7int main()
8{
9 struct student s = {"张三",18,90};
10
11 printstu(&s);
12}