数组的引入

定义

将一到多个相同类型的对象串连到一起,所组成的类型

  1. #include <iostream>
  2. #include <type_traits>
  3. int main()
  4. {
  5. int a;
  6. int b[10];
  7. std::cout << std::is_same_v<decltype(a),int> << std::endl;
  8. std::cout << std::is_same_v<decltype(b),int[10]> << std::endl;
  9. }

Output:

  1. 1
  2. 1

special one:

  1. #include <iostream>
  2. #include <type_traits>
  3. int main()
  4. {
  5. int a;
  6. std::cin >> a;
  7. int b[a];
  8. }

这样写是错误的,涉及到编译器标准对C++标准的一些扩展:https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_pbt1493403170123.htm

special two:

  1. int a;
  2. int b[1];

两者对应的内存大小一样,但相关的操作是不同的,因此不能把两者混为一谈

special three:
https://en.cppreference.com/w/cpp/language/array
special four:
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

数组的初始化方式

● 缺省初始化

  1. int array[3];//在全局域中定义,缺省初始化,数组中的元素被初始化为0;函数里定义,缺省初始化,元素为随机值。

● 聚合初始化

  1. int array[3] = {1,2}; --> {1,2,0}
  2. int array[3] = { }; --> {0,0,0}

注意事项

  1. 不能使用 auto 来声明数组类型 ```cpp

    include

    include

    include

int main() { auto a = {1,2,3}; std::cout << typeid(a).name(); }

  1. **_Output_**
  2. ```cpp
  3. St16initializer_listIiE //name mangling

Insight:

  1. #include <iostream>
  2. #include <type_traits>
  3. #include <typeinfo>
  4. int main()
  5. {
  6. std::initializer_list<int> a = std::initializer_list<int>{1, 2, 3};
  7. std::operator<<(std::cout, typeid(a).name());
  8. }
  1. 数组不能复制
  2. 元素个数必须是一个常量表达式(编译期可计算的值)
  3. 字符串数组的特殊性
    ```cpp

    include

    include

int main() { char str1[] = “hello”;// —> {‘h’,’e’,’l’,’l’,’o’,’\n’} char str2[] = {‘h’,’e’,’l’,’l’,’o’}; std::cout << std::is_same_v << std::endl; std::cout << std::is_same_v << std::endl; }

  1. **_Output_**
  2. ```cpp
  3. 1
  4. 1

数组的复杂声明

指针数组与数组的指针 :

  1. int* a[3];
  2. int (*)a[3];

声明数组的引用 :

  1. int a[3];
  2. int (&)b[3] = a;

special:不能声明引用数组
引用在底层上仍是指针来实现,但从概念上来说仍是对象的别名,而数组里的元素是对象。

  1. #include <iostream>
  2. int main()
  3. {
  4. int x1,x2,x3;
  5. int& a[3] = {x1,x2,x3};
  6. }

Output:

  1. <source>:6:10: error: declaration of 'a' as array of references
  2. 6 | int& a[3] = {x1,x2,x3};
  3. | ^

数组中的元素访问

  1. 数组对象是一个左值 ```cpp

    include

    include

int main() { int a[3] = {1,2,3}; std::cout << std::is_same_v << std::endl; }

  1. **_Output_**
  2. ```cpp
  3. 1

special:注意这里a要加上(),转化为表达式,根据decltype的用法恰好验证了a作为数组对象的左值性
具体内容可参考这篇文章:
左值与右值

  1. 使用时通常会转换成相应的指针类型
  2. x[y] → *((x) + (y)) ```cpp

    include

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))
}

  1. ```cpp
  2. 1
  3. 1
  4. 1

从数组到指针

数组到指针的隐式转换

  1. 使用数组对象时,通常情况下会产生数组到指针的隐式转换
  2. 隐式转换会丢失一部分类型信息,可以通过声明引用来避免隐式转换
  3. 注意:不要使用 extern 指针来声明数组

    1. Unknown Bounded Array 声明 <br />**_special:数组和指针不能混为一谈_**<br />在项目C++practice里建立test.cppsource.cpp两个文件,代码分别如下:

    ```cpp //test.cpp

    include

void fun(); extern int* array;

int main() { fun(); std::cout << array << std::endl; }

  1. ```cpp
  2. //source.cpp
  3. #include <iostream>
  4. int array[5] = {8,2,3,4,5};
  5. void fun()
  6. {
  7. std::cout << array << std::endl;
  8. }

运行工程,结果如下:

  1. 0x100402010
  2. 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[]

  1. 0x100402010
  2. 0x100402010

获得指向数组开头与结尾的指针

std::(c)begin, std::(c)end

  1. std::cbegin() // const int*
  2. std::begin() // int*

指针算数

  • 增加、减少
  • 比较
  • 求距离
  • 解引用
  • 指针索引 数组

其他操作

求元素的个数

