基本语法:
cin >> a; cout << "文字提示" << 变量或表达式 << endl;这里 endl 相当于回车在使用 cin 和 cout 时,要在源文件最上方加上 #include <iostream>和 using namespace std;
分为单行注释和多行注释:
///* */定义为数据类型+变量名+赋初值
定义方式有两种
x12
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。前置递减和后置递减也是同样的道理。
举例如下:
xxxxxxxxxx81int a = 10;2
3a = 100; //普通赋值4a += 2; //等价于a = a+25a -= 3; //等价于a = a-36a *= 4; //等价于a = a*47a /= 5; //等价于a = a/58a %= 6; //等价于a = a%6主要有大于、小于、等于、不等于、大于等于、小于等于。比较运算表达式的结果为0或1,0代表逻辑 false ,1代表逻辑 true 。
注意:
xxxxxxxxxx31//有个优先级问题2int a = 10,b = 20;3cout << (a == b) << endl; // cout << a == b << endl; 是错误的,必须在比较运算表达式外括一个括号主要有与(&&)、或(||)、非(!)三种。
1.单行 if 语句,语法如下:
xxxxxxxxxx41if (条件)2{3 要执行的语句4}注意的是 if(条件) 后面不要加分号,否则不管条件满足与否都会执行后面语句。
2.多行 if 语句,语法如下:
xxxxxxxxxx81if (条件)2{3 条件满足下要执行的语句4}5else6{7 条件不满足要执行的语句8}3.多条件的 if 语句,语法如下:
xxxxxxxxxx161if (条件1)2{3 条件1满足要执行的语句4}5else if(条件2)6{7 条件1不满足,但条件2满足要执行的语句8}9else if(条件3)10{11 条件1不满足、条件2不满足,但条件3满足要执行的语句12}13else14{15 以上条件都不满足要执行的语句16}4.此外 if 语句还支持嵌套,让条件判断更加精细
语法为:表达式1 ? 表达式2 : 表达式3;
如果表达式1的值为真,执行表达式2,并返回表达式2的结果;如果表达式1的值为假,执行表达式3,并返回表达式3的结果。在C++中三目运算符返回的是变量,可以继续赋值,举例如下:
xxxxxxxxxx71int a = 10;2int b = 20;3int c = 0;4
5c = (a > b ? a : b); //相当于c = b6
7(a > b ? a : b) = 100; //在C++中三目运算符返回的是变量,可以继续赋值,执行后a的值为10,b的值为100通过上面例子可以知道,三目运算符表达式既可以作左值也可以作右值。
语法如下:
xxxxxxxxxx71switch (表达式)2{3 case 1:执行语句1;break;4 case 2:执行语句2;break;5 ……6 default:执行语句;break;7}case 里面如果不写 break 程序会一直向下执行
与 if 的区别:
语法:while (循环条件) {循环语句}
只要循环条件为真,就一直执行循环内的语句,要检查是否有跳出循环的出口,除非有的时候我们确实需要死循环。
做一个猜数字的案例,随机生成一个1到100的随机数,让玩家去猜,每次猜系统要提示玩家大了小了,直到玩家猜对为止,游戏结束。
生成一个1到100的随机数,代码为:
xxxxxxxxxx91//生成随机数必须要包含的头文件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 语句过于强大,不推荐使用,容易造成程序流程混乱。示例如下:
xxxxxxxxxx111int 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和511}数组两个特点
一维数组定义的三种方式
1.数据类型 数组名[数组长度];
2.数据类型 数组名[数组长度] = {值1,值2……}; 定义的时候赋初值,如果初始化的时候没有全部填写完会用0来填充剩余的数据比如:
xxxxxxxxxx11int arr[5] = {10,20,30}; //那么arr[3]和arr[4]就都为0了3.数据类型 数组名[] = {值1,值2……};初始化几个值,数组长度就为几,不允许不赋值如:int arr[];错误!
需要注意的是一旦采用第一种方式,后面就不能群体赋值了
一维数组名称的用途:
sizeof(数组名);,返回结果为字节数。如果要看每个元素占用的大小代码为 sizeof(数组名[0]);如果要看元素个数两个相除就可以。xxxxxxxxxx41cout << "数组首地址为:" << arr << endl;2cout << "数组中第一个元素的地址为:" << &arr[0] << endl; //取地址符号很重要3
4cout << "数组首地址为:" << (int)arr << endl; //把十六进制的地址转化成十进制数组名一旦定义就成为一个常量,不能再对其进行赋值操作。
冒泡排序是一个比较重要的排序算法,下面为升序的冒泡排序代码:
xxxxxxxxxx181//对一个已知的数组 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]);
获取二维数组首地址。示例如下:
xxxxxxxxxx51cout << "数组首地址为:" << arr << endl;2cout << "数组第一行的首地址为:" << arr[0] << endl;3cout << "数组第一个元素的地址为:" << &arr[0][0] << endl;4
5cout << "数组首地址为:" << (int)arr << endl; //把十六进制的地址转化成十进制成绩统计,每个人有多门课程成绩,需要用到二维数组
在存储人名的时候可以用一个字符串数组,如下:
xxxxxxxxxx31//用字符串型的数据 string 必须加的头文件2
3string names[3] = {"张三","李四","王五"};作用:将一段经常使用的代码封装起来,减少重复性代码。
语法如下:
xxxxxxxxxx61返回值类型 函数名 (参数列表)2{3 函数体语句;4 5 return表达式;6}语法为:函数名 (参数);
值传递的时候,形参发生任何改变都不会影响实参。可以从内存分配的角度来理解。
常见有四种类型:
一般情况下,自定义的函数要写在main函数的前面,如果自定义函数写在main函数之后,需要用到函数声明,语法为:
返回值类型 函数名 (参数列表);而且声明可以写多次,但是定义只能定义一次。
步骤为:
注意:在上述的源文件中,开头要写 #include "头文件名.h"这个就代表了头文件和源文件它俩之间是配套的(即有关联的) ;在上述的头文件中,要包含源文件用到的一些标准库,比如源文件中用到cout、cin,那么头文件开头就要写 #include<iostream>和 using namespace std; 最后,在main函数所在的源文件上方写 #include "头文件名.h" 。
指针的作用:可以通过指针间接访问内存
指针变量定义的语法为:数据类型 *变量名;下面为示例:
xxxxxxxxxx101int 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的值修改为209cout << "a的值为:" << a << endl;10cout << "*p = " << *p << endl; //通过解引用的方式对变量进行修改和读取在32位操作系统下,指针都是占4个字节大小空间,不管是什么数据类型;在64位操作系统下,指针都是占8个字节大小空间,不管是什么数据类型。
空指针作用:用于给指针变量进行初始化,空指针是不可以进行访问的,空指针NULL是0值,而0到255之间的内存编号是系统占用的,因此不可以访问。
野指针:指向非法的内存空间,示例如下:
xxxxxxxxxx21int *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; 均错误。
利用指针来访问数组中的元素,下面为用指针遍历数组的示例代码:
xxxxxxxxxx91int 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}利用指针作函数参数,可以修改实参的值,示例代码如下:
xxxxxxxxxx231void 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};
xxxxxxxxxx361void 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的初值……};xxxxxxxxxx181struct student2{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 结构体名 数组名[元素个数] = {{},{}……};
结构体数组与其他数组一样,可以在创建的时候赋初值,也可以只创建,之后用.号来赋属性的值。
利用操作符->可以通过结构体指针访问结构体属性。
xxxxxxxxxx41struct student s = {"张三",18,90};2struct student *p = &s;3
4p->name = "李四"; //与其他数据类型的指针不同在结构体中可以定义另一个结构体作为成员,形成结构体嵌套结构体。
xxxxxxxxxx161struct teacher2{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}将结构体作为参数向函数中传递,传递方式有两种:
比如说,向子函数传一个学生的结构体,要求子函数打印学生的信息,代码如下:
xxxxxxxxxx191void 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 之后一旦有修改的操作就会报错,可以防止误操作。
xxxxxxxxxx121void 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}