Skip to content

    C++ 数组:常见操作


    数组作为一种基础的数据结构,我们不仅需要学会如何声明和初始化它,更重要的是掌握如何对数组中的数据进行各种常见操作。这些操作是许多算法和程序的基础。


    数组的常见操作

    下面我们将通过一个例子,演示如何在 C++ 中对数组进行查找、求和、计算平均值以及反转等操作。

    C++
    #include <iostream>  
    #include <algorithm> 
    using namespace std; 
    
    int main() {
        // 声明并初始化一个整数数组
        int arr[] = {64, 34, 25, 12, 22, 11, 90};
        // 计算数组的元素数量:
        // sizeof(arr) 返回整个数组在内存中占用的总字节数。
        // sizeof(arr[0]) 返回数组第一个元素(即单个 int 类型)在内存中占用的字节数。
        // 两者相除,即可得到数组中元素的数量。
        // 这种方法只适用于在当前作用域内完整定义的静态数组。
        int size = sizeof(arr) / sizeof(arr[0]);
        
        // 打印原始数组
        cout << "Original array: ";
        for (int i = 0; i < size; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
        
        // --- 查找元素 ---
        // 目标:检查某个特定值是否存在于数组中,并找出其位置。
        int searchValue = 25; // 我们要查找的值
        bool found = false;   // 标记是否找到
        int index = -1;       // 存储找到的索引
        
        // 遍历数组,逐个比较
        for (int i = 0; i < size; i++) {
            if (arr[i] == searchValue) {
                found = true; // 找到啦!
                index = i;    // 记录它的索引
                break;        // 找到后就可以停止查找了,提高效率
            }
        }
        
        if (found) {
            cout << "Found " << searchValue << " at index " << index << endl;
        } else {
            cout << searchValue << " not found" << endl;
        }
        
        // --- 计算总和 ---
        // 目标:将数组中所有元素的值加起来。
        int sum = 0;
        for (int i = 0; i < size; i++) {
            sum += arr[i]; // 累加每个元素
        }
        cout << "Sum: " << sum << endl;
        
        // --- 计算平均值 ---
        // 目标:计算数组中所有元素的平均值。
        // 注意:需要将 `sum` 转换为 `double` 类型,以避免整数除法造成的精度丢失。
        double average = (double)sum / size;//(double)sum可以把sum强制转换为double形
        cout << "Average: " << average << endl;
        
        // --- 反转数组 ---
        // 目标:将数组的元素顺序颠倒,例如 {1,2,3,4} 变为 {4,3,2,1}。
        // 思路:交换数组两端的元素,然后逐渐向中间靠拢。
        for (int i = 0; i < size / 2; i++) {
            // 使用 std::swap 交换 arr[i] 和 arr[size - 1 - i]
            // std::swap 用法:std::swap (a,b) 即为把 a 和 b 的值互换。
            // 例如:i=0 交换 arr[0] 和 arr[size-1]
            //       i=1 交换 arr[1] 和 arr[size-2]
            swap(arr[i], arr[size - 1 - i]);
        }
        
        cout << "Reversed array: ";
        for (int i = 0; i < size; i++) {
            cout << arr[i] << " ";
        }
        cout << endl;
        
        return 0;
    }

    运行结果示例:

    Plain Text
    Original array: 64 34 25 12 22 11 90 
    Found 25 at index 2
    Sum: 258
    Average: 36.8571
    Reversed array: 90 11 22 12 25 34 64

    数组的限制和注意事项

    尽管数组是 C++ 中非常基础和有用的数据结构,但它也有一些固有的限制和需要特别注意的地方。理解这些限制可以帮助你避免常见的编程错误,编写出更健壮的代码。

    1. 固定大小

    C++ 中的普通数组一旦声明,其大小(能容纳的元素数量)就固定了,在程序运行时不能改变。这意味着如果你声明了一个 int arr[5]; 的数组,它就只能存储 5 个整数。

    尝试访问数组范围之外的内存(称为越界访问)会导致未定义行为。这可能导致程序崩溃、输出错误数据,或者在某些情况下,程序甚至能正常运行,但会在未来引发难以调试的问题。

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int arr[5]; // 声明一个包含5个元素的数组,索引范围是 0 到 4
    
        // arr[5] = 10; // 错误且危险:这是一个越界访问!
                      // 数组只有 arr[0] 到 arr[4]
    
        // 编译时通常不会报错,但运行时会带来问题,切记避免!
    
        return 0;
    }

    如何避免越界访问?

    • 始终检查索引:在循环或任何访问数组元素的代码中,确保索引值在 0size - 1 的有效范围内。

    • 使用常量定义大小:在算法竞赛(一般会限定最大输入量)或大型项目中,通常会使用 const int 定义数组的最大尺寸,这样可以集中管理和避免硬编码数字。

      C++
      const int N = 1000; // 定义一个最大值
      int my_array[N];    // 使用常量声明数组,避免越界

    2. 不能直接复制

    与基本数据类型不同,C++ 中的数组不能像这样直接通过赋值运算符 = 进行整体复制:

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int arr1[5] = {1, 2, 3, 4, 5};
        int arr2[5];
        
        // arr2 = arr1; // 错误:C++ 不允许直接使用赋值运算符复制整个数组
        
        // 正确的复制方法:逐个元素复制
        for (int i = 0; i < 5; i++) {
            arr2[i] = arr1[i];
        }
    
        // 验证复制是否成功
        cout << "arr2 after copy: ";
        for (int i = 0; i < 5; i++) {
            cout << arr2[i] << " ";
        }
        cout << endl;
        
        return 0;
    }

    提示:

    • 对于字符数组(C 风格字符串),你可以使用 strcpy 函数进行复制(但要注意目标数组空间是否足够)。

    3. 不能直接返回

    C++ 函数不能直接返回一个完整的数组。当你尝试返回数组名时,实际上返回的是指向该数组第一个元素的指针

    C++
    #include <iostream>
    using namespace std;
    
    // 错误示范:不能直接返回数组
    // int[] getArrayBad() {
    //     int arr[5] = {1, 2, 3, 4, 5}; // 局部数组在函数结束后会被销毁
    //     return arr; // 返回一个指向已销毁内存的指针,非常危险!
    // }
    
    // 正确的方法:返回一个指向数组的指针
    // 注意:这里使用 static 关键字,确保数组在函数结束后依然存在于内存中。
    //      但通常不推荐返回局部静态数组,因为它可能引起意想不到的副作用。
    int* getArray() {
        static int arr[5] = {1, 2, 3, 4, 5}; // static 局部变量在程序生命周期内都存在
        cout << "Inside getArray: " << arr[0] << endl;
        return arr; // 返回数组第一个元素的地址
    }
    
    int main() {
        int* myArrPtr = getArray(); // 接收返回的指针
        
        cout << "Elements from returned array: ";
        for (int i = 0; i < 5; i++) {
            cout << myArrPtr[i] << " "; // 通过指针访问数组元素
        }
        cout << endl;
        
        return 0;
    }

    更安全的替代方案:

    • 通过参数传递:让函数接受一个数组(或指针)作为参数,并在函数内部修改它(正如我们前面“数组作为函数参数”一节所学)。

    如何使程序“崩溃”(或产生未定义行为)

    了解这些常见错误可以帮助你避免它们。尝试以下操作来观察程序的行为,并理解其危险性:

    1. 数组越界: 试图访问数组的有效索引范围之外的元素。

      C++
      #include <iostream>
      using namespace std;
      
      int main() {
          int arr[5] = {1, 2, 3, 4, 5};
          // 尝试访问索引 10,但数组只有索引 0 到 4
          cout << "Attempting to access arr[10]: " << arr[10] << endl; // 越界访问!
          return 0;
      }
      • 你会看到什么? 程序可能会崩溃(段错误),或者打印出一个随机的、无意义的数字。这是因为你正在读取不属于你程序内存区域的数据。
    2. 未初始化的数组: 声明数组后,如果没有明确地初始化所有元素,那么它们会包含“垃圾值”(即之前内存中残留的随机数据)。

      C++
      #include <iostream>
      using namespace std;
      
      int main() {
          int arr[5]; // 声明,但未完全初始化
          cout << "Uninitialized array elements: ";
          for (int i = 0; i < 5; i++) {
              cout << arr[i] << " "; // 打印未定义的值
          }
          cout << endl;
          return 0;
      }
      • 你会看到什么? 你会看到一些看起来像随机数的奇怪数字。这些值是不可预测的,并且在每次程序运行时可能都不同。
    3. 字符串缓冲区溢出: 当你尝试将一个比目标字符数组更大的字符串复制进去时,就会发生缓冲区溢出。这会写入到数组之外的内存区域。

      C++
      #include <iostream>
      #include <cstring> // For strcpy
      using namespace std;
      
      int main() {
          // str 只能容纳 5 个字符,包括末尾的空字符 '\0'。
          // 所以它最多只能存储 4 个实际字符。
          char str[5]; 
          
          // "Hello World" 有 11 个字符 + 1 个空字符 = 12 个字节。
          // 这远超 str 的 5 个字节容量。
          strcpy(str, "Hello World"); // 缓冲区溢出!
          
          cout << "Overflowed string: " << str << endl;
          return 0;
      }
      • 你会看到什么? 程序可能会崩溃,或者打印出部分字符串,后面跟着一些奇怪的字符,甚至可能影响到其他变量的值。这是非常危险的,因为它可以被恶意利用。

    通过了解这些限制和常见的陷阱,你就能更好地掌握 C++ 数组的使用,并编写出更安全、更可靠的代码。当你遇到上述问题时,首先检查你的数组索引、初始化以及是否使用了合适的标准库容器。