本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这是本文档旧的修订版!
第 3 章笔记
int a; //int
int b[10]; // int[10]
size_t
(converted expression,比如浮点就不行)
int x;
std::cin >> x;
//不合法
//类型由编译器决定,但长度要在运行期才能得到
//编译器无法推断出b[x] 的类型
int b[x];
//合法
constexpr short y = 3;
int c[y];
–pedantic-errors
tag 来支持 variable length array。但这样做是有风险的,因为该功能是 complier denpend。除此之外,某些必须在编译器完成的功能也不能使用该类类型做为参数,比如 decltype(b)
int b[3]; //default int
int c[3] = {1, 2, 3}; // int[3], aggregate init
int cn[] = {1,2, 3}; //int[3]
int d[3] = {1,2} // partially aggregate init, 1,2,0
auto
:auto 会将 {}
视作 initialization_list
//char[6] 简化定义写法,会自动添加 ''\0'' 表示结束
char str[] = "hello";
//char[5] 完整定义写法,但不会为末尾添加 ''\0''
char str[] = {‘h’, 'e', 'l', 'l', '0'};
//a[3] 是有3个元素为指针的数组
int* a[3];
//注意括号
//a 是指针,指向 int[3]
int (*a) [3];
//a 是引用,指向 int[3]
int b[3];
int (&a) [3] = b;
引用不支持聚合初始化。C++ 中不支持元素为引用的数组。从概念上来说,引用不是对象。数组要求元素使对象,因此没有元素为引用的数组。
a[0]
[]
操作符x[y]
,且 x
, y
都是 Built-in 类型时,x[y]
被解析为 *(x+y)
,也就是转换为指针,再解引用。
int a[3] = {1,2,3};
auto b = a;
//int(&)
decltype(b)
//int*
a;
//int*
b;
//int, *(b+0)
b[0]
可以使用 decltype 来判断表达式是否是左值。
// decay, b 是 int* 类型
int a[3];
auto b = a;
//不会 decay 的范例
//decltype 返回的是数组的类型
// int[3]
decltype(a);
// int size * 3
sizeof(a);
// b 是 Int(&)[3] 类型
// 此时长度信息没有丢失
auto& b = a;
extern
指针声明数组
//声明数组
//不能使用 extern 指针
extern int array[4];
//illegal
//runtime error
extern int* array;
很多情况下,长度信息需要反复的修改。如果在声明的时候带上长度信息,那么每次更改 array 的长度都必须同时去声明中改变 array 的长度。因此很多情况下会使用指针替代数组。但 extern
不能直接使用指针代替数组。来看看下面的例子:
//source.cpp
int array[4] = {1,2,3,4};
void fun()
{
std::cout << array << std::endl;
}
//main.cpp
extern int* array;
void fun();
int main()
{
fun();
std::cout << array << std::endl;
}
// output
// 两个 array 的输出不一样
0x7ff718ee3000
0x200000001
*(array + step)
array[4]
中存储的类型是 int(32字节)一个16进制数为 24,占 4 bits,那么每个数组成员都需要 32 / 4 = 8 个 16 进制数来表示,比如 1 就是 00 00 00 01
01 00 00 00
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
0x 2 00 00 00 01
。01 00 00 00 02 00 00 00
按内存地址的方式解析出来的结果。这个结果反应的是数组的内容,是固定的,应该怎么做?
extern int array[];
int array[]
是数组的合法声明。定义已经在 source.cpp 中完成了。
计算的两种方法:
int a[3] = {1,2,3};
//开头指针
a; //数组名即为指向数组首地址的指针
&(a[0]) // 取数组首元素的地址(注意括号)
std::begin(a); std::cbegin(a); //标准库提供的取地址函数,int* 和 const int*
//结尾指针
a + 3;
&(a[3]);
std::end(a); std::cend(a);
std::begin()
与 std::end()
适用于数组类型而不是指针类型,任何导致数组信息的丢失(转换,Unkonwn bounded)都会导致调用失败:
b = a;
//error, begin() & end() are not applicable to a pointer.
std::begin(b); std::end(b);
//之前的共享 array 也不能使用 begin 和 end,因为边界未知
extern int array[];
std::begin(b); std::end(b);
std::begin()
和 std::end()
,需要在声明的时候声明为 complete type,也就是提供其数组的长度:
//ok
extern int array[4];
std::begin(b); std::end(b);
std::begin(); std::end(); 是泛型函数,可以应用到各种类型的容器中。
int a[3] = {1,2,3};
auto ptr = a;
auto ptr2 = a;
//指针的前进
ptr = ptr + 1;
//比较
ptr == ptr2;
//关系运算(不推荐,除非两个指针在同数组中)
ptr > ptr2;
//求距离
ptr2 - ptr
sizeof()
:使用 sizeof 获取数组总大小,再除以元素的宽度std::size()
:使用标准库方法std::end() - std::begin()
/* 必须都是对数组类型进行操作 */
int b[10];
//sizeof,[C method]
sizeof(b) / sizeof(int);
//std::size, [recommand]
std::size(b);
//end - begin, [run time method],通常使用 const 版本。
std::end(b) - std::begin(b);
int c[3] = {1,2,3};
//使用 std::size() 和 while
size_t index = 0;
while(index < std::size(c))
{
std::cout << c[index] << '\n';
index++;
}
//cbeign, cend
auto bPtr = std::cbegin(c);
while(bPtr != std::cend(c))
{
std::cout << *bPtr << '\n';
bPtr++;
}
//range for ,语法糖,基于 cbegin / cend 实现
for (auto elem : c)
{
std::cout << elem << '\n';
}
<cstring>
//由于在内存中是连续的,因此可以使用 list 初始化
//初始值不够的所有元素都会进行默认初始化
//按位初始化
//1,2,3,4,0,0,0,0,0,0,0,0,0
int x[3][4] = {1,2,3,4};
//分组初始化
int y[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
//分组初始化的默认初始化处于分组内部
int z[3][4] = {{1,2,3}, {4,5,6,7}, {8,9,10,11}};
//输出 0
std::cout << z[0][3];
//输出 4
std::cout << z[1][0];
//如果需要自动推断,必须指定元素的类型
//只能省略最高位
int a[][] = {1,2,3,4}; // error
int a[][] = {{1,2,3,4}}; // error
int a[][4] = {1,2,3,4}; // int[1][4]
===多维数组的索引和遍历===