C++程序结构
程序注释
①连续斜杠//
②/**/为界
#include指令——头文件
①#include<>
编译器只搜索包含标准头文件的目录
②#include“”
编译器先在包含此指令的源文件所在的目录搜索头文件。如果头文件未找到,编译器再搜索存储标准头文件的目录。
名称空间和using声明
名称空间是一种机制,它可以将无意中使用重名的风险降至最低。
作用域解析运算符::
using告诉编译器,要在不指定名称空间名称的情况下使用名称空间中的名称。
在头文件中应避免把using声明放在全局作用域中,因为他们会应用于包含该头文件的所有源文件。
程序语句
每个程序语句都以一个分号结束。分号表示语句结束,而不是一行的结束。
空白
空包包括空格、制表符、换行符、换页符、注释的任意序列。
语句块
把几个语句在一对大括号中,这时他们就变成了块或复合语句。
函数体就是块的一个示例。
#include "pch.h"
#include <iostream>
int main()
{
int num1 = 10;
std::cout << "num1 : "<< num1 << std::endl;
{
int num1 = 20;
std::cout << "num1 : " << num1 << std::endl;
}
std::cout << "num1 : " << num1 << std::endl;
}
预编译的头文件
编译*.h文件会得到一个*.pch文件(预编译的头文件),只要没有对应的*.pch文件,或者*.pch文件的时间戳比*.h的早,编译器才重新编译*.h文件。
Main函数
编写使用Unicode字符的程序时,VC++支持wmain()作为main()的替代函数。wmain()是main()的Microsoft特有定义。
tchar.h头文件定义了名称_tmain,它一般由main取代,但是如果定义了符号_UNICODE,它由wmain取代。
变量
变量命名
变量名最长可达到2048个字符,名称必须以字母或下划线开头,可以是任意字母(下划线)和数字的序列。
最好避免以下划线且包含大写字母的名称和双下划线开头的名称,如_Upper,因为他们可能与相同的标准库名称发生冲突。
第二个单词和以后各单词的首字母大写,或者在各单词之间插入下划线。
变量名称区分大小写。
关键字
关键字是C++中的保留字,它们在该语言内有特殊的意义。
关键字区分大小写。
声明变量
声明变量是一个语句,它指定变量的名称和类型。
为了避免编译器报告错误消息,必须在第一次使用变量之前声明它。
变量的初始值
声明变量时,还可以给它赋予初始值。
赋予初始值的方法:
常规赋值:
int value=0;
函数表示法:
int value(0);
初始化列表(推荐方式):
int value {0};
基本数据类型
基本类型:存储整数的类型、存储非整数值的类型(浮点)、指定空的值集或者不指定任何类型的void类型。
类型 | 字节数 | 备注 | 类型 |
---|---|---|---|
bool | 1 | true 或 flase | 布尔 |
char | 1 | -128~+127,常规情况和signed char一样 | 字符 |
signed char | 1 | -128~+127 | 字符 |
unsigned char | 1 | 0~255 | 字符 |
wchar_t | 2 | 0~65535,初始化时字符常量前面的字母L告诉编译器,这是一个16位字符代码值。也可以使用整形常量初始化。 | 字符 |
short | 2 | 可存储±整数数值,等同于short int,-32768~32767 | 整形 |
unsigned short | 2 | 0~65535(2^16) | 整形 |
int | 4 | 可存储±整数数值 | 整形 |
unsigned int | 4 | 0~2^32 | 整形 |
long | 4 | VC++中存储的值域与int相同,可存储±整数数值,后缀L或l | 整形 |
unsigned long | 4 | 0~2^32 | 整形 |
long long | 8 | 可存储±整数数值,后缀LL或ll | 整形 |
unsigned long long | 8 | 0~2^64 | 整形 |
float | 4 | 精度大约7个小数位 | 浮点 |
double | 8 | 精度大约15个小数位 | 浮点 |
long double | 8 | 精度大约15个小数位 | 浮点 |
signed 和 unsigned是关键字。
浮点常量必须包含一个小数点,一个指数或者两者都有。常量结尾的f指定它属于float型。
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
int num1;
std::cout << "num1 size: " << sizeof(num1) <<" bytes" << std::endl;
std::cout << "num1 type: " << typeid(num1).name() << std::endl;
std::cout << std::endl;
short num2;
std::cout << "num2 size: " << sizeof(num2) << " bytes" << std::endl;
std::cout << "num2 type: " << typeid(num2).name() << std::endl;
std::cout << std::endl;
long num3;
std::cout << "num3 size: " << sizeof(num3) << " bytes" << std::endl;
std::cout << "num3 type: " << typeid(num3).name() << std::endl;
std::cout << std::endl;
long long num4;
std::cout << "num4 size: " << sizeof(num4) << " bytes" << std::endl;
std::cout << "num4 type: " << typeid(num4).name() << std::endl;
std::cout << std::endl;
char num5;
std::cout << "num5 size: " << sizeof(num5) << " bytes" << std::endl;
std::cout << "num5 type: " << typeid(num5).name() << std::endl;
std::cout << std::endl;
wchar_t num6;
std::cout << "num6 size: " << sizeof(num6) << " bytes" << std::endl;
std::cout << "num6 type: " << typeid(num6).name() << std::endl;
std::cout << std::endl;
bool num7;
std::cout << "num7 size: " << sizeof(num7) << " bytes" << std::endl;
std::cout << "num7 type: " << typeid(num7).name() << std::endl;
std::cout << std::endl;
float num8;
std::cout << "num8 size: " << sizeof(num8) << " bytes" << std::endl;
std::cout << "num8 type: " << typeid(num8).name() << std::endl;
std::cout << std::endl;
double num9;
std::cout << "num9 size: " << sizeof(num9) << " bytes" << std::endl;
std::cout << "num9 type: " << typeid(num9).name() << std::endl;
std::cout << std::endl;
long double num10;
std::cout << "num10 size: " << sizeof(num10) << " bytes" << std::endl;
std::cout << "num10 type: " << typeid(num10).name() << std::endl;
std::cout << std::endl;
unsigned char num11;
std::cout << "num11 size: " << sizeof(num11) << " bytes" << std::endl;
std::cout << "num11 type: " << typeid(num11).name() << std::endl;
std::cout << std::endl;
signed char num12;
std::cout << "num12 size: " << sizeof(num12) << " bytes" << std::endl;
std::cout << "num12 type: " << typeid(num12).name() << std::endl;
std::cout << std::endl;
}
字面值
类型 | 字面值 | 备注 |
---|---|---|
char signed char unsigned char | 'A'、'Z'、'8'、'*' | |
w_char_t | L'A'、L'Z'、L'8'、L'*' | L表示这是一个16位字符代码值 |
int | -77,65,12345,0x9FE | |
unsigned int | 10U、64000u | U表示unsigned |
long | -77L、65L、12345l | L表示long |
unsigned long | 5UL、999999UL | UL表示unsigned long |
long long | -777LL、66LL | LL表示long long |
unsigned long long | 55ULL、99999ULLL | ULL表示unsigned long long |
float | 3.14f、34.506F | F表示float |
double | 1.414、2.71828 | |
long double | 1.414L、2.71828L | |
bool | true、false |
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
auto num = 'A';
std::cout << "num size: " << sizeof(num) <<" bytes" << std::endl;
std::cout << "num type: " << typeid(num).name() << std::endl;
std::cout << std::endl;
auto num1 = L'A';
std::cout << "num1 size: " << sizeof(num1) << " bytes" << std::endl;
std::cout << "num1 type: " << typeid(num1).name() << std::endl;
std::cout << std::endl;
auto num2 = 1;
std::cout << "num2 size: " << sizeof(num2) << " bytes" << std::endl;
std::cout << "num2 type: " << typeid(num2).name() << std::endl;
std::cout << std::endl;
auto num3 = 1U;
std::cout << "num3 size: " << sizeof(num3) << " bytes" << std::endl;
std::cout << "num3 type: " << typeid(num3).name() << std::endl;
std::cout << std::endl;
auto num4 = 1L;
std::cout << "num4 size: " << sizeof(num4) << " bytes" << std::endl;
std::cout << "num4 type: " << typeid(num4).name() << std::endl;
std::cout << std::endl;
auto num5 = 1UL;
std::cout << "num5 size: " << sizeof(num5) << " bytes" << std::endl;
std::cout << "num5 type: " << typeid(num5).name() << std::endl;
std::cout << std::endl;
auto num6 = 1LL;
std::cout << "num6 size: " << sizeof(num6) << " bytes" << std::endl;
std::cout << "num6 type: " << typeid(num6).name() << std::endl;
std::cout << std::endl;
auto num7 = 1ULL;
std::cout << "num7 size: " << sizeof(num7) << " bytes" << std::endl;
std::cout << "num7 type: " << typeid(num7).name() << std::endl;
std::cout << std::endl;
auto num8 = 3.14f;
std::cout << "num8 size: " << sizeof(num8) << " bytes" << std::endl;
std::cout << "num8 type: " << typeid(num8).name() << std::endl;
std::cout << std::endl;
auto num9 = 3.14;
std::cout << "num9 size: " << sizeof(num9) << " bytes" << std::endl;
std::cout << "num9 type: " << typeid(num9).name() << std::endl;
std::cout << std::endl;
auto num10 = 3.14L;
std::cout << "num10 size: " << sizeof(num10) << " bytes" << std::endl;
std::cout << "num10 type: " << typeid(num10).name() << std::endl;
std::cout << std::endl;
auto num11 = true;
std::cout << "num11 size: " << sizeof(num11) << " bytes" << std::endl;
std::cout << "num11 type: " << typeid(num11).name() << std::endl;
std::cout << std::endl;
}
定义类型的别名
typedef关键字能够为现有的类型定义自己的类型名称。例如:typedef long int BigOnes;
using关键字来定义类型别名。using BigOnes= long int;
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
typedef int ___Integer;
___Integer num = 0xFF;
std::cout << "num size: " << sizeof(num) <<" bytes" << std::endl;
std::cout << "num type: " << typeid(num).name() << std::endl;
std::cout << std::endl;
using ___character = char;
___character num1 = 0xF;
std::cout << "num1 size: " << sizeof(num1) << " bytes" << std::endl;
std::cout << "num1 type: " << typeid(num1).name() << std::endl;
std::cout << std::endl;
}
基本的输入输出操作
命令行的标准输出流称为cout,来自键盘的互补输入流称为cin。
std::cin>> num1 >> num2 ;
该输入语句从左向右执行,在连续值之间必须有一些空白,以便区分它们。输入Enter后,流输入操作结束。
std::cout<< num1 << num2 ;
格式化输出
操作符在头文件中iomapip中定义。
setw(n):输出值在n个字符宽度的字段中右对齐
std::cout<< std::setw(6) << num1 << std::setw(6) << num2 ;
setiosflags:
std::cout<<std::setiosflags(std::ios::left);
可以使输出在给定字段宽度内左对齐,而不是默认的右对齐。
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
typedef int ___Integer;
___Integer num = 0xFF;
std::cout << std::setw(20)<<"num size: " << sizeof(num) <<" bytes" << std::endl;
std::cout << std::setw(20) << std::setiosflags(std::ios::left) << "num type: " << typeid(num).name() << std::endl;
std::cout << std::endl;
}
转义序列
转义序列以反斜杠字符开头,反斜杠字符告诉编译器按照特殊的方法解释后的字符。
转义序列 | 作用 | 转义序列 | 作用 |
---|---|---|---|
\a | 发出蜂鸣声 | \b | 退格 |
\n | 换行 | \t | 制表符 |
\' | 单引号 | \" | 双引号 |
\\ | 反斜杠 | \? | 问好 |
C++中的计算
赋值
a=c+c;
连续赋值
a=b=2;
把2先赋值给b,再把b赋值给a。
算数运算
基本算数运算符是加法、减法、乘法和除法,分别由+、-、*、/表示。
%计算余数。
+ | - | * | / | % |
<< | >> | & | ^ | | |
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
int num = 0x01;
std::cout << (num << 2) << std::endl;
std::cout << (num >> 1) << std::endl;
std::cout << (1^0) << std::endl;
}
const修饰符
一种类型修饰符,表示常量,初始赋值后不能修改。
常量表达式
使用常量表达式初始化变量,就不必亲自计算数值。
增量、减量运算符
增量 | 减量 |
+=,++前缀,++后缀 | -=,--前缀,--后缀 |
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
int num = 0x01;
std::cout <<"num=1,num++: "<< (num++) << std::endl;
std::cout << "num: " << (num) << std::endl;
std::cout << std::endl;
std::cout << "num=2,++num: " << (++num) << std::endl;
std::cout << "num: " << (num) << std::endl;
}
运算符的优先顺序
顺序 | 运算操作符 | 关联性 |
---|---|---|
1 | :: | 无 |
2 | () [] -> . 后缀++ 后缀-- typeid const_cast dynamic_cast static_cast reinterpret_cast | 从左向右 |
3 | ! ~ 一元+ 一元- 前缀++ 前缀-- &(取址符) *(间接操作符) type cast (type) 类型转换 sizeof decltype 声明类型 new new[] delete delete[] | 从右向左 |
4 | . -> | 从左向右 |
5 | * / % | 从左向右 |
6 | + - | 从左向右 |
7 | << >> | 从左向右 |
8 | == != | 从左向右 |
9 | & | 从左向右 |
10 | ^ | 从左向右 |
11 | | | 从左向右 |
12 | && | 从左向右 |
13 | || | 从左向右 |
14 | ?: | 从右向左 |
15 | = *= /= %= += -= &= |= <<= >>= | 从右向左 |
16 | throw | 从右向左 |
17 | , | 从左向右 |
始终可以使用圆括号重写运算符的优先顺序。
类型转换和类型强制转换
隐式转换
二元运算中,编译器都必须把其中一个操作数的类型转换成与另一个操作数相匹配的类型,这个过程称为隐式转换。
隐式转换可能会产生意想不到的结果,这可能导致丢失信息。
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
unsigned int num1{ 0x01 };
signed int num2{ 0x02 };
std::cout << "num1 - num2: " << (num1 - num2) << std::endl;
//output:4294967295
}
编译器根据下表所示由高到低的类型排序,来确定将哪个操作数转换成另一个操作数的类型。
1 | long double |
---|---|
2 | double |
3 | float |
4 | unsigned long long |
5 | long long |
6 | unsigned long |
7 | long |
8 | unsigned int |
9 | int |
显示类型转换(强制转换)
static_cast<要转换成的类型>(表达式)
static_cast:静态地检查类型强制转换。
dynamic_cast:动态地检查转换,在程序执行时转换。
const_cast:用于删除表达式中的const属性。
reinterpret_cast:一种无条件的强制转换。
一般来说,应当尽可能避免强制转换。
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
auto num1{ 3.14f };
auto num2{ 3.14 };
auto num3{static_cast<int>(num1)+ static_cast<int>(num2) };
std::cout << "num3 : " << num3 << std::endl;
std::cout << "num3 : " << typeid(num3).name() << std::endl;
//output:num3 : 6
//output:num3 : int
}
老实的类型强制转换
(要转换成的类型)表达式
老式类型强制转换的代码更容易出错
#include "pch.h"
#include <iostream>
#include <iomanip>
int main()
{
auto num1{ 3.14f };
auto num2{ 3.14 };
auto num3{(int)num1+ (int)num2 };
std::cout << "num3 : " << num3 << std::endl;
std::cout << "num3 : " << typeid(num3).name() << std::endl;
//output:num3 : 6
//output:num3 : int
}
auto关键字
用auto关键字用作变量的类型,根据提供的初始值来推断。
使用auto关键字时,必须为变量提供初始值。
类型的确认
typeid操作符能确定表达式的类型。
typeid(表达式)会产生一个类型为type_info的对象。
按位运算符
它们只能处理整形变量或整形常量。
& | | | ^ | ~ | >> | << |
按位与 | 按位或 | 按位异或 | 按位取反 | 右移 | 左移 |
#include "pch.h"
#include <iostream>
#include "bitset"
using namespace std;
int main()
{
bool num1{ true };
std::cout << "num3 : "<< (bitset<16>) num1 << std::endl;
std::cout << "num3 : " << (bitset<16>)(~num1) << std::endl;
}
lvalue和rvalue
每个表达式的结果都是lvalue或rvalue。
lvalue指的是内存中持续存储数据的一个地址。
rvalue是临时存储的表达式结果。
只包含一个命名变量的表达式始终是lvalue。
存储时间和作用域
自动变量
自动变量在作用域中的时间从声明它的那一刻开始,一直到包含其声明的代码块结束为止。
自动变量占用的内存在一个陈伟栈的内存区域分配,栈是专门为此而流出的内存。可以修改/STACK或/F编译器命令行选项设置为所选值,也可把栈的大小设置为一个链接器属性。
变量的声明位置
- 变量的作用域
- 放在靠近第一次使用变量的地方
全局变量
在所有代码块和类之外声明的变量称为全局变量。
全局变量默认具有静态存储时间。意味着从程序开始执行到结束,变量都存在。
静态变量
可以对它进行局部定义和访问,但是在退出声明它的代码块后,它还继续存在。
static操作符提供了这样的方法。
具有特定值集的变量
旧枚举
枚举器按顺序赋值,默认从0开始,后续每个枚举器的值都比其前面的枚举器大1.
如果愿意,也可以把特定的值赋予所有枚举器,枚举器也不一定按升序排列。
不需要用枚举名限定枚举常量,当然也可以这么做。
enum Weekdays {Mon,Tues,Wed,Thurs,Fri,Sat,Sun} today; //枚举器按顺序赋值,默认从0开始,后续每个枚举器的值都比其前面的枚举器大1.
enum Weekdays {Mon=1,Tues,Wed,Thurs,Fri,Sat,Sun} today;
Weekdays myBirthday {Weekdays::Tues}; //不需要用枚举名限定枚举常量,当然也可以这么做。
可以把枚举器类型显示地指定除wchar_t之外的其他整形,如枚举器是unsigned long类型:
enum Weekdays :unsigned long {Mon,Tues,Wed,Thurs,Fri,Sat,Sun} today;
类型安全的枚举
enum 后面使用class关键字,就可以使用新枚举类型。
枚举器的名称就不会到处到封闭的范围内,而必须使用类型的名称来限定它们。
enum class Weekdays {Mon,Tues,Wed,Thurs,Fri,Sat,Sun} today;
Weekdays myBirthday {Weekdays::Tues}; //枚举器的名称就不会到处到封闭的范围内,而必须使用类型的名称来限定它们。
如果希望把一个枚举值转换为另一种类型,就可以使用显示类型转换。
enum class Weekdays {Mon,Tues,Wed,Thurs,Fri,Sat,Sun} today;
Weekdays myBirthday {Weekdays::Tues}; //枚举器的名称就不会到处到封闭的范围内,而必须使用类型的名称来限定它们。
int value {static_cast<int> (myBirthday )};
名称空间
名称空间定义一个作用域,其中声明的每个名称都有名称空间的名称限定。
从名称空间外部引用名称时,需要限定这些名称。
使用名称空间吗来限定对象名,就可以在名称空间的外部访问该名称空间中的各个对象。
可以给名称空间中要引用的每个名称提供using声明。
声明名称空间
使用namespace可以声明一个名称空间
namespace myNamespace
{
//user codes
}