sizeof 方法
std::size 方法
(c)end - (c)begin 方法

  1. #include <iostream>
  2. #include <type_traits>
  3. int main()
  4. {
  5. int a[3] = {1,2,3};
  6. std::cout << std::cend(a) - std::cbegin(a) << std::endl;
  7. std::cout << sizeof(a) / sizeof(int) << std::endl;
  8. std::cout << std::size(a) << std::endl;
  9. }

Output:

  1. 3
  2. 3
  3. 3

元素遍历

基于元素个数
基于 (c)begin/(c)end
基于 range-based for 循环

C字符串

C 字符串本质上也是数组
C 语言提供了额外的函数来支持 C 字符串相关的操作 : strlen, strcmp

多维数组

多维数组的实质无非就是数组的数组:

  1. int array[3][4];
  2. int array[0] --> int[4] --> int array[3]的每一个元素是int[4](从里到外)
  3. -->
  4. int int int int
  5. int int int int
  6. int int int int
  7. -->
  8. (int int int int) (int int int int) (int int int int)

多维数组的聚合初始化

一层大括号 V.S. 多层大括号

多维数组的索引与遍历

使用多个中括号来索引
使用多重循环来遍历
range based for循环:

  1. #include <iostream>
  2. #include <type_traits>
  3. #include <typeinfo>
  4. int main()
  5. {
  6. int array[3][4] = {1,2,3,4,5,6,7,8,9,10};
  7. for(auto& p : array){ //&不能少,否则会发生类型退化
  8. for(auto q : p){
  9. std::cout << q << std::endl;
  10. }
  11. }
  12. }

Insight:

  1. #include <iostream>
  2. int main()
  3. {
  4. int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 0, 0}};
  5. {
  6. int (&__range1)[3][4] = array;
  7. int (*__begin1)[4] = __range1;
  8. int (*__end1)[4] = __range1 + 3L;
  9. for(; __begin1 != __end1; ++__begin1) {
  10. int (&p)[4] = *__begin1;
  11. {
  12. int (&__range2)[4] = p;
  13. int * __begin2 = __range2;
  14. int * __end2 = __range2 + 4L;
  15. for(; __begin2 != __end2; ++__begin2) {
  16. int q = *__begin2;
  17. std::cout.operator<<(q).operator<<(std::endl);
  18. }
  19. }
  20. }
  21. }
  22. }

while循环

指针与多维数组

多维数组可以隐式转换为指针,但只有最高维会进行转换,其它维度的信息会被保留

  1. #include <iostream>
  2. int main()
  3. {
  4. int array[3][4][5];
  5. auto ptr = array;
  6. auto ptr1 = ptr[0];
  7. }

Insight:

  1. #include <iostream>
  2. int main()
  3. {
  4. int array[3][4][5];
  5. int (*ptr)[4][5] = array;
  6. int (*ptr1)[5] = ptr[0];
  7. }

使用类型别名来简化多维数组指针的声明
以上面的代码为例:

  1. #include <iostream>
  2. using A = int[4][5];
  3. int main()
  4. {
  5. int array[3][4][5];
  6. A* ptr = array;
  7. auto ptr1 = ptr[0];
  8. }

再来一段代码:

  1. #include <iostream>
  2. using A = int[4];
  3. int main()
  4. {
  5. A array[3];
  6. //int array[3][4];
  7. A* ptr = array;
  8. auto* ptr1 = array;
  9. auto ptr2 = array;
  10. }

Insight:

  1. #include <iostream>
  2. using A = int[4];
  3. int main()
  4. {
  5. A array[3];
  6. A (*ptr) = array;
  7. int (*ptr1)[4] = array;
  8. }

使用指针来遍历多维数组

  1. #include <iostream>
  2. int main()
  3. {
  4. int array[3][4] = {1,2,3,3,4,7,5,6,7,9,9,10};
  5. auto ptr1 = std::begin(array);
  6. while(ptr1 != std::end(array)){
  7. auto ptr2 = std::begin(*ptr1);
  8. while(ptr2 != std::end(*ptr1)){
  9. std::cout << *ptr2 << std::endl;
  10. ptr2 = ptr2 + 1;
  11. }
  12. ptr1 = ptr1 + 1;
  13. }
  14. }

Insight:

  1. #include <iostream>
  2. int main()
  3. {
  4. int array[3][4] = {{1, 2, 3, 3}, {4, 7, 5, 6}, {7, 9, 9, 10}};
  5. int (*ptr1)[4] = std::begin(array);
  6. while(ptr1 != std::end(array)) {
  7. int * ptr2 = std::begin(*ptr1);
  8. while(ptr2 != std::end(*ptr1)) {
  9. std::cout.operator<<(*ptr2).operator<<(std::endl);
  10. ptr2 = (ptr2 + 1);
  11. }
  12. ptr1 = (ptr1 + 1);
  13. }
  14. }