gpt4 book ai didi

c++ - 如何在 C++ 中使用数组?

转载 作者:行者123 更新时间:2023-12-01 16:10:16 25 4
gpt4 key购买 nike

C++ 从 C 继承了数组,它们几乎无处不在。 C++ 提供了更易于使用且不易出错的抽象( std::vector<T> 自 C++98 和 std::array<T, n> C++11 ),因此对数组的需求不像在 C 中那样频繁出现。但是,当您阅读遗留代码或与用 C 编写的库进行交互时,您应该牢牢掌握数组的工作原理。
本FAQ分为五个部分:

  • arrays on the type level and accessing elements
  • array creation and initialization
  • assignment and parameter passing
  • multidimensional arrays and arrays of pointers
  • common pitfalls when using arrays

  • 如果您觉得此常见问题解答中缺少某些重要内容,请写下答案并将其作为附加部分链接到此处。
    在下面的文本中,“数组”表示“C 数组”,而不是类模板 std::array .假定具备 C 声明符语法的基本知识。注意 new的手册使用和 delete如下所示,面对异常是极其危险的,但这就是 another FAQ 的主题。 .

    _(注意:这是 [Stack Overflow 的 C++ 常见问题解答](https://stackoverflow.com/questions/tagged/c++-faq)的入口。如果您想批评以这种形式提供常见问题解答的想法,那么[开始这一切的元数据发布](https://meta.stackexchange.com/questions/68647/setting-up-a-faq-for-the-c-tag) 将是这样做的地方. 该问题的答案在 [C++ 聊天室](https://chat.stackoverflow.com/rooms/10/c-lounge) 中被监控,FAQ 的想法首先在那里开始,所以你的答案很可能被提出这个想法的人阅读。)_

    最佳答案

    类型级别的数组

    数组类型表示为 T[n]哪里T是元素类型和 n是一个正大小,数组中元素的数量。数组类型是元素类型和大小的乘积类型。如果这些成分中的一种或两种不同,您将获得不同的类型:

    #include <type_traits>

    static_assert(!std::is_same<int[8], float[8]>::value, "distinct element type");
    static_assert(!std::is_same<int[8], int[9]>::value, "distinct size");

    请注意,大小是类型的一部分,即不同大小的数组类型是不兼容的类型,彼此之间绝对没有任何关系。 sizeof(T[n])相当于 n * sizeof(T) .

    数组到指针衰减
    T[n]之间唯一的“连接”和 T[m]是这两种类型都可以隐式转换为 T* ,并且此转换的结果是指向数组第一个元素的指针。也就是说,任何地方 T*需要,您可以提供 T[n] ,编译器会默默地提供那个指针:
                      +---+---+---+---+---+---+---+---+
    the_actual_array: | | | | | | | | | int[8]
    +---+---+---+---+---+---+---+---+
    ^
    |
    |
    |
    | pointer_to_the_first_element int*

    这种转换被称为“数组到指针衰减”,它是一个主要的混淆源。数组的大小在此过程中丢失,因为它不再是类型 ( T* ) 的一部分。优点:在类型级别忘记数组的大小允许指针指向任何大小的数组的第一个元素。缺点:给定一个指向数组第一个(或任何其他)元素的指针,无法检测该数组有多大或指针相对于数组边界所指向的确切位置。 Pointers are extremely stupid .

    数组不是指针

    只要认为有用,编译器就会默默地生成一个指向数组第一个元素的指针,也就是说,每当一个操作在数组上失败但在一个指针上成功时。这种从数组到指针的转换是微不足道的,因为结果指针值只是数组的地址。请注意,指针不作为数组本身(或内存中的任何其他位置)的一部分存储。 数组不是指针。
    static_assert(!std::is_same<int[8], int*>::value, "an array is not a pointer");

    数组不会衰减为指向其第一个元素的指针的一个重要上下文是 &运算符应用于它。在这种情况下, &运算符产生一个指向整个数组的指针,而不仅仅是指向它的第一个元素的指针。尽管在这种情况下值(地址)相同,但指向数组第一个元素的指针和指向整个数组的指针是完全不同的类型:
    static_assert(!std::is_same<int*, int(*)[8]>::value, "distinct element type");

    以下 ASCII 艺术解释了这种区别:
          +-----------------------------------+
    | +---+---+---+---+---+---+---+---+ |
    +---> | | | | | | | | | | | int[8]
    | | +---+---+---+---+---+---+---+---+ |
    | +---^-------------------------------+
    | |
    | |
    | |
    | | pointer_to_the_first_element int*
    |
    | pointer_to_the_entire_array int(*)[8]

    注意第一个元素的指针是如何只指向一个整数(描绘成一个小盒子),而指向整个数组的指针指向一个由 8 个整数组成的数组(描绘成一个大盒子)。

    同样的情况出现在类里面,而且可能更明显。指向对象的指针和指向其第一个数据成员的指针具有相同的值(相同的地址),但它们是完全不同的类型。

    如果您不熟悉 C 声明符语法,请使用类型中的括号 int(*)[8]是必不可少的:
  • int(*)[8]是一个指向 8 个整数数组的指针。
  • int*[8]是一个包含 8 个指针的数组,每个元素的类型为 int* .

  • 访问元素

    C++ 提供了两种语法变体来访问数组的各个元素。
    它们都不优于另一个,您应该熟悉两者。

    指针运算

    给定一个指针 p到数组的第一个元素,表达式 p+i产生一个指向数组第 i 个元素的指针。通过之后取消引用该指针,可以访问单个元素:
    std::cout << *(x+3) << ", " << *(x+7) << std::endl;

    x表示一个数组,那么数组到指针的衰减就会开始,因为添加一个数组和一个整数是没有意义的(对数组没有加号操作),但是添加一个指针和一个整数是有意义的:
       +---+---+---+---+---+---+---+---+
    x: | | | | | | | | | int[8]
    +---+---+---+---+---+---+---+---+
    ^ ^ ^
    | | |
    | | |
    | | |
    x+0 | x+3 | x+7 | int*

    (注意隐式生成的指针没有名字,所以我写了 x+0 来标识它。)

    另一方面,如果 x表示指向数组第一个(或任何其他)元素的指针,则不需要数组到指针衰减,因为 i 所在的指针将要添加的已经存在:
       +---+---+---+---+---+---+---+---+
    | | | | | | | | | int[8]
    +---+---+---+---+---+---+---+---+
    ^ ^ ^
    | | |
    | | |
    +-|-+ | |
    x: | | | x+3 | x+7 | int*
    +---+

    请注意,在所描述的情况下, x是一个指针变量(可以通过 x 旁边的小框识别),但它也可能是返回指针的函数的结果(或任何其他类型的 T* 表达式)。

    索引运算符

    由于语法 *(x+i)有点笨拙,C++ 提供了替代语法 x[i] :
    std::cout << x[3] << ", " << x[7] << std::endl;

    由于加法是可交换的,因此以下代码完全相同:
    std::cout << 3[x] << ", " << 7[x] << std::endl;

    索引运算符的定义导致以下有趣的等价:
    &x[i]  ==  &*(x+i)  ==  x+i

    然而, &x[0]一般不等于 x .前者是指针,后者是数组。只有当上下文触发数组到指针衰减时才能 x&x[0]可以互换使用。例如:
    T* p = &array[0];  // rewritten as &*(array+0), decay happens due to the addition
    T* q = array; // decay happens due to the assignment

    在第一行,编译器检测到从一个指针到一个指针的赋值,这很容易成功。在第二行,它检测从数组到指针的赋值。由于这是无意义的(但指向指针赋值的指针是有意义的),数组到指针的衰减照常开始。

    范围
    T[n] 类型的数组有 n元素,索引自 0n-1 ;没有元素 n .然而,为了支持半开范围(其中开头包含,结尾不包含),C++ 允许计算指向(不存在的)第 n 个元素的指针,但取消引用该指针是非法的:
       +---+---+---+---+---+---+---+---+....
    x: | | | | | | | | | . int[8]
    +---+---+---+---+---+---+---+---+....
    ^ ^
    | |
    | |
    | |
    x+0 | x+8 | int*

    例如,如果您想对数组进行排序,以下两种方法同样有效:
    std::sort(x + 0, x + n);
    std::sort(&x[0], &x[0] + n);

    注意提供 &x[n]是违法的作为第二个参数,因为这相当于 &*(x+n) ,以及子表达式 *(x+n)技术上调用 undefined behavior在 C++ 中(但不是在 C99 中)。

    另请注意,您可以简单地提供 x作为第一个论点。这对我来说有点太简洁了,而且它也使编译器的模板参数推导有点困难,因为在这种情况下,第一个参数是一个数组,而第二个参数是一个指针。 (再次,数组到指针的衰减开始了。)

    关于c++ - 如何在 C++ 中使用数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4810664/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com