数组的引入
定义
将一到多个相同类型的对象串连到一起,所组成的类型
#include <iostream>
#include <type_traits>
int main()
{
int a;
int b[10];
std::cout << std::is_same_v<decltype(a),int> << std::endl;
std::cout << std::is_same_v<decltype(b),int[10]> << std::endl;
}
Output:
1
1
special one:
#include <iostream>
#include <type_traits>
int main()
{
int a;
std::cin >> a;
int b[a];
}
这样写是错误的,涉及到编译器标准对C++标准的一些扩展:https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_pbt1493403170123.htm
special two:
int a;
int b[1];
两者对应的内存大小一样,但相关的操作是不同的,因此不能把两者混为一谈
special three:
https://en.cppreference.com/w/cpp/language/array
special four:
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
数组的初始化方式
● 缺省初始化
int array[3];//在全局域中定义,缺省初始化,数组中的元素被初始化为0;函数里定义,缺省初始化,元素为随机值。
● 聚合初始化
int array[3] = {1,2}; --> {1,2,0}
int array[3] = { }; --> {0,0,0}
注意事项
int main() { auto a = {1,2,3}; std::cout << typeid(a).name(); }
**_Output:_**
```cpp
St16initializer_listIiE //name mangling
Insight:
#include <iostream>
#include <type_traits>
#include <typeinfo>
int main()
{
std::initializer_list<int> a = std::initializer_list<int>{1, 2, 3};
std::operator<<(std::cout, typeid(a).name());
}
int main()
{
char str1[] = “hello”;// —> {‘h’,’e’,’l’,’l’,’o’,’\n’}
char str2[] = {‘h’,’e’,’l’,’l’,’o’};
std::cout << std::is_same_v
**_Output:_**
```cpp
1
1
数组的复杂声明
指针数组与数组的指针 :
int* a[3];
int (*)a[3];
声明数组的引用 :
int a[3];
int (&)b[3] = a;
special:不能声明引用数组
引用在底层上仍是指针来实现,但从概念上来说仍是对象的别名,而数组里的元素是对象。
#include <iostream>
int main()
{
int x1,x2,x3;
int& a[3] = {x1,x2,x3};
}
Output:
<source>:6:10: error: declaration of 'a' as array of references
6 | int& a[3] = {x1,x2,x3};
| ^
数组中的元素访问
int main()
{
int a[3] = {1,2,3};
std::cout << std::is_same_v
**_Output:_**
```cpp
1
special:注意这里a要加上(),转化为表达式,根据decltype的用法恰好验证了a作为数组对象的左值性
具体内容可参考这篇文章:
左值与右值
int main()
{
int a[3] = {1,2,3};
auto b = a; // b —> int
std::cout << b[0] << std::endl;
std::cout << (b + 0) << std::endl;
std::cout << 0[b] << std::endl; // x[y] → *((x) + (y))
}
```cpp
1
1
1
从数组到指针
数组到指针的隐式转换
- 使用数组对象时,通常情况下会产生数组到指针的隐式转换
- 隐式转换会丢失一部分类型信息,可以通过声明引用来避免隐式转换
注意:不要使用 extern 指针来声明数组
Unknown Bounded Array 声明 <br />**_special:数组和指针不能混为一谈_**<br />在项目C++practice里建立test.cpp、source.cpp两个文件,代码分别如下:
```cpp //test.cpp
include
void fun(); extern int* array;
int main() { fun(); std::cout << array << std::endl; }
```cpp
//source.cpp
#include <iostream>
int array[5] = {8,2,3,4,5};
void fun()
{
std::cout << array << std::endl;
}
运行工程,结果如下:
0x100402010
0x200000008
分析:
在test.cpp里我们要使用source.cpp里的数组,于是使用了extern指针声明数组,显然这是错误的,而array里的元素由于大端小端的存储方式不同
8 —> 00000008 —> 08 00 00 00
2 —> 00000002 —> 02 00 00 00
而直接输出array,考虑到array此时是个指针类型,占一个字节共八位,经过一些转化,输出0x200000008
原因就是混用了指针和数组的概念
解决方法:
Unknown Bounded Array 声明 extern int* array
—> extern int array[]
0x100402010
0x100402010
获得指向数组开头与结尾的指针
std::(c)begin, std::(c)end
std::cbegin() // const int*
std::begin() // int*
指针算数
- 增加、减少
- 比较
- 求距离
- 解引用
- 指针索引 数组
其他操作
求元素的个数
sizeof 方法
std::size 方法
(c)end - (c)begin 方法
#include <iostream>
#include <type_traits>
int main()
{
int a[3] = {1,2,3};
std::cout << std::cend(a) - std::cbegin(a) << std::endl;
std::cout << sizeof(a) / sizeof(int) << std::endl;
std::cout << std::size(a) << std::endl;
}
Output:
3
3
3
元素遍历
基于元素个数
基于 (c)begin/(c)end
基于 range-based for 循环
C字符串
C 字符串本质上也是数组
C 语言提供了额外的函数来支持 C 字符串相关的操作 : strlen, strcmp
多维数组
多维数组的实质无非就是数组的数组:
int array[3][4];
int array[0] --> int[4] --> int array[3]的每一个元素是int[4](从里到外)
-->
int int int int
int int int int
int int int int
-->
(int int int int) (int int int int) (int int int int)
多维数组的聚合初始化
一层大括号 V.S. 多层大括号
多维数组的索引与遍历
使用多个中括号来索引
使用多重循环来遍历
range based for循环:
#include <iostream>
#include <type_traits>
#include <typeinfo>
int main()
{
int array[3][4] = {1,2,3,4,5,6,7,8,9,10};
for(auto& p : array){ //&不能少,否则会发生类型退化
for(auto q : p){
std::cout << q << std::endl;
}
}
}
Insight:
#include <iostream>
int main()
{
int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 0, 0}};
{
int (&__range1)[3][4] = array;
int (*__begin1)[4] = __range1;
int (*__end1)[4] = __range1 + 3L;
for(; __begin1 != __end1; ++__begin1) {
int (&p)[4] = *__begin1;
{
int (&__range2)[4] = p;
int * __begin2 = __range2;
int * __end2 = __range2 + 4L;
for(; __begin2 != __end2; ++__begin2) {
int q = *__begin2;
std::cout.operator<<(q).operator<<(std::endl);
}
}
}
}
}
while循环
指针与多维数组
多维数组可以隐式转换为指针,但只有最高维会进行转换,其它维度的信息会被保留
#include <iostream>
int main()
{
int array[3][4][5];
auto ptr = array;
auto ptr1 = ptr[0];
}
Insight:
#include <iostream>
int main()
{
int array[3][4][5];
int (*ptr)[4][5] = array;
int (*ptr1)[5] = ptr[0];
}
使用类型别名来简化多维数组指针的声明
以上面的代码为例:
#include <iostream>
using A = int[4][5];
int main()
{
int array[3][4][5];
A* ptr = array;
auto ptr1 = ptr[0];
}
再来一段代码:
#include <iostream>
using A = int[4];
int main()
{
A array[3];
//int array[3][4];
A* ptr = array;
auto* ptr1 = array;
auto ptr2 = array;
}
Insight:
#include <iostream>
using A = int[4];
int main()
{
A array[3];
A (*ptr) = array;
int (*ptr1)[4] = array;
}
使用指针来遍历多维数组
#include <iostream>
int main()
{
int array[3][4] = {1,2,3,3,4,7,5,6,7,9,9,10};
auto ptr1 = std::begin(array);
while(ptr1 != std::end(array)){
auto ptr2 = std::begin(*ptr1);
while(ptr2 != std::end(*ptr1)){
std::cout << *ptr2 << std::endl;
ptr2 = ptr2 + 1;
}
ptr1 = ptr1 + 1;
}
}
Insight:
#include <iostream>
int main()
{
int array[3][4] = {{1, 2, 3, 3}, {4, 7, 5, 6}, {7, 9, 9, 10}};
int (*ptr1)[4] = std::begin(array);
while(ptr1 != std::end(array)) {
int * ptr2 = std::begin(*ptr1);
while(ptr2 != std::end(*ptr1)) {
std::cout.operator<<(*ptr2).operator<<(std::endl);
ptr2 = (ptr2 + 1);
}
ptr1 = (ptr1 + 1);
}
}