[@TOC]
一、 C++基础
C++的IDE有CLion、Visual Studio、DEV C++、eclipse等等,这里使用CLion 进行学习。
0. C++初识
0.1 第一个C++程序
编写一个C++程序总共分为4个步骤
#include <iostream>
int main()
{
using namespace std;
cout << "Come up and C++ me some time.";
cout << endl;
cout << "You won't regret it!" << endl;
return 0;
}
C语言中省略int只写main()相当于函数的类型为int,相当于int main(),C++中不建议省略 C++中int main()等价于int main(void) C++中main函数可以省略return 0;
0.2 注释
作用 :在代码中加一些说明和解释,方便阅读代码
两种格式
单行注释 ://注释
通常放在一行代码的上方或者一条语句的末尾,对该行代码进行说明 多行注释 :/*多行注释*/
编译器在编译代码时会忽略注释的内容
0.3 变量
作用 :给一段指定的内存空间起名,方便操作这段内存
语法 :数据类型 变量名 = 初始值;
变量声明的语法
typename variablename;
int year;
初始化
//C++98中可以使用初始化数组的方式初始化变量
int num = {10};
//C++11中这种用法更常见,也可以不使用=
int num{10};
//{ }也可以不包含东西
int num{};//此时变量的值初始化为0
在C++11中可以将 { }大括号初始化器用于任何类型({}前面可以使用=,也可以不使用=),这是一种通用的初始化语法
0.3常量
作用 :用于记录程序中不可更改的数据
C++定义常量的两种方式
#define 宏常量:#define 常量名 常量值
const 修饰的变量const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量,不可修改
exp:
//1、宏常量
#define day = 7;
int main()
{
cout<<"一周总共有"<<day<<"天"<<endl;
//day = 8;会报错
//2. const修饰变量
const int month = 12;
cout<<"一年总共有"<<month<<"个月"<<endl;
//month = 24;报错,常量不可更改
}
0.4 关键字
作用 :关键字时C++中预先保留的标识符
C++关键字如下:
asm do if return typedef auto double inline short typeid bool dynamic_cast int signed typename break else long sizeof union case enum mutable static unsigned catch explicit namespace static_cast using char export new struct virtual class extern operator switch void const false private template volatile const_cast float protected this wchar_t continue for public throw while default friend register true delete goto reinterpret_cast try
提示:在给变量或者常量起名称时候,不要用C++得关键字,否则会产生歧义。
0.5 标识符命名规则
作用 :C++规定给变量和常量命名有一定的规则
标识符不能是关键字 标识符只能由字母、数字、下划线组成 第一个字符必须为字母或下划线 标识符中字母区分大小写
建议:给标识符命名时,争取做到见名知意的效果,方便自己和他人的阅读
1. 数据类型和运算符
1.1 数据类型
创建一个变量或者常量时必须指出相应的数据类型,否则无法给变量分配内存空间
数据类型存在的意义是方便编译器分配空间
1.1.1 整型
变量类型 所占字节大小 short 至少16位 64位windows下sizeof short = 2字节=16位,同下 int 至少与short一样长 sizeof int = 4,32位 long 至少32位且至少与int一样长 sizeof long = 4,32位 long long 至少64位且至少与long一样长 sizeof long long = 8,64位
无符号类型:
在变量类型前加unsigned可以使之称为无符号类型的变量,如果short类型变量所占字节大小为2,所占位数16位,则第一位用于表示变量的正负,则可用位数为15位,能表示的范围为-215~2 15,如果添加了unsigned关键字,则无符号位,可以表示的数据大小为0~2^16,无负数。
unsigned是unsigned int的缩写
无符号类型的优点是可以增大变量能够存储的范围(仅当数值不为负时)
一个整型数据在不设置数据类型的情况下默认为int型,如果不想使用int可以通过设置后缀可以为一个值确定数据类型
unsigned可以用u表示
long可以用L表示
unsigned和long的后缀可以叠加使用:
ul表示unsigned long
ull表示unsigned long long
不区分大小写
查看数据类型所占空间大小使用sizeof关键字
int main() {
cout << "short 类型所占内存空间为: " << sizeof(short) << endl;
cout << "int 类型所占内存空间为: " << sizeof(int) << endl;
cout << "long 类型所占内存空间为: " << sizeof(long) << endl;
cout << "long long 类型所占内存空间为: " << sizeof(long long) << endl;
system("pause");
return 0;
}
C++中进制的表示
不管数据以几进制的形式表现出来,在内存中都是以二进制进行存储
C++中以四种进制进行输出
# include <iostream>
# include <bitset>
using namespace std;
int main ( )
{
int a= 64 ;
cout<< ( bitset< 32 > ) a<< endl;
cout<< oct<< a<< endl;
cout<< dec<< a<< endl;
cout<< hex<< a<< endl;
return 0 ;
}
1.1.2浮点型(实型)
浮点数能表示带小数的数字
浮点数有两种表示方法:
标准小数点表示法
exp:
3.14
12.3
科学计数法
exp:
3.45e6
10.12E-6
5.98E+10
同科学计数E、e(不区分大小写),表示10的多少次方
int main()
{
float f1 = 3.14f;
double d1 = 3.14;
cout<<f1<<endl;
cout<<d1<<endl;
cout<<"float sizeof = "<<sizeof(f1)<<endl;
cout<<"double sizeof = "<<sizeof(d1)<<endl;
//科学计数法
float f2 = 3e2;//3*10^2
cout<<"f2 = "<<f2<<endl;
float f3 = 3e-2;//3*0.1^2
cout<<"f3 = "<<f3<<endl;
}
3种浮点类型:
float 至少32位64位windows中:sizeof float = 4字节32位,7位有效数字 double 至少48位sizeof double = 8,64位,15~16位有效数字 long double sizeof long double = 16,128位
默认情况下浮点数是double类型的,如果想使用float类型,可以在值后面加上后缀F或者f
使用long double类型可以在值后面加上L或者l,因为l与1不好分辨,建议使用L
1.1.3 字符型
作用 :字符型变量用于显示单个字符
语法 :char ch = 'a';
使用单引号 单引号内只能有一个字符
char类型常用于表示字符与小整数,大小为1个字节 ,表示范围-128~127
char类型也可以使用unsigned关键字修饰,unsigned char表示的范围是0~255
char型使用单引号 ’ ’ 括起来
exp:
char ch = 'a';
字符型变量并不是把字符本身放到内存中,而是将对应的ASCII编码放入存储单元
ASCII码表格:
ASCII 值控制字符 ASCII 值字符 ASCII 值字符 ASCII 值字符 0 NUT 32 (space) 64 @ 96 、 1 SOH 33 ! 65 A 97 a 2 STX 34 " 66 B 98 b 3 ETX 35 # 67 C 99 c 4 EOT 36 $ 68 D 100 d 5 ENQ 37 % 69 E 101 e 6 ACK 38 & 70 F 102 f 7 BEL 39 , 71 G 103 g 8 BS 40 ( 72 H 104 h 9 HT 41 ) 73 I 105 i 10 LF 42 * 74 J 106 j 11 VT 43 + 75 K 107 k 12 FF 44 , 76 L 108 l 13 CR 45 - 77 M 109 m 14 SO 46 . 78 N 110 n 15 SI 47 / 79 O 111 o 16 DLE 48 0 80 P 112 p 17 DCI 49 1 81 Q 113 q 18 DC2 50 2 82 R 114 r 19 DC3 51 3 83 S 115 s 20 DC4 52 4 84 T 116 t 21 NAK 53 5 85 U 117 u 22 SYN 54 6 86 V 118 v 23 TB 55 7 87 W 119 w 24 CAN 56 8 88 X 120 x 25 EM 57 9 89 Y 121 y 26 SUB 58 : 90 Z 122 z 27 ESC 59 ; 91 [ 123 { 28 FS 60 < 92 / 124 | 29 GS 61 = 93 ] 125 } 30 RS 62 > 94 ^ 126 ` 31 US 63 ? 95 _ 127 DEL
ASCII 码大致由以下两部分组 成:
ASCII 非打印控制字符: ASCII 表上的数字 0-31 分配给了控制字符,用于控制像打印机等一些外围设备。 ASCII 打印字符:数字 32-126 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。
转义字符
作用 :用于表示一些不能显示出来的ASCII字符
exp:
转义字符 含义 ASCII 码值(十进制)\a 警报 007 \b 退格(BS) ,将当前位置移到前一列 008 \f 换页(FF),将当前位置移到下页开头 012 \n 换行(LF) ,将当前位置移到下一行开头 010 \r 回车(CR) ,将当前位置移到本行开头 013 \t 水平制表(HT) (跳到下一个TAB位置) 009 \v 垂直制表(VT) 011 \\ 代表一个反斜线字符"" 092 ’ 代表一个单引号(撇号)字符 039 " 代表一个双引号字符 034 ? 代表一个问号 063 \0 数字0 000 \ddd 8进制转义字符,d范围0~7 3位8进制 \xhh 16进制转义字符,h范围09,a f,A~F 3位16进制
示例:
int main()
{
cout<<"\\"<<endl;
cout<<"\tHello"<<endl;
cout<<"\n"<<endl;
}
1.1.4 布尔型 bool
作用 :布尔数据类型代表真或假的值
语法 :bool x;
布尔型有两个值:
true —真(本质是1) false —假(本质是0)
占用一个字节
exp:
int main()
{
bool flag = true;
cout<<flag<<endl;//1
flag = false;
cout<<flag<<endl;//0
cout<<"sizeof bool = "sizeof(bool)<<endl;
}
1.2 类型转换
三种类型转换:
初始化和赋值时进行的转换 表达式中包含不同的类型时进行的转换 参数传递给函数时进行的转换
1.2.1. 初始化类型转换
C++允许将一种类型的值赋给另一种类型的变量,这时值会自动转换为变量的类型
exp:
int a = 1.23;//double赋给int,结果转换为int,值为1
float a = 1;//int赋给float,结果转换为float 值为1.0
将表示范围小的类型值赋给表示范围大的类型没有问题,将表示范围大的类型的值赋给表示范围小的类型时,会出现失真
2. 表达式中的转换
当表达式中存在多种类型时,一些类型会自动进行类型转换,一些类型在遇到某些类型时才进行转换。
整型提升:bool、unsigned char、char、short会自动转换为int
当表达式中存在表示范围比int大的类型时,较小的类型会转换为较大的类型。
3. 传递参数时
传递参数时对char、short进行整型提升
强制类型转换
语法:
(typename)value
来自C语言的用法typename(value)
来自C++的用法,使其更像是函数的用法static_cast<typename> (value)
C++中的强制转换符
exp:
(int)1.5;//C语言中的方式
int(1.5);//C++中的方式
在C++中使用typeid(typename).name()查看数据类型名称
exp:
cout<<typeid(int).name();//使用类型名查看
int a;
cout<<typeid(a).name();//使用变量名
C++中的缩窄转换
C++中的列表初始化(使用{}进行初始化)不允许缩窄转换(narrawing convertions),即变量的类型无法表示赋给它的值。
从浮点 数转换为整数 从取值范围大的浮点数转换为取值范围小的浮点数(在编译期可以计算并且不会溢出的表达式 除外) 从整数转换为浮点数(在编译期可以计算并且转换之后值不变的表达式除外) 从取值范围大的整数转换为取值范围小的整数(在编译期可以计算并且不会溢出的表达式除外)
C++中的auto声明
auto是C++11中根据初始值类型推断变量的类型的关键字
作用:在初始化声明中如果使用auto,而不指定变量的类型,编译器会把变量的类型设置成与初始值相同
常用于STL中
exp:
vector<double> scores;
vector<double>::iterator v = scores.begin();
//可以简写为
auto v = scores.begin();
算术运算符
加法:+
减法:-
乘法: *
除法:/
除法分为:
即使使用float接收参数结果也是取整的,因为运算符进行运算时就已经根据运算规则进行了取整
C:
float a = 9/2;
cout<<a<<endl;
结果:
4
根据运算规则,操作数会自动类型转换,int转float再运算
求模:%
算术运算符的优先级
递增递减运算符
组合赋值运算符
关系运算符
关系运算符不能用于C风格的字符串,可以用于string类型的字符串,对于关系表达式,如果判断结果为真,则返回true,如果为假返回false,对于C风格的字符串,如果使用 == 运算符进行判断则判断两者的地址是否相等。如果要判断内容是否相等,可以使用strcmp()函数,该函数接收两个字符串地址作为参数。
< 小于 <= 小于等于 > 大于 >= 大于等于 == 等于 != 不等于
2. 数组
数组的声明和初始化
数组的声明 :typename arrayname[arraySize];
exp:
int array[10];
数组的初始化:
使用逗号分隔的值列表(初始化列表):{}
int array[3] = {1,2,3};
使用数组下标为数组元素赋值
int array[3];
array[0] = 0;
array[1] = 1;
array[2] = 2;
如果初始化数组时 [ ] 中为空则编译器自动计算元素个数
int array[] = {0};
注意事项:
C++11数组初始化
C++11中新增列表初始化( { } )的方式
可以不加 = 不允许缩窄转换 可以在大括号内不包含任何东西,所有的值初始化为0
exp:
int array[] {};
二维数组
二维数组的定义方式:
int arr[][4] = {
{1,2,3},
{4,5,6},
{7,8,9},
{10,11,12}
};
int arr2[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
两个中括号必须有一个有值,否则编译器无法确定二维数组的排列方式
二维数组在内存空间中的排列方式与一维数组一致,都是连续的内存空间,编译器将其分为几个一维数组并且每个一维数组中的元素又包含了一个一维数组
在一维数组中数组名是指向数组的起始地址的指针 ,也是第1个元素的地址,在二维数组中同理,数组名arr=&arr[0]
,因为arr[0]
也是一个一维数组,所以数组名也等于&arr[0][0]
,但本质上还是arr[0][0]
的地址。因为对arr
解引用两次才能取到值
使用指针的方式访问数组中的元素:
*(*(arr+1)+1)取的是arr[1][1]的值
arr:arr是一个指针,指针存放的是arr[0]的地址
arr+1:指针+1表示指针指向的地址向后移动1 * sizeof(typename)位
*(arr+1):指针解引用出来是arr[1]这个地址,同时arr[1]也是一个一维数组,arr[1]相当于数组名,也是个指针,指向arr[1]这个数组的第一个元素arr[1][0]
*(arr+1)+1:arr[1]这个数组所指向的地址arr[1][0]向后移动1 * sizeof(typename)位指向arr[1][1]这个地址
*(*(arr+1)+1):将指向arr[1][1]这个地址的指针解引用得到arr[1][1]这个地址中的值
字符串
字符串是一个或多个字符的序列,是用双引号括起来的。
exp:
"Today is Thursday!"
C语言中没有指定存储字符串的基本数据类型,C语言中的字符串都是由char类型的数组组成的,字符串的末尾一定是以’\0’结束的,因此数组的容量必须比字符的数量多1
字符串有两种声明方式:
使用char数组,C语言的方式 使用string类,C++的方式
C风格的字符串:
char str[] = "xxx";
char str[] = {'a','b','c'};
char *str = "abcd";
因为C风格的字符串本质是一个数组,所以除了使用引号的方式进行声明之外,还可以使用数组的初始化方式进行初始化。
C风格的字符串与char数组的区别是字符串有内置的结束字符 '\0'
C++风格的字符串:
string str = "xxx";
C++中使用string类需要包含string头文件,头文件iostream中已经隐式的包含了这个头文件。
string类在命名空间std中
string类相比于char数组有很多优势,比如:
可以将一个字符串赋值给另一个字符串 可以使用+,=,+=,==等操作符 不用考虑数组内存不足的问题
String 类型对象包括三种求解字符串长度的函数:size() 和 length()、 maxsize() 和 capacity():
size() 和 length():这两个函数会返回 string 类型对象中的字符个数,且它们的执行效果相同。 max_size():max_size() 函数返回 string 类型对象最多包含的字符数。一旦程序使用长度超过 max_size() 的 string 操作,编译器会拋出 length_error 异常。max_size = 4611686018427387903 capacity():该函数返回在重新分配内存之前,string 类型对象所能包含的最大字符数
3. 结构体
语法:
struct stname{
typename1 variable1;
typename2 variable2;
};
stname s {value1,value2};//C++中的初始化列表
cout<<s.variable1<<endl;//使用成员运算符 . 访问结构体元素
C++中初始化结构体时可以省略struct关键字
而c语言中需要加struct关键字
常用typedef关键字与结构体一起使用
typedef struct student{
string name;
int age;
}stu;//给student这个结构体起一个别名
结构体的内存对齐
结构体成员的内部起始地址能够被其所占字节的大小整除
结构体的大小是最大成员所占字节的整数倍
对于结构体中的结构体要按照结构体展开之后的内存对齐来处理
人为制定规则,#pragma pack(n)
按照n进行对齐,覆盖第一条规则,如果n比原来的规则大,则用小的规则
# pragma pack ( 2 )
struct test {
char a;
int b;
char c;
} ;
不内存对齐 :用于单片机等
#pragma pack(1)
结构体数组
声明:
stname s[] = {
{value1,value2},
{v1,v2}
};
结构体指针&结构体数组&结构体指针数组
# include <iostream>
using namespace std;
struct pstruct
{
int a;
double b;
} ;
int main ( )
{
pstruct s1 = { 1 , 3.14 } ;
pstruct s2 = { 2 , 3.14 } ;
pstruct * ps1 = & s1;
pstruct * ps2 = & s2;
pstruct sarr[ 2 ] = { s1, s2} ;
pstruct * sparr[ 2 ] = { ps1, ps2} ;
}
}
4.共用体(union)
共用体与结构体类似,能存储多种数据类型,但是同时只能存一种数据类型。
union u{
int i_value;
float f_value;
double d_value;
};
u u1{};
u1.d_value=3.14;
共用体常用于节省内存
5. 枚举
枚举
enum spectrum {red,black,green,blue};
6. 指针
指针是一种变量,指针中存储的值为地址。
指针的声明:
typename* p;
exp:
int *p;//C语言中常用的格式
int* p;//C++中常用的格式
int * p;//
int*p;//这几种格式都可以
int* p1,p2;//注意: p2的类型是int,对于每个指针变量名都需要使用一个*
int的意思是 指针是指向int类型数据的指针
指针的初始化:
int* p = 地址;//因为指针中存储的是地址,所以初始化需要给指针一个地址,可以使用 &变量 的方式,也可以使用数组名等其他方式,总之需要是一个地址。如果不为其赋初值,最好使其指向NULL,防止野指针。
指针中处理存储数据的策略是解引用,*运算符被称为间接值(indirect value)或解除引用(dereferencing)运算符,将其放在指针变量的前面可以获得指针指向的地址中存储的值
指针常量和常量指针
左定值,右定向
const在*左边值是常数,const在*右边指针的指向是固定的
指针常量:
指针常量的含义是指针类型的常量 ,指针常量中指针自身的值是一个常量,即指针指向的地址是一个常量,不可修改,在定义时必须初始化
int a = 10,b = 20;
int* const p = &a;
cout<<"a = "<<a<<endl;
cout<<"*p = "<<*p<<endl;
//p = &b;报错:cannot assign to variable 'p' with const-qualified type 'int *const'不能复制给具有const属性的变量p
*p = 20;//可以修改地址指向的值但是不可以修改地址
cout<<"修改后*p = "<<*p<<endl;
a = 10 p = 10 修改后 p = 20
可以修改指针指向的值,不可以修改地址
const在 p 前就是修饰p,p是地址,所以地址不可以改
常量指针:
常量指针的含义是指向常量的指针
int a = 10,b = 20;
const int *p = &a;
cout<<"*p = "<<*p<<endl;
p = &b;//可以修改指针的指向
//*p = 20;报错:Read-only variable is not assignable 只读变量不可赋值
const修饰符
const限定符用于将变量变为常量
初始化时一定要赋值
const int c1 = sum(1,2);//运行时初始化
const int c2 = c1;
const int c3 = 1;//编译时初始化
const int c4;//不正确
const与引用
const int num = 10;
int &c1 = num;//报错,因为引用可以修改变量值,但是这是个常量,不能修改,需要加const修饰
const int &c1 = num;
double pi = 3.14;
const int &c1 = pi;
实际上发生了自动类型转换
int temp = pi;
const int &c1 = temp;
extern关键字
作用:当在一个文件中声明了一个变量而想在所有文件中使用的时候需要在变量定义前加extern关键字
new操作符
C++中利用new在堆区开辟数据,利用delete释放 语法:new 数据类型
利用new创建的数据会返回改数据对应类型的指针
# include <iostream>
using namespace std;
int * func ( )
{
int * p = new int ( 10 ) ;
return p;
}
int main ( )
{
int * p = func ( ) ;
cout<< * p << endl;
cout<< * p << endl;
cout<< * p << endl;
delete p;
cout<< * p<< endl;
system ( "pause" ) ;
}
使用delete只能删除使用new产生的内存
在堆区new一个数组
# include <iostream>
using namespace std;
int * func ( )
{
int * arr = new int [ 10 ] ;
return arr;
}
int main ( )
{
int * arr = func ( ) ;
cout<< arr[ 0 ] << endl;
for ( int i= 0 ; i< 10 ; i++ )
{
arr[ i] = i+ 1 ;
}
for ( int i= 0 ; i< 10 ; i++ )
{
cout<< arr[ i] << endl;
}
delete[ ] arr;
arr = NULL ;
}
释放数组对象时使用delete[]
在C++中数组名表示的是地址
指针指向的数组的访问
直接使用指针名[number]即可访问,不需要*指针名
指针名+1表示指针指向数组下一个元素,解引用*(p+1)表示下一个值
在数组中数组名和指针的区别:
数组名是常量而指针是变量
7. 循环和关系表达式
for循环
for(int i=0;i<5;i++)
{
cout<<"第"<<i<<"次输出"<<endl;
}
程序执行的顺序:
设置初始值,初始化循环变量 i 执行测试,判断 i 是否符合要求 执行循环体内容,打印输出 更新用于测试的值, i++
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char ch[] = "abcd";
for(int i=0, j=strlen(ch)-1; i<j; ++i,--j)
{
char temp;
temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
for(int i=0;i<4;i++)
{
cout<<ch[i]<<endl;
}
}
基于范围的for循环(C++11)(ranged-base)
C++11中引入的新特性,可以遍历容器或者其他序列的所有元素
语法:for(声明:表达式){语句}
声明建议使用auto
进行自动类型推断,如果要修改值可以使用引用的方式
double arr = {12.1,3.14,5.23,3.56};
for(double x:arr)
{
cout<<x<<endl;
}
for(double &x:arr)//引用的方式
{
cout<<x<<endl;
}
while循环
while(条件表达式)
{
循环体;
}
do while循环
do…while与while的区别是不管表达式是否成立do while至少执行一次
do
{
循环体;
}
while(条件表达式);
do while循环至少执行一次
8. 分支语句和逻辑运算符
语法:
if(条件表达式)
{
语句;
}
if-else语句:
if(条件表达式)
{
语句1;
}
else
{
语句2;
}
if else if else语句:
if(条件表达式)
{
语句1;
}
else
if(条件表达式2)
{
语句2;
}
else
if(条件表达式3)
{
语句3;
}
else
{
语句4;
}
其实是多个if else的嵌套,一般写为下面这种形式
if(条件表达式)
{
语句1;
}
else if(条件表达式2)
{
语句2;
}
else if(条件表达式3)
{
语句3;
}
else
{
语句4;
}
逻辑表达式
逻辑或 || ,||两边有一个或者都为真时返回真,可以使用or替代
用法:
5>3||5<4
因为||为顺序点,所以运算符左边的表达式优先于右边的表达式
i++ < 6 || i == j;
假设 i 的值为10,则在 i 与 j 进行比较时,i 的值为11
||左边表达式为真时右边的表达式不会执行
#include <iostream>
using namespace std;
int main()
{
1||cout<<"||后边不执行"<<endl;
0||cout<<"||后边执行"<<endl;
}
//结果输出第二句
**逻辑与&&**可以使用and替代
两侧的表达式都为真则返回真
第一个为假则直接返回假,不再判断右侧表达式
用于确定取值范围:
if(age>17&&age<35)
{
...
}
逻辑非 也可以使用not替代
!将后面的表达式真值取反
总结:
&&和||优先级低于所有关系运算符和算术运算符
!的优先级高于所有关系运算符和算术运算符
顺序点
顺序点的解释
C++中的顺序点有以下几个:
1)分号;
2)未重载的逗号运算符的左操作数赋值之后(即’,'处)
3)未重载的’||‘运算符的左操作数赋值之后(即’||'处);
4)未重载的’&&'运算符的左操作数赋值之后(即"&&"处);
5)三元运算符’? : ‘的左操作数赋值之后(即’?'处);
6)在函数所有参数赋值之后但在函数第一条语句执行之前;
7)在函数返回值已拷贝给调用者之后但在该函数之外的代码执行之前;
8)每个基类和成员初始化之后;
9)在每一个完整的变量声明处有一个顺序点,例如int i, j;中逗号和分号处分别有一个顺序点;
10)for循环控制条件中的两个分号处各有一个顺序点。
尽量保证在两个相邻顺序点之间同一个变量不可以被修改两次以上或者同时有读取和修改,否则,就会产生未定义(无法预测)的行为。
字符函数库cctype
头文件cctype
作用:用于确定字符是不是大写字母,数字,标点等工作,返回值为int ,但也可以当做bool类型用
包含的函数:
函数 作用 isalpha(char) 判断是否是字母 ispunct(char) 判断是否是标点符号 isdigit(char) 判断是否是数字 isspace(char) 判断是否是空白 isalnum(char) 字母或数字 iscntrl(char) 是否是控制字符 isgraph(char) 是否是除空白以外的字符 isupper(char) 是否是大写字母 islower(char) 是否是小写字母 toupper(char) 如果是小写,返回大写形式,否则返回该参数 isxdigit(char) 是否是16进制 tolower(char) 如果是大写返回小写形式,否则返回该参数
三元运算符( ? : )
语法:expression1 ? expression2 : expression3
如果expression1表达式结果为true,整个表达式的值为expression2的值,否则为exoression3的值
switch语句
switch (inter-expression)//inter-expression是一个返回值为整型的表达式
{
case 1:
expression1;
break;
case 2:
expression2;
break;
default:
expression3;
}
switch语句和if else 语句的作用一样,但是switch语句的效率更高一点,当选项超过3个时,优先选用if else
9. 文件读写
写文件
使用文件读写的主要步骤:
包含头文件fstream 创建一个ofstream对象 将这个ofstream同一个文件关联起来 像使用cout那样使用ofstream
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream of;//创建一个输出流对象
of.open("test.txt");//与文件关联起来
if(of.is_open())
{
of<<"这句话会输出到文件中";
of.close;//关闭文件
}
}
创建的文件对象和cout用法一样,对象的方法也一样,setf(), precision()
运行程序之前,要绑定的文件不存在则自动创建这个文件,如果存在,则覆盖写入 在写文件之前还要判断文件是否打开
读文件
方法与写文件类似使用 istream 创建对象
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
string s;
ifstream ifs;
ifs.open("test.txt");
if(!ifs.is_open())
{
exit(-1);//如果文件打开失败,退出程序
}
else
{
ifs>>s;//读取文件内容给字符串s
cout<<s<<endl;//输出s
ifs.close();//关闭文件
}
}
exit(-1)用于退出程序
I/O操作中的方法:
方法 作用 open(文件名,) 将文件对象绑定文件 is_open() 判断文件是否打开 eof() 判断是否读取到文件尾,是返回true fail() 如果最后一次文件读取发生了类型不匹配和读取到文件尾,则返回true bad() 如果文件受损或硬件问题,则返回true good() 该方法在没有任何问题的情况下返回true
10. 函数
函数的作用是将一段经常使用的代码封装起来,减少重复代码,一个较大的程序一般分为若干块,每个模块实现特定的功能
函数的定义 :
函数的定义一般主要有5个步骤:
1、返回值类型
2、函数名
3、参数表列
4、函数体语句
5、return 表达式
语法:
返回值类型 函数名 (参数列表)
{
函数体语句
return表达式
}
返回值类型 :一个函数可以返回一个值。在函数定义中 函数名:给函数起个名称 参数列表:使用该函数时,传入的数据 函数体语句:花括号内的代码,函数内需要执行的语句 return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据
函数原型语句 (函数的声明):
作用 :
在C++使用函数时,C++编译器需要知道这个函数的参数类型和返回值类型,C++中使用函数原形来提供这些信息。 当函数写在main函数之后时,编译器从上往下执行,在main函数中又调用了写的函数,编译器又找不到函数的实现,这时需要在main函数之前写一个函数原型,告诉编译器有这个函数
例如:
double sqrt(double);
函数的声明可以多次,但是定义只能有一次
默认参数
作用:在调用函数时可以不传参,使用默认的参数。
语法:int func(const char *, int n = 1);
如果有多个参数需要默认参数,默认参数必须从右往左进行赋值 ,实参按照从左到右的方式传参,但是不能少传参
func(1, ,2);
这是不允许的
占位参数
语法:
#include <iostream>
using namespace std;
void func(int a;int )
{
cout<<"占位参数"<<endl;
}
void func2(int a=10, int = 10)//占位参数也有默认值
{
cout<<"占位参数2"<<endl;
}
int main()
{
func(10,20);//占位参数也要传进去,否则报错
}
函数的调用
函数和数组
#include <iostream>
#define ArrSize 8
using namespace std;
int sum_arr(int *,int);
int main()
{
int cookies[ArrSize] = {1,2,4,8,16,32,64,128};
cout<<"数组名cookies的地址是"<<cookies<<"大小为"<< sizeof(cookies)<<endl;
int sum = sum_arr(cookies,4);
cout<<"the first sum = "<<sum<<endl;
// sum = sum_arr(cookies+4,4);
// cout<<"the last sum = "<<sum<<endl;
}
int sum_arr(int arr[],int n)
{
cout<<"形参arr的地址是"<<arr<<"大小为"<< sizeof(arr)<<endl;
// cout<<typeid(arr).name()<<endl;
int total = 0;
for(int i=0; i < n;i++)
{
total += arr[i];
}
return total;
}
输出结果:
数组名cookies的地址是0xc1dabff5f0大小为32 形参arr的地址是0xc1dabff5f0大小为8 the first sum = 15
数组名就是一个地址,作为参数传入函数中后编译器无法推断数组的大小,所以需要传入数组元素的个数,我们也可以指定元素的个数来进行求和等操作 数组名作为地址传入函数中,函数操作的是数组本身而不是数组的副本(比如int类型作为形参传入就是一个副本),这样可以不用再复制一个副本来占用内存空间 函数原型的作用是告知编译器这个函数的返回类型以及参数类型以及提前告知编译器存在这个函数,只不过在main函数之后,让编译器不要报错。参数类型可以只写类型不写变量名,函数定义在main函数之后需要使用函数原型,如果函数定义在main函数之前则不需要函数原型
使用const保护数组
因为数组作为参数传入数组不是以副本的形式传入的,而是传入地址,而使用地址能够修改地址中的值,所以如果在函数中使用数组,又不想误操作修改数组的话可以使用const关键字
void show_array(const double arr[], int n)
{
for(int i=0;i<n;i++)
{
cout<<arr[i]<<endl;
//arr[i]++;编译器将会报错
}
}
注意:使用const修饰之后数组在函数范围内是只读的,并不会修改数组本身的可读可写性
除了使用数组地址+元素个数
的方式进行传参,还可以使用首地址+尾地址
数组区间的方式进行传参,即传入首地址+尾地址就能够确定数组的大小,这也是STL中使用的方法
exp:
#include <iostream>
#define ArrSize 8
using namespace std;
int sum_arr(int *,int*);
int main()
{
int cookies[ArrSize] = {1,2,4,8,16,32,64,128};
cout<<"首地址"<<cookies<<endl<<"尾地址"<<cookies+ArrSize<<endl;
int sum = sum_arr(cookies,cookies+ArrSize);
cout<<"sum = "<<sum<<endl;
}
int sum_arr(int* begin,int* end)
{
int total =