新式C++头文件没有后缀名,例:
#include
...
using namespace std;
使用不带后缀的头文件时注意要声明名称空间
int
,但main函数可以不写 return 0;
默认添加 return 0;
_tmain()
,实际上也是被一个隐藏的main函数调用了_tmain()
。using namespace std;
using std::istream;
using std::ostream;
using std::cout;
using std::endl;
注意:方法一和方法二都可以在函数定义中声明,这样只有这个函数可以使用该名称空间
名称空间是为了区分名称相同的标识符,若使用方法一和方法二,则不能再出现同名称的标识符。
std::cout<<"Hello World"<<std::endl;
(c++11 的原始(raw)字符串可包含回车)
endl
—— 操作符。
效果:结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。
缓冲刷新操作可以保证目前为止程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流
打印语句时应该保证“一直”刷新流。C++Primer_P6.
换行细节 ‘\n’ 与 endl 的差别
endl 确保程序继续运行前刷新输出(立即显示到屏幕上),’\n’ 没有这样的保证。
(在某些系统中,有时才会可能出现这种差别,比如在输入信息后才出现提示)
例如:
int i;
cin>>i;
//但输入了字符,就会导致不合法输入
有的不合法输入会导致禁止读取输入,可通过cin.clear() 重置输入,并注意读取不合法的那些输入。
大多数操作系统支持文件重定向,该机制允许我们将标准输入和标准输出与命名文件关联起来:
在 cmd 中:
Test.exe outfile
# 或
Test outfile
#infile 是输入文件,outfile是输出文件
setf(ios_base::fixed)
将对象置于使用定点表示法的模式setf(ios_base:showpoint)
将对象置于显示小数点的模式,即使小数部分为0precision
指定对象显示多少位小数width()
设置下一次输出操作使用的字段宽度。默认为0,即刚好能容纳要显示的内容。由于它不是语言的组成部分,然而它是必不可少的函数的名称,可以把main作为变量名,但可能出错(在某种神秘的情况下)。
c++允许在程序的任何地方声明新变量
在首次使用变量前声明它。
以 两个下划线 或 下划线+大写字母 打头的名称被保留给(编译器及其使用的资源)使用。以 一个下划线 开头的名称保留给实现,用作全局标识符。
(“实现”可以理解为被c++内部使用,编程时尽量别用)
内置类型的机器实现
可寻址的最小内存块称为“字节(byte)”
存储的基本单元称为“字(word)”
大多数字节为8bit,字为4/8字节
整型类型 | 含义 | 大小 |
---|---|---|
short | 短整型 | 至少16位,64位win7为16位 |
int | 整型 | 至少与 short 一样长,64位win7为32位 |
long | 长整型 | 至少32位,且至少与 int 一样长,64位win7为32位 |
long long (c++11新增) | 长整型 | 长整型至少64位,且至少与 long 一样长,64位win7为64位 |
bool | 布尔类型 | 未定义,取值true/flase |
char | 字符 | 至少8位 |
wchar_t | 宽字符 | 至少16位 |
char16_t | Unicode字符 | 至少16位 |
char32_t | Unicode字符 | 至少32位 |
float | 单精度浮点型 | 6位有效数字 |
double | 双精度浮点型 | 10位有效数字 |
long double | 扩展精度浮点型 | 10位有效数字 |
c/c++ 的初始化
int v1 = 16;
int v2(16);//c++ 的初始化
c++11 的列表初始化,用于结构和数组
int hamburgers = {24};//单值变量的初始化,c++98也能用
//c++11:
int var1{7};
int var2{};//var2=0 大括号里没有东西,被初始化为0
c++11 使得大括号初始化器 用于任何类型(等号可选)
列表初始化的注意:
如果列表初始化且初始值存在丢失信息的风险,则编译器将报错:
long double ld = 3.1415926536;
int a{ld};//Error!存在丢失信息的风险
int c(ld),d=ld;//OK! 执行转换(确实丢失了部分)
(列表初始化待详解)
即使系统上int 与 long 都是32位,在需要表示的值大于16位最大值时,仍要用 long,便于移植。
进制表示:
012 八进制 =十进制的10
0x12 十六进制 =十进制的18
进制输出:
控制符 | 进制 |
---|---|
dec | 十进制 |
hex | 十六进制 |
oct | 八进制 |
控制符在名称空间 std 中
例:
int var = 42;
cout<<hex;
cout<<var<<endl;
输出 2a
除非有理由将常量存储为其他类型(用户用后缀指定,或 值太大以至于不能用 int),否则默认就存为 int 型。
常量后 | 说明 |
---|---|
l 或 L | long 常量 |
u 或 U | unsigned int 常量 |
ul,uL,Ul,UL | unsigned long 常量 |
ll 或 LL (c++11) | long long 常量 |
ull …(c++11) | unsigned long long 常量 |
12L
对于不带后缀的常量:
对十进制整数:
使用 int,long,long long 中能够存储该常量的最小类型
对十六进制或八进制整数:
使用 int,unsigned int long,unsigned long,long long 或 unsigned long long 中能够存储该常量的最小类型
八位,通常用于存储字符
\u + 8个16进制位 或 \U + 16个16进制位
用于表示各种字符集signed char ch1;
unsigned char ch2;
\u + 8个16进制位
匹配。\U + 16个16进制位
匹配。(1)直接 3.14
(2)E 表示法(E的大小写都行)
底数E指数
3.14e+8//(3.14*10的8次方)
5.38E24
9.11e-31
浮点类型 | 有效位数(2进制) |
---|---|
float | 至少32位,通常32位 |
double | 至少48位,且不少于 float,通常64位 |
long double | 至少和 double 一样多,通常为80,96,128位 |
ostream 中的 setf() 方法迫使输出使用定点表示法,防止程序把较大的数切换为 E表示法,并使程序显示小数点后6位,参数 ios_base::fixed 和 ios_base::floatfield 是iostream 提供的常量。(没有 ios_base 就 ios)
#include
int main( )
{
using namespace std;
cout.setf(ios_base::fixed,ios_base::floatfield);
float fvar=10.0/3.0;
double dvar=10.0/3.0;
const float million = 1.0e6;
cout<<"fvar = "<<fvar<<endl;
cout<<"fvar*million = "<<fvar*million<<endl;
cout<<"10*fvar*million = "<<10*fvar*million<<endl;
cout<<"dvar = "<<dvar<<endl;
cout<<"dvar*million = "<<dvar*million<<endl;
return 0;
}
输出:
fvar = 3.333333
fvar*million = 3333333.250000//精度不够
10*fvar*million = 33333332.000000//精度不够
dvar = 3.333333
dvar*million = 3333333.333333
程序说明:
#include
int main( )
{
using namespace std;
float fvar1=2.34e22f;
float fvar2=1.0+fvar1;
cout<<"fvar1 = "<<fvar1<<endl;
cout<<"fvar2 = "<<fvar2<<endl;
cout<<"fvar2 - fvar1 = "<<fvar2-fvar1<<endl;
return 0;
}
输出:
fvar1 = 2.34e+022
fvar2 = 2.34e+022
fvar2 - fvar1 = 0
原因:float 只能表示数字中的前6位或前7位,+1是在小数点左边第23位,不会对其有任何影响。
比如 42
[外链图片转存失败(img-MqengVuD-1568986200716)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1568206529570.png)]
换行符 \n | 反斜线 \\ |
---|---|
横向制表符 \t | 纵向制表符 \v |
回车符 \r | 退格符 \b |
进纸符 \f | 报警(响铃)符 \a |
问号 ? | 双引号 \" |
单引号 \' |
这些转义字符被当做一个字符使用
也可以使用泛化的转义序列
// \x后紧跟1个/多个 十六进制数字
// \后紧跟1个/2个/3个 八进制数字
//数字部分表示字符对应的数值
//对于 Latin-1字符集(向下兼容ASCII):
\7(响铃) \12(换行符) \40(空格)
\0(空字符) \115(字符M) \x4d(字符M)
像使用普通字符一样使用转义序列
std::cout<<"Hi \x4dO\115!\n";
//输出 Hi MOM!
const int Months = 12;
const int num[Months]{1,2,3,4,5,6,7,8,9,10,11,12};;
将变量声明为constexpr类型以由编译器来验证变量的值是否是一个常量表达式。
声明为 constexpr 的变量一定是一个常量且必须用常量表达式初始化
constexpr int mf =20;
constexpr int sz =mf +1;
constexpr int sz = size();//size() 必须是一个constexpr函数
constexpr 函数待补充(C++Primer P214)
一般来说,如果认为变量是一个常量表达式,就把它声明为constexpr
指针/引用都能定义成constexpr,但是初始值受限
constexpr 的指针初始值必须是 nullptr/0,或者存储于某固定地址的对象(全局对象/静态对象/…)
只需所指对象的地址固定,而无需对象的值固定!
constexpr 仅对指针有效,与所指对象无关(强行顶层const)
constexpr int *p = nullptr;//p是常量指针
隐式转换(c++自动进行的转换);
值将被转换为接收变量的类型
可能会出现的问题;
转换 | 潜在问题 |
---|---|
将较大的浮点类型转换为较小的浮点类型,如double→float | 精度(有效位)降低,值超出范围后结果不确定 |
将浮点类型转换为整型 | 小数部分丢失,值超出范围后结果不确定 |
赋给无符号类型一个超出他表示范围的值。如-1赋给unsigned char->255 | 超出范围后,通常只复制右边的字节(所赋值%该类型的表示范围) |
赋给带符号类型一个超出他表示范围的值 | 结果未定义,程序可能崩溃… |
不允许从较大类型到较小类型的转换(缩窄narrowing)
char c1{31352}; //错误,不能缩窄
char c2=31352;
c++会执行两种自动转换:
一般由函数原型控制,也可以取消这种控制。
取消该控制后,对 char 和 short(signed和unsigned)整型提升,float→double。
不会修改变量本身,而是创建一个新的,指定类型的值,该值可以在表达式中使用。
(typeName)value // from c
typeName(value) //c++
(第15章详说)
auto n = 100; //n is int
auto x = 1.5; //x is double
auto y = 1.3e12L; //y is long double
auto i = 0,*p = &i;//基本类型必须一样
以上例子尽量别用,auto 用于其他作用
其真正含义待以后学习。(STL 中)
编译器推断 auto 类型可能和初始值类型并完全一样,会适当调整:
初始值为引用类型,则实际判断类型为被引用的类型
auto 一般会忽略顶层const,而保留底层const
const int ci = i,&cr =ci;
auto b = ci;//c是整型
auto c = cr;//c是整型
auto e = &ci;//e是指向整型常量的指针
若希望是顶层const,则
const int ci = 0;
const auto f = ci;
引用设为 auto:
auto &g = ci;//g是一个整型常量引用,绑定ci
//↑↑↑↑↑保留了ci的顶层const
auto &h = 42;//Error!
const auto &h = 42;//Right!
一条语句定义多个变量时,初始值必须是同一种类型,包括是否const
int i = 0;
const int ci = 42;
auto k = ci,&l = i;//k是整数型,l是整数型引用
auto &m = ci,*p = &ci;//m是整数引用,p是指向常量的指针
auto &n = i,*p2 = &ci;//Error!i是int,而ci是const int
auto 作用于数组名是效果为指针
目的:从表达式推断要定义的变量的类型,但是不以该表达式的值初始化
选择并返回操作数的数据类型(不会实际计算其值)
decltype(f()) sum = x;//x的类型就是函数f的返回类型
decltype 与 引用
int i =42,*p =&i,&r = i;
decltype(r+0)b;//b是 int 型
decltype(r)b1;//b1是 int& 引用
decltype(*p)b2;//b2是 int& 引用 *p就是对象本身r
表达式的形式影响
int i =42;
decltype(i) d;//d是一个int
decltype((i)) e;//Error!! e是一个int&,必须初始化
//注意:
//decltype((variable))永远是引用
//decltype(variable)只有 variable本身是引用才能使引用
赋值是会产生引用的一类典型表达式
decltype 对于数组,效果还是数组
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);//ia2是int指针
decltype(ia) ia3;//ia3是10个元素的数组
C++是一种静态类型语言,即在编译阶段进行类型检查,这有助于提前发现问题。
int card[4]={3,6,8,10};
long hand[12]={1};//hand[0]=1,hand[i]=0,i=1,2,..,11
double dog[]={1,2,3,4}//可省略 [] 内,由编译器计算
double earnings[4]{1.2e4,1.6e5,1.1e4,1.7e4};// c++11
int cards[4]={};//all elements set to 0
int cardss[4]{};//all elements set to 0
char slifs{'h','h',311111}//error! 禁止缩窄转换
long slifs{12,10000,1.2}//error! 禁止缩窄转换(浮点到整型都是缩窄)
int *ptrs[10];
int &refs[10]; //Error!不存在引用的数组
int (*Parray)[10] = &arr;//Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
通常将下标定义为 size_t 类型,是一种机器相关的无符号类型
但是内置的下标运算符可以处理负数
int *p = &ia[-2];
int j = p[2];// j是 ia[0]
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);//ia2是int指针
decltype(ia) ia3;//ia3是10个元素的数组
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(arr);
int *last = end(ia);//尾后指针
auto m = end(arr) - begin(arr);
//n是数组元素个数
遍历数组的又一种方法:
int *b = arr,*e = arr+sz;
while(b
int ia[3][4] = {{3},{1},{2}};
//3 0 0 0
//1 0 0 0
//2 0 0 0
int p[3][4] = {{1},{2},{3}};
for(auto &i:p){ //注意这一层必须引用,否则会把数组类型转换成指针
//i 类型为 int(&i)[4]
for(auto k:i)//如果要修改则这一层也要引用
{//k就是int型
cout<
char char1[20]="ni hao a ";
char char2[]={"da jia hao a "};
char char3[]{"wo hen hao"};
c++ 中 任何两个由空白(空格,制表符,换行符)分割的字符串敞亮都将自动拼接成一个。
cout<<"Hello " "World!""\n";
char name[20];
cin>>name;
cin 判断一个字符串输入结束的方式:空白(空格,制表符,换行符)。
#include
int main(int argc, char** argv) {
using namespace std;
char ch;
int count=0;
cout<<"Enter a string end with '#'\n";
cin>>ch;//输入字符串后会保存在输入流中进入while循环后逐个读取
while(ch!='#')
{
cout<>ch;
}
cout<
//结果:
Enter a string end with '#'
ss ss sss#
sssssss
The length of string you input is: 7
请按任意键继续. . .
cin.getline() 和 cin.get()
(1)面向行的输入 cin.getline()
(2)面向行的输入 cin.get()
该函数有多个变体,其中一种变体:
参数与 cin.getline() 一样,只是不再读取换行符,而是把它留在输入队列中。
还有一种不带参数的变体:读取下一个字符(即便是换行符)
可用来处理换行符
char name[20],address[40];
cin.get(name,20);
cin.get();
cin.get(address,40).get();//cin.get() 返回 cin对象
<=>
cin.getline(name,20).getline(address,40);//cin.getine() 返回 cin对象,从左往右执行
(3)空行 和 其他问题
cin.get() 读取空行后将设置失效位(failbit),使接下来的输入被阻断,可用 cin.clear() 来恢复输入。
cin.getline()会使输入从空行之后开始输入。
另一个问题:输入字符串比分配的空间(指定的第二参数)长。cin.geiline() 和cin.get() 会把余下的字符留在输入队列中,cin.getline() 还会设置失效位,关闭后来的输入。
详说 见5,6,17 章
(cin>>year).get(ch);
包含头文件
名称空间 std
#include
#include
using namespace std;
int main()
{
char char1[20]="ni hao a ";
string str1="da jia hao a";
cout<<char1<<endl<<str1<<endl;
cin>>str1;
cout<<str1[0]<<endl;
return 0;
}
运行结果:
ni hao a
da jia hao a
hello
h
可以和C-风格字符串一样的初始化形式,或用C-风格字符串 初始化。
string str1="ni hao a ";
string str2={"da jia hao a "};
string str3{"wo hen hao"};
str1=str2;//可用 str 对象给 str对象赋值
str3 = str1 + str2;
str1+=str2;
string 对象的初始长度默认为0,可自动调整长度。
getline(cin,str);
类似于 cin.getline() 但是不用指明长度。(详见 二,文本输入-getline())
(1)其他编码
wchar_t title[] = L"Chief Astogator";
char16_t name[] = u"Felonia Ripova";
char32_t car[] = U"Humber Super Snipt";
c++11 还支持 Unicode 字符编码方案 UTF-8 。该方案中字符可能存储为1~4 个字节,c++使用前缀 u8 表示这种类型的字符串的字面值。
(2)原始字符串(raw)
字符表示的就是字符自己。
R"( … )"
cout<<R"(Jim "King" Tutt uses "\n" instead of endl)"<<"\n";
cout<<R"+*("""(Who wouldn't?)",she whispered.)+*"<<endl;
原始字符串语法允许在 “ 和 ( 之间添加其他字符,但必须在结束的 ) 和 " 之间添加同样的字符。
可将前缀 R 与其他字符串前缀结合使用,例如 Ru 或 uR 。
初始化可以使用结尾为 ‘\0’ 的字符数组
string对象的等号右边可以是C风格字符串
.c_str 返回C风格字符串
返回结果是一个指针,指向C风格字符串
结果指针类型是const char*
无法保证返回的该字符串一直有效,可能C被修改后就无法使用
所以应该把返回的内容拷贝一份以供使用
string s("Hello World");
const char *str;
strcpy(str,s.c_str());
struct produces
{
char name[20];
int price;
};
...
struct produces dress;
produces hat;
c++ 允许在定义结构变量时 不用 struct 而直接使用结构名。
struct produces
{
char name[20];
int price;
}bag={"bag",15},bigbag={"big bag",32};
...
produces hat =
{
“Glorious Gloria”,
18
};
produces apple{"apple",6};//C++11
produces mayor{};//所有成员被设为0
可作为参数
可互相赋值
可没有结构名而直接定义结构变量:
struct
{
int x,
int y
}position;
produces bags[2]=
{
{"small bag",15},
{"big bag",32}
};
字段类型为 整型 或 枚举,然后是 冒号,然后是它指定了使用的位数,可以使用没有名称的字段来提供间距,每个成员都被称为 位字段(bit field)
struct torgle_register
{
unsigned int SN:4;
unsigned int :4;
bool goodIn :1;
bool goodTorgle:1;
}
初始化:
torgle_register tr={14,true,flase};
union one4all
{
int int_val;
long long_val;
double double_val;
};
…
enum spectrum{blue,orange,red,yellow};
从0开始作为其符号常量的值,又称为枚举量。
可用枚举来声明这种类型的变量:
spectrum color = blue;
color = blue + orange;
错在枚举常量运算后变为int型,无法自动转换为枚举spectrum型)可用定义枚举但不声明枚举变量来直接使用枚举量(符号常量),并省略枚举名称。
enum {blue,orange,red,yellow};
enum cor{blue=1,orange,red=8,yellow=12};
enum cor{blue,orange=0,red,yellow=1};
上限:大于这个最大值的,最小的2的幂,将它减去1。例 最大枚举量101 的枚举上限为127。
下限:最小枚举量 大于等于0则下限为0,小于0则计算方式与上限一样,只是加上负号。
变量定义 = 一个数据类型 + 声明符列表
声明符命名了变量并指定与给基本数据类型有关的某种类型
类型修饰符就是 * ,&
修饰符的个数没有限制
类型修饰符是声明符的一部分
int i = 24,*p;
所以正确写法就是 *与名字紧贴 int *p;
int & n = i;
一定要在对指针使用解除引用运算符(*)前将指针初始化为一个确定的,适当的地址。
nullptr
要将数字值作为地址来使用必须通过强制类型转换将数字转换为适当的地址类型。
int * pt;
pt = (int*) 0xB8000000;
new 分配的内存来自 堆(heap) 或 自由存储区
一般变量或指针来自 栈(stack)
new 分配的内存必须用 delete 释放,不要释放已释放的内存,可对空指针delete。
创建:
int * psome = new int[10];
释放:
delete[]psome;
对指针变量+1,其增加的值等于指向类型占用的字节数。
数组名被解释为第一个元素的地址(大小为数组第一个元素大小的内存块),对数组名取地址则是整个数组的地址(大小为数组的大小的内存块)。
前者 int (*psome)[10]
是int数组
后者int * psome[10]
是指针数组
若给 cout 提供一个字符的地址(char型地址),则它从该字符开始打印直到空字符为止。
在 cout 和 多数 c++表达式中,char数组名,char地址(char指针),以及用双引号括起的字符串常量都被解释为字符串第一个字符的地址。
给 cout 提供一个指针,他将打印地址,提供 char* 指针会打印字符串,若要显示char*指针的地址可转换为int*
指针。
const double *cptr = π
int i =42;
int *const cp = &i;
引用本身不是对象
int i=42;
int *p;
int *&r =p;
r = &i;//p=&i
*r = 0;//i=0
都是模板类
vector 长度可变
array 长度不变
都不检测中括号索引的有效性,用其 .at() 成员函数可检测索引有效性。
std::vector
#include
using std::vector;
...
vector ivec;
vectorSales_vec;
vector > file; //该向量的元素是vector对象
//注意:外层<>与内层<>之间应该有一个空格,不然> >会与>>混淆
//引用不是对象,故引用不能包含于vector。
vector v1;
vector v2(v1);
vector v2 = v1;//同上一个,拷贝初始化
vector v3(n,val);//包含n个重复的元素,每个元素值为val
vector v33{n1,n2};//v33包含两个分别初始化为n1,n2的对象
vector v4(n);//v4包含n个重复地执行了值初始化的对象
vevtor v44{n};//v44 包含一个值为n的对象
vector v5(a,b,c,...);//
vector v5 = (a,b,c,...);//同上一个
//列表初始化 vector(C++11)
vector articles = {"a","an","the"};
//用数组初始化
//只需要给出指向第一个元素和最后一个元素的指针
int int_arr[] = {0,1,2,3,4};
vector ivec(begin(int_arr),end(int_arr));//全部
vector ivec2(int_arr+1,int_arr+3);//部分
//
vector v6("hi");//Error!不是列表初始化,而是企图将字符串类型作为元素个数
vector v2;
for(int i=0;i!=100;++i)
v2.push_back(i);
//v2包含从0到99
v.empty() //vector为空则返回真;否则返回假
v.size() //返回v中元素个数
v.push_back(t) //向v的尾端添加一个值为t的元素
v[n] //返回v中第n个位置上元素的引用 n类型为 vector::size_type
//下标从0开始
v1 = v2 /拷贝v2元素拷贝替换 v1 中的元素
v1 = {a,b,c} //将列表元素拷贝替换 v1 中的元素
v1 == v2 //v1和v2值相等
<,<=,>,>= //以字典顺序进行比较
auto b = v.begin(),e = v.end();
//begin 返回指向第一个元素的迭代器
//end 返回指向尾后迭代器(尾元素的下一个位置),只是起标记作用,并不能实际使用
//如果 v 为空,则两个函数返回同一个迭代器,都是尾后迭代器
*iter //返回迭代器iter所指元素的引用
iter->men //解引用iter并获取该元素的名为men的成员,等价于 (*iter).mem
(*iter).mem //注意解引用要用圆括号括起来
++iter //令iter指示容器中的下一个元素
--iter //令iter指示容器中的上一个元素
iter1 == iter2 //判断迭代器是否相等
iter1 != iter2
vector::iterator it;//it是vector类型的迭代器
string::iterator it2;//it2是string类型的迭代器
vector::const_iterator it3;
string::const_iterator it4;
//只能通过该迭代器读取元素,不能修改
iter + n
iter - n //返回 difference_type,带符号的,拥有迭代器类型的类型的常量
iter += n
iter -= n
iter - iter2;
>,>=,<,<=
typedef double wages;
typedef wages base,*p;//p <=> double *
using ST = String; //ST 是 String的同义词
typedef char *pString;
//pString就是char指针,离pString最近的const表示指针是常量
const pString cstr = 0;//cstr是指向char的常量指针
const pString *ps; //ps是指向 指向char的指针 的常量指针