文章目录
前言
pass
一、变量和基本类型
数据类型
size_t | |
string::size_type | |
vector<int>::iterator | |
vector<int>::const_iterator | |
vector<int>::const_pointer | |
vector<int>::const_reference |
算数类型(arithmetic type)
整型数
整数、字符、布尔类型
浮点型
空类型(void):函数不返回任何值时实用空类型作为返回类型等
一个地址可存储8bit的数据(在我的笔记本上)
类型 | 空间占用大小 sizeof() (在我的笔记本上) |
---|---|
char | 1B 8b |
int | 4B 32b |
short | 2B 16b |
long | 4B 32b |
long long | 8B 64b |
float | 4B 32b |
double | 64B 8b |
字面值
20 /*十进制*/ 024 /*八进制*/ 0xff /*十六进制*/ 3.14e0 34.43E-2
'a' //字符
"a" //字符串字面值 有时候字符串后边自动添加 \0
转义序列escape sequence
\n(换行) | \t(横向制表符) | \a(响铃) | \v(纵向制表符) | \b | \" |
\7(响铃) | \12 (换行符) | \40 (空格) | \0 (空字符) | \115 (字符M) | \x4d (字符M) |
L'a' //
u8"hi!" //
42ULL //
1E-3F //
3.14L //
variables
声名——定义——初始化
语法:类型说明符 标识符[ 初始化 ];
eg: int a [ = 0 ];
变量可以被重复声名(用externa),但不可以被重复定义;
//五种定义方式
int val = 0;
//变量不可重复定义
int val = {0}; //列表初始化
int val = (0)
int val{0}; //和 = {}等价吗?
int val(0); //有时候不能用此种方式
//在头文件中已经定义了val,要在此文件中使用它,需要声名extern,重复定义会覆盖原定义
extern int val; //声名val而非定义val
extern int val=1; //编译器报错,
int val=1; //只能出现一次 int val
C++关键字 keywords
作用域scope
作用域是程序的一部分,在其中的明字有其特定的含义。C++语言中大多数作用域都以花括号分隔。
复合类型
引用
相当于a = list() b = a中的b
变量的应一个别名,引用类型引用(refers to)另外一种类型
nete: 引用不是对象,相反的,他只是为一个已经存在的对象所起的另一个名字。
绑定后不可更改
引用在定义后必须初始化,且只能指向对象,不能是表达式,数字,字符等。
int ival = 1024;
int &refVal = ival; //refVal指向ival(是ival的另一个名字) refVal与ival绑定(bind)后不可更改
int &refVal; //报错:引用必须初始化
int ival=1024, &refVal=ival; //√
int &refVal4 = 10; //× 引用类型的初始值必须是一个对象
double dval = 3.14;
//普通引用类型的类型(这里是int)必须与要引用的另外一种类型的类型一致(既:被引用的“另外一种类型”也要为int)
int &refVal5 = dval; //× 此处引用类型的初始值必须是int型对象
指针
指针是对象,允许赋值拷贝等操作;定义时赋初值不是必须的。
指针的值(即地址)应属下列四种状态之一:
1.指向一个对象
2.指向紧邻对象所占空间的下一个位置
3.空指针,意味着指针没有指向任何对象 p=0;p=NULL;p=nullptr;
4.无效指针,也就是上述情况之外的其它值
int ival=1, *ip1=&ival, *ip2=ip1 //√
double dval=2.31;
int *p = &dval; //×
// p指针所指对象的数据类型为int
*ip1=0 //由符号*得到指针p所指的对象,即可经由p为变量ival赋值
//空指针
int *p=nullptr, *p1=0, *p2=NULL;
p=0 //× 不能把int值直接赋值给指针
int v, *p;
if (v) //v==0 为false其他为true
if (p) //任何非0指针对应的条件值都为true
void* 指针
void* 是一种特殊的指针类型,可用于存放任意对象的地址。
不可解引用
void *pv = &int/&double...
*pv //× 不可解引用
指向指针的指针
指针是一个对象所以可以让一个指针指向另一个指针,而引用不是对象
int ival=1024, *pi=&ival, **ppi=π
pi //地址
ppi //地址
*pi //1024
**ppi //1024
指向指针的引用
指针是对象所以可行
/*首先r是一个引用,这个引用指向一个指针,这个指针指向一个数据类型为int的对象*/
int i=42, *p=&i/*p是一个指针*/, *&r=p;
r = &i; //r引用了一个指针,因此给r赋值&i就是令p指向i
*r=0; //解引用r得到i,也就是p指向的对象,将i赋值0
//从右向左阅读r的定义
const限定符
目的:让变量read-only
const int bufSize = get_size(); //√ 运行时初始化
const int j = 43; //√ 编译时初始化
const int k; //× k是一个未经初始化的常量
const int ci = bufSize //√
extern const int bufSize; //√ 头文件中的bufSize
const 的引用
const int ci = 1024; //方便看:【const int】 ci = 1024;
// 【const int】 &r1 r1是一个引用,他指向的对象为【const int】
const int &r1 = ci; //√ r1为常量引用,必须用常量引用指向常量
r1 = 42; //× 不可通过常量引用赋值
// r2为一个引用,它指向的对象为【int】,而ci为【const int】,所以不行
int &r2 = ci //× 只有常量引用才可指向常量对象
int i = 42;
const int &r1 = i; //√ 这是特例情况,允许将const int & 绑定到一个普通 int 对象上
const int &r2 = 43; //√
const int &r3 = r1 * 2; //√
int &r4 = r1 *2; //×
double dval = 3.14;
const int &ri = dval; //√ 常量引用必须初始化,对初始化的值几乎不做要求,可以是:{
//常量、非常量、指定类型、非指定类型、简单表达式(需要进一步验证更复杂的是否可行)}
/*****原理**********
*编译器把上述代码变成了如下形式:
*const int temp = dval; //由双精度浮点数生成一个临时的整型常量
*const int &ri = temp; //让ri绑定这个临时量*/
int i = 42;
int &r1 = i; //以用r1绑定对象i
const int &r2 = i; //r2也绑定对象i,但是不允许通过r2修改i的值
r1 = 0; //√
r2 = 0; //×
指针和const
指向常量的指针 只能指向 指定的类型(可为常量或非常量)
const double pi = 3.14; //
double *ptr = π // ×
const double *cptr = π // √
*cptr = 42; // ×
double dval = 3.14;
cptr = &dval; //√ 但是不能通过cptr改变dval的值
const 指针
常量指针,指针是个常量而非指向的对象是常量
必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了
int err = 0;
int * const errp = &err;
const double pi = 3.14;
const double * const *p = π
顶层const表示指针本身是个常量,底层const表示指针所指的对象是一个常量
const int i=4;是顶层const
int i = 0;
int * const p1 = &i; //p1的值不能变,但是*p1可以变
const int ci = 42; //ci不可变
const int * p2 = &ci; //p2可变, *p2不可变
const int * const p3 = p2; //p3和*p3都不可变
const int &r = ci //不可通过r改变ci
i = ci; //√
p2 = p3; //√
int *p = p3; //×
p2 = p3; //√
p2 = &i; //√
int &r = ci; //×
const int &r = i; //√
constexpr和常量表达式
const int max_files = 20; // max_files是常量表达式
const int limit = max_files + 1; // limit是常量表达式
int staff_size = 27; //staff_size不是常量表达式
const int sz = get_size(); // sz不是常量表达式,因为它的具体值直到运行时才能获取到
constexpr变量
将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式
声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化
constexpr int mf = 30; //30是常量表达式
constexpr int limit = mf + 1; //mf+1是常量表达式
constexpr int sz = size(); //只有当size()是一个constexpr函数时才是一条正确的声名语句
常量表达式的值需要在编译时就得到计算。
一个constexpr指针的初始值必须是nullptr或者0,或者是存储与某个固定地址中的对象。
指针和constexpr
在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:
const int * p = nullptr;
constexpr int * q = nullptr;
constexpr int * np = nullptr; //np是一个指向整数的常量指针,其值为空
int j = 0;
constexpr int i = 42; //i的类型是整型常量
//i和j必须都定义在函数体之外
constexpr const int * p = &i; //p是常量指针,指向整型常量i
constexpr int *p1 = &j; //p1是常量指针,指向整数j
处理类型
类型别名 type alias
类型别名是一个名字,他是某种类型的同义词
//传统方法
typedef double anothername; // anothername 是 double 的同义词
typedef anothername base, *p; // base是double的同义词,p是 double * 的同义词
// (anothername *) p
base dval; // 等价于double dval;
p dval; // 等价于double * dval;
// p是一个复合类型 const p dval;等价于 double*const dval;
//较为新的方法
using SI = SalesItem; // SI 是 SalesItem 的同义词
SI item; //等价于 SailesItem item;
比较一下typedef 和using
typedef int arrT[10]; // arrT是一个类型别名,它表示的类型是含有10个整数的数组
using arrT = int[10]; //arrT的等价声名
指针、常量和类型别名
typedef char * pstring; // typedef 【char *】 pstring,pstring本质上是个指针,它指向char类型
const pstring cstr = 0; // 从右向左看:cstr是变量名,pstring是声名符声名cstr是一个指向char的
// 指针,const用于修饰指针(pstring)说明它是常量指针
// 因此:cstr是指向char的常量指针
// 'const pstring' {aka 'char* const'}
const pstring * ps; // ps是指针,它指向另一个 常量指针,该常量指针指向char
/*注意,该理解是错误的:const char * cstr=0;
正确的理解应该是: char * const cstr = 0;
*/
char ci = 'a';
char const * p1 = 0; //p1 等价p2
const char * p2 = 0;
char * const p3 = &ci;
p1 = &ci;
p2 = &ci;
// *p1 = ci; //×
// *p2 = ci; //×
// p3 = &ci; //×
*p3 = 0;
auto
// 由v1 和 v2相加的结果可以推断出item的类型
auto item = v1 + v2;
auto i = 0, *p = &i; //√ i是整数,p是整型指针
auto sz = 0, pi=3.14; //× sz和pi的类型不一致
int i = 0, &r = i;
auto a = r; //a是一个整数(r是i的别名,而i是一个整数)
//auto一般会忽略顶层const,同时底层const会保留,入当初是指是一个指向常量的指针时:
const int ci = i, &cr = ci;
auto b = ci; //b是int(ci的顶层const特性被忽略)
auto c = cr; //c是int(cr是ci的别名,ci本身是一个顶层const)
auto d = &i; //d是一个指向int的指针(整型指针)
auto e = &ci; //e是一个指向整数常量的指针(对常量取地址是一种底层const)
//如果希望推断出的auto类型是一个顶层const,需要明确说明:
const auto f = ci; //f是const int
//将引用的类型设为auto,此时原来的初始化规则仍然适用:
auto &g = ci; //g is const int &
auto &h = 42; //× h是int &, 不能为非常量引用绑定字面值
const auto &j = 42; //√
//
auto k = ci, &l = i; //k is int, l is int&
auto &m = ci, *p = &ci; //m is const int &, p is const int *
auto &n = i, *p2= &ci; //错误,i是int,而&ci的类型是 const int
decltype类型指示符
decltype(f()) sum = x; //sum的类型是函数f的返回类型
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x is const int
decltype(cj) y = x; //y is const int &
decltype(cj) z; //×, z是一个引用,它(自以为是的)想与const int绑定,必须赋初值
//引用从来都是作为其所指对象的同义词出现,只有用在decltype处是一个例外
int i = 42, *p = &i, &r = i;
decltype(r+0) b; // √ 加法的结果是int,b是int类型
/* r是一个引用,因此decltype(r)的结果必然是引用类型,若让结果类型是r所指向的类型,
可把r作为表达式的一部分,入r+0,显然这个表达式的结果是一个具体值而非一个引用*/
decltype(*p) c; // × c是int&,必须初始化
const int *p=0;
decltype(*p) c=9; //c is const int &
decltype((i=0))是引用
decltype((i))是引用
decltype(*p) 是引用
decltype(&r) 是指针
自定义数据结构
预处理器
本章小结
常量必须初始化,包括指向常量的引用和常量指针。而引用或者指针会“自以为是”的以为自己指向的是常量,即,不可通过指针或引用更改对象的值,而指向的对象并不一定为常量。
自以为是指向常量的引用除了必须初始化外对对象的类型几乎没要求;
自以为是指向常量的指针(并非常量指针)则可以不初始化,或者指向的类型必须与实际的类型相同。
C++在变量的声名、定义、初始化有一套比较复杂的逻辑
类型 | 初始化方式 |
---|---|
int | 可初始化也可不初始化,若不初始化则会有一个未定义的值 |
const int | 必须初始化 |
int & | 必须初始化,必须指向对象,不能是数字 |
int * | 可初始化可不初始化 |
const int & | 必须初始化 const int &i = 42; const int &i = [const int i]; const int &i = [int i]; const int &i = [(const) double i]; |
const int * | 可初始化也可不初始化 const int *p=&[int i]; |
第二章 字符串、向量和数组
命名空间的using声名
在此文章搜索 help 查看粗体斜体 【】的含义
using namespace :: name ; using namespace :: name2 ;
为了防止名字冲突,头文件不应包含using声名
string
初始化 ”\0“代表字符串的结束
初始化string对象的六种方法
string s1; //s1=""
string s2(s1); //s2是s1的副本
string s2 = s1; //s2是s1的副本
string s3 = "hiya"; //s3是该字符串字面值的副本
string s4(n, 'c'); //s4= n个连续的c
//拷贝初始化
string s8 = string(n,'c');//equal to:
//string temp(n, 'c'), s8 = temp;
有 = 拷贝初始化
无 = 直接初始化
string类除了要规定初始化其对象的方式外,还要定义对象上所能执行的操作。
字符串字面值与string是不同的类型
std::size_type和int不是同一种类型
/* 输入固定个数的字符串 */
string s1, s2;
cin >> s1 >> s2;
/* 读取未知数量的string对象 */
while (cin >> wd) // 反复读取,至到达到文件末尾
cout << wd <<endl; // 逐个输出单词,每个单词后面紧跟换行
/* getline(is, s) 遇到'\n'就结束读取操作并返回结果 */
while (getline(cin, s)) if (!s.empty() && s.size()>10) cout << s << endl;
// s.size() 返回的是string::size_type类型,是一个无符号整数
string st1(19, 'c'), st2;
st2 = st1; //将st1的副本赋值给st2,和python不同的是st1和st2指向两个不同的对象
st1 += st2 //真的和st1 = st1 + st2等价吗? python中不一定
string st3 = st1 + ',' + "aa"; //(st1 + ",") + "aa"
#include <cctype>
decltype(s.size()) punct = 0;
for (auto c : s) if (ispunct(c)) ++punct;
cout << punct << endl;
// upper
for (auto &c : s) c = toupper(c);
//another example
for (decltype(s.size()) index=0; index != s.size() && !isspace(s[index]); ++index)
s[index] = toupper(s[index]);
字符处理函数
c: ctype.h
c++: cctype
不太懂cctype和 toupper函数的关系
标准库类型vector
vector表示对象的集合,其中所有对象的类型相同。和list相比所有元素必须是相同的类型,有点像numpy array
vector和list有点像
因为vector“容纳着”其他对象,所以它也常被称为容器container
vector是模板而非类型
元素为vector的vector对象
//c++11
vector<vector<int>>
//老版
vector<vector<int> >
初始化vector对象
不存在 vector<int> iv=(10,1);
vector是模板,v1~v5是vector的对象
v1~v5相当于numpy array
vector<string> v1{"a", "an", "the"}; //列表初始化
vector<string> v2("a", "an", "the"); //❌错误
vector<int> ivec(10, -1); //10个int类型的元素,每个都被初始化为-1
vector<string> svec(10, "hi!"); //10个string类型的元素,每个都被初始化为"hi!"
//此方式<class>必须提供初始值
vector<int> ivec(10); //10个元素,每个都初始化为0
vector<string> svec(10); //10个元素,每个都是空string对象
//圆括号与花括号 圆括号代表构造,花括号:优先列表初始化在考虑构造
vector<int> v1(10); //v1有10个元素,每个的值都是0
vector<int> v2{10}; //v2有1个元素,该元素的值是10
vector<int> v3(10, 1); //v3有10个元素,每个都是1
vector<int> v4{10, 1}; //v4有2个元素,值分别是10和1
vector<string> v5{"hi"}; //列表初始化:v5有一个元素
vector<string> v6("hi"); //❌ 不能使用字符串字面值构建vector对象
vector<string> v7{10}; //10个默认初始化的元素
vector<string> v8{10, "hi"}; //10个值默认为“hi”的元素
vector对象的成员函数(method/方法)
v.push_back(element);
v.empty(); //return true or false
v.size(); //return 由vector定义的size_type类型
v[n]; //返回v中第n个位置上元素的引用
v1 = v2; //用v2中元素的拷贝替换v1中的元素
v1 = {a,b,c..}; //用列表中元素的拷贝替换v1中的元素
v1 ==v2;
v1 != v2;
<, <=, >, >=
vector<int> vi{1,2,3,4,5,6,7,8,9};
for (auto &i : vi){
i *= 9; //i必须为引用才能更改vi的值
}
for (auto i : vi){
cout << i << endl;//输出vi
}
/*指定size_type*/
vector<int>::size_type //正确
vector::size_type //错误
vector和string对象的索引的数据类型是 size_type 而非int
缓冲区溢出
迭代器
vector.begin() vector.end() 若vect为空则都返回同一个迭代器,尾后迭代器
*iter 返回迭代器iter所知元素的引用
iter->mem 解引用iter并获取该元素的名为mem的成员,定价于(*iter).mem
++iter 指向下一个元素
--iter 指向上一个元素
iter1 == iter2 两个迭代器指向同一个vector的同一个element,则相等,否则不等
iter1 != iter2
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的类型是vector<int>::iterator
auto it2 = cv.begin(); //it2的类型是vector<int>::const_iterator
auto it1 = v.cbegin();//it1的类型是vector<int>::const_iterator
auto it2 = v.cend(); //it2的类型是vector<int>::const_iterator
(*it).empty() //解引用it,然后调用结果对象的empty成员(函数/属性)
it->empty 等价于 (*it).empty
for (auto it = v.cbegin(); it != v.cend() && ! it->empty() ; ++it){
cout << *it << endl;}
但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
vector和string迭代器支持的运算
iter1 - iter2 可正可负,类型为difference_type
应该为“索引相减”
vector<int> v1{1,2,3,4,5,6,7,9,10,11,12};
int target = 9;
auto left = v1.begin(), right = v1.end();
auto mid=left+(right-left)/2;
while (true){
if (*mid == target)break;
if (mid == right) break;
if (target < *mid){
right = mid;
}
else{
left = mid + 1 ;
}
mid = left + (right-left)/2 ;
}
数组
不清处元素的确切个数用vector
数组固定大小,元素数据类型相同
和vector一样,元素必须为对象,因此不存在引用的数组
数组的维度必须是常量表达式
unsigned cnt = 42; //not constexpr
constexpr unsigned sz=442;
int arr[10]; //含有十个整数的数组
int *parr[sz]; //含有42个整型指针的数组
string badp[cnt]; //❌,cnt不是常量表达式, 虽然有的编译器不报错但还是要用常量表达式
string strs[get_size()]; //当函数是constexpr时正确,否则❌
const unsigned sz=3;
//显示初始化数组
int a1[sz] = {0,1,2}; //[0,1,2]
int a2[] = {1,2,3}; //[1,2,3]
int a3[5] = {1,2,3}; //[1,2,3,0,0]
int a4[3] = {"hi","bye"};//["hi", "bye", ""]
int a5[2] = {1,2,3}; //❌ 初始值过多
//字符数组
char a[] = {'c','+','+'}; //列表初始化,没有空字符
char a[] = {'c','+','+','\0'}; //列表初始化,含有显式的空字符
char a[] = "c++"; //自动添加表示子符串结束的空字符
const char a[6]="123456"; //❌ 没有空间可存放空字符
数组不允许拷贝和赋值
int a[] = {1,2,3};//✔
int a2[] = a; //❌不允许使用一个数组初始化另一个数组
a2 = a; //❌不能把一个数组直接赋值给另一个数组
复杂数组
int *ptrs[10]; //含有10个整型指针的数组 (int*) ptrs [10] 从右向左看
int &refs[10] = /*?*/ //❌ 不存在引用的数组
int (*Parry)[10] = &arr; //Parry指向一个含有10个整型数的数组
//由名字开始,从内向外看 (*Parry) --> [10] --> int
int (&arrrRef)[10] = arr; //arrRef引用一个含有10个整型数的数组
int *(&arry)[10] = ptrs; //arry是数组的引用,该数组含有10个指针
数组下标定义为 size_t类型,是一种机器相关的无符号类型
#include cstddef 定义了size_t类
unsigned scored[11];
unsigned grade;
while(cin << grade){
if (grade <= 100)
++scores[grade/10];} //emm,这个grade/10是常量表达式吗
for (auto i: scores) cout << i << " ";
指针与数组
string nums[] = {"one", "two", "three"};
string * p = &nums[0]; //与下面等价
string * p = nums; //与上面等价
auto ia2(nums) //ia2是一个string型指针,指向nums的第一个元素
//类似:auto ia2(&nums[0])
decltype(nums) ai3={"one","two","three"}
指针也是迭代器
<iterator>
//输出数组元素
string a[2]{"jalsjdf", "jflksd"}, *p=a, *e=&a[2];
for (auto i(a); i<e;i++)
cout <<*i << endl;
//建议的用法
string *beg = begin(a), *last = end(a); //beg &a[0] last &a[2]
指针运算
所有迭代器运算,包括解引用、递增、比较、与整数相加、两个指针相减
constexpr size_t sz=5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr, *ip2 = ip + 4;
int *p = arr + sz;
//指针相减
auto n=end(arr)-begin(arr);//n是ptrdiff_t标准库类型,定义在cstddef头文件中的机器相关的类型
数组的下标运算符所用的索引不是无符号类型,这与vector和string不一样(它俩是)
int a[]={0,2,4,6};
int last=*(a+4);
int *p = &a[2];
int j = p[1]; // p[1]等价于*(p+1)
int k = p[-2]; // p[-2]等价于*(p-2)
C风格字符串
充满风险,不要用
在cstring头文件中, c++版本为string.h
strlen(p)
strcmp(p1, p2)
使用数组初始化vector对象
int int_arr[]={1,2,3,4};
vector<int> ivec(std::begin(int_arr), std::end(int_arr));
多维数组
数组的数组
数组的名字就是地址,可直接赋值给指针
数组的名字本质上就是地址,address[n] 等价于 *(p+n)指针p加n,然后解引用
int a[4]; 中a的类型是 int *
int ia[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int ia[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; //同上
int ia[3][4] = {{0},{1}}; //未列出的元素执行默认初始化
ia[2][3] = arr[0][0][0];
int (&row)[4] = ia[1]; //把row绑定到ia的第二个4元素数组上
//是非法的,i:ia是吧得知给了i(i的类型int *),所以j:i非法
for (auto i: ia) //❌
for (auto j:i)
cout << j;
数组用范围for必须用引用型
要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型
//得到正确结果
int ia[3][4]={1,2,3,4 };
for (auto &i: ia){ //i是引用
cout << i <<endl; //输出的i为地址
for (auto j: i){
cout<<j<<endl;
}
}
//程序报错
int ia[3][4]={1,2,3,4 };
for (auto i: ia){
cout << i <<endl;
for (auto j: i){
cout<<j<<endl;
}
}
//
int *p = ia; //❌ cannot convert 'int (*)[4]' to 'int*'
int (*p)[4] = ia; //√ 是指针,它指向一个数组, (*p)-->[4]-->[int]
int *p[4]; // 数组有4个维度,名称是p,数组类型(即存放的元素的类型)是int*
/*为什么不是int (*p)[3]?可能最外层不需要显式指出?或者说指针就是一个数组?
参见 int a[4], *p=a;*/
for (auto p=std::begin(ia);p != end(ia); ++p){
for (auto q=std::begin(*p); q != std::end(*p); ++q){
cout << *q << " ";
}
}
多维数组小结
// 直接写出数据类型,不使用类型别名、auto关键字或decltype关键字
int ia[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; //多为数组初始化
/*1 使用范围for语句*/
for (int (&i)[4]: ia){ //仔细体会 范围for语句
cout << i ;
for (int j: i){ //j非引用,不可更改数组元素
cout << j << " ";
}
cout << endl;
}
/*2 下标运算符*/
for (std::size_t i=0; i!=3;++i){
for (std::size_t j=0; j!=4;++j){
cout<<ia[i][j] << " ";
}
cout << endl;
}
/*3 指针*/
for (int (*i)[4] = std::begin(ia); i != std::end(ia); i++){
for (int *j=std::begin(*i); j!=std::end(*i); j++){
cout << *j << " ";
}
cout << endl;
}
/*引用与数组*/
int a[3]={1,2,3}, (&ref)[3] = a; //仔细品
/*指针与数组*/
int a[3]={1,2,3}, *p = a;
/*引用与多维数组*/
int a[3][4]={1,2,3}, (&ref)[3][4] = a; //仔细品 名字ref与名字a完全等价
/*指针与多维数组*/
int a[3][4]={1,2,3}, (*p)[4] = a;
本章不完全小结
string st="abc\0end"; //看到\0后即可终止,并抛弃\0
char st[]="abc\0end" //至到字符串结束并自动再添加个\0