C++入门教程:数组、字符串与指针入门

本文是 C++ 系列教程的第 4 篇。上一篇讲解了函数与作用域,本篇深入讲解数组、字符串和指针:一维/多维数组、C 风格字符串与 std::string、指针基础、指针与数组的关系、引用与 const 限定。

一、数组

1.1 一维数组

数组是同类型元素的集合,内存中连续存储:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    // 声明与初始化
    int scores[5];                 // 未初始化(含垃圾值)
    int nums[5] = {1, 2, 3, 4, 5}; // 列表初始化
    int partial[5] = {1, 2};       // 其余补 0
    int autoSize[] = {10, 20, 30}; // 自动推导大小为 3

    // 访问与修改
    cout << "nums[0] = " << nums[0] << endl;      // 1
    nums[2] = 100;                                 // 修改第三个元素
    cout << "修改后 nums[2] = " << nums[2] << endl; // 100

    // 遍历
    for (int i = 0; i < 5; i++) {
        cout << nums[i] << " ";
    }
    cout << endl;
    return 0;
}

1.2 数组越界(危险!)

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int arr[3] = {1, 2, 3};
    // arr[3] 越界访问:编译不报错,但行为未定义
    // 可能读到垃圾值,甚至修改其他内存导致崩溃
    cout << "越界访问 arr[3] = " << arr[3] << "(未定义行为!)" << endl;
    return 0;
}

数组下标从 0 开始,合法范围是 0 ~ 大小-1。C++ 不检查越界,这是很多 bug 的根源。

1.3 多维数组

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    // 3x4 二维数组(3 行 4 列)
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // 遍历
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            cout << matrix[i][j] << "\t";
        }
        cout << endl;
    }
    return 0;
}

1.4 数组与范围 for

C++11 起可用范围 for 遍历数组:

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int nums[] = {5, 10, 15, 20};

    // 范围 for:只读
    for (int n : nums) {
        cout << n << " ";
    }
    cout << endl;

    // 用引用修改元素
    for (int &n : nums) {
        n *= 2;
    }
    for (int n : nums) {
        cout << n << " ";
    }
    cout << endl;  // 10 20 30 40
    return 0;
}

二、字符串

2.1 C 风格字符串

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    // C 风格字符串:以 '\0' 结尾的字符数组
    char str1[] = "Hello";          // 自动加 '\0'
    char str2[] = {'H', 'i', '\0'}; // 手动加 '\0'
    char str3[20];

    // 常用函数
    strcpy(str3, "World");          // 复制
    strcat(str3, " C++");           // 拼接
    cout << "str3 = " << str3 << endl;              // World C++
    cout << "strlen(str1) = " << strlen(str1) << endl;  // 5(不含'\0')

    // 比较
    if (strcmp(str1, "Hello") == 0) {
        cout << "相等" << endl;
    }
    return 0;
}

2.2 std::string(推荐)

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1 = "Hello";
    string s2 = " C++";

    // 拼接
    string s3 = s1 + s2;
    cout << "s3 = " << s3 << endl;          // Hello C++

    // 长度与访问
    cout << "长度: " << s3.length() << endl;  // 9
    cout << "首字符: " << s3[0] << endl;      // H

    // 查找
    size_t pos = s3.find("C++");
    if (pos != string::npos) {
        cout << "找到 C++ 在位置 " << pos << endl;  // 6
    }

    // 截取子串
    string sub = s3.substr(6, 3);
    cout << "子串: " << sub << endl;          // C++

    // 比较
    if (s1 < s2) {
        cout << "s1 字典序小于 s2" << endl;
    }
    return 0;
}

2.3 string 与 C 字符串互转

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    // string -> const char*
    string s = "Hello";
    const char *cstr = s.c_str();
    cout << "C 字符串: " << cstr << endl;

    // const char* -> string
    const char *p = "World";
    string s2 = p;
    cout << "string: " << s2 << endl;
    return 0;
}

三、指针基础

3.1 指针的概念

指针是存储地址的变量

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int num = 42;

    // 声明指针并取地址
    int *ptr = &num;   // & 取地址,* 声明指针

    cout << "num 的值: " << num << endl;         // 42
    cout << "num 的地址: " << &num << endl;      // 如 0x61ff08
    cout << "ptr 的值: " << ptr << endl;         // 同 num 地址
    cout << "ptr 解引用: " << *ptr << endl;      // 42

    // 通过指针修改值
    *ptr = 100;
    cout << "修改后 num = " << num << endl;      // 100
    return 0;
}

3.2 空指针与野指针

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int *p1 = nullptr;   // C++11 空指针(推荐)
    int *p2 = NULL;      // C 风格(不推荐)
    int *p3 = 0;         // 等价于空指针

    if (p1 == nullptr) {
        cout << "p1 是空指针" << endl;
    }

    // 野指针:未初始化的指针,指向未知地址
    int *wild;           // 未初始化
    // *wild = 10;       // 危险!解引用野指针是未定义行为

    // 正确做法:使用前必须指向有效内存
    int value = 5;
    int *safe = &value;
    cout << *safe << endl;  // 5
    return 0;
}

3.3 指针的运算

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40};

    int *p = arr;              // 数组名退化为首元素地址
    cout << "*p = " << *p << endl;            // 10
    cout << "*(p+1) = " << *(p + 1) << endl;  // 20(指针+1 跳过 1 个 int)
    cout << "*(p+2) = " << *(p + 2) << endl;  // 30

    // 指针算术与下标等价
    cout << "p[1] = " << p[1] << endl;        // 20
    cout << "*(arr+1) = " << *(arr + 1) << endl;  // 20

    // 遍历
    for (int *q = arr; q < arr + 4; q++) {
        cout << *q << " ";
    }
    cout << endl;  // 10 20 30 40
    return 0;
}

四、指针与数组的关系

4.1 数组名与指针

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int arr[3] = {1, 2, 3};

    // 数组名 arr 可视为指向首元素的常量指针
    cout << "arr = " << arr << endl;         // 首地址
    cout << "&arr[0] = " << &arr[0] << endl; // 同首地址
    cout << "a[i] == *(a+i): " << (arr[1] == *(arr + 1)) << endl;  // 1(相等)

    // 数组名不可被赋值(不是真正的变量指针)
    // arr = &arr[1];  // 错误!

    // 但可以用指针指向数组并移动
    int *p = arr;
    p++;   // 合法,指针可以移动
    cout << "*p = " << *p << endl;  // 2
    return 0;
}

4.2 指针作为函数参数(传数组)

cpp 复制代码
#include <iostream>
using namespace std;

// 数组参数退化为指针,需要同时传大小
int sumArray(int arr[], int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return sum;
}

// 等价写法:int *arr
int sumArrayPtr(int *arr, int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += *(arr + i);
    }
    return sum;
}

int main() {
    int scores[] = {85, 90, 78, 92};
    cout << "总分 = " << sumArray(scores, 4) << endl;      // 345
    cout << "总分 = " << sumArrayPtr(scores, 4) << endl;   // 345
    return 0;
}

五、引用

5.1 引用基础

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int num = 10;

    // 引用:变量的别名,必须初始化且不能改绑
    int &ref = num;
    ref = 20;   // 通过引用修改原变量

    cout << "num = " << num << endl;  // 20
    cout << "ref = " << ref << endl;  // 20

    // 引用不是独立变量,修改引用就是修改原变量
    int other = 50;
    ref = other;   // 这是把 other 的值赋给 num,不是让 ref 绑定 other
    cout << "num = " << num << endl;  // 50
    return 0;
}

5.2 指针 vs 引用

维度 指针 引用
定义 存储地址的变量 变量的别名
初始化 可以不初始化(野指针) 必须初始化
重新绑定 可以指向其他对象 不可改变
空值 可以为 nullptr 不能为空
语法 需要 * 解引用 直接使用
适用 动态分配、可选对象、数组遍历 函数参数、运算符重载

六、const 限定

6.1 顶层 const 与底层 const

cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int num = 10;

    // 顶层 const:指针本身不可变
    int *const pTop = &num;   // pTop 不能指向别处
    *pTop = 20;               // 但可以修改指向的值
    cout << "num = " << num << endl;  // 20

    // 底层 const:指向的对象不可变
    const int *pLow = &num;   // 不能通过 pLow 修改值
    // *pLow = 30;            // 错误!
    pLow = nullptr;           // 但 pLow 可以指向别处

    // 同时 const
    const int *const pBoth = &num;
    // pBoth = nullptr;       // 错误!
    // *pBoth = 30;           // 错误!
    return 0;
}

6.2 const 指针传参惯例

cpp 复制代码
#include <iostream>
using namespace std;

// const int* 表示只读访问,不修改数组
void printArray(const int arr[], int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    // arr[0] = 99;  // 错误!const 数组不可修改
}

int main() {
    int data[] = {1, 2, 3, 4};
    printArray(data, 4);  // 1 2 3 4
    return 0;
}

七、实战:字符串反转与回文判断

综合本篇知识,实现字符串处理和回文判断:

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 反转字符串(用指针实现)
void reverseStr(char *str) {
    int len = 0;
    while (str[len] != '\0') len++;   // 求长度

    char *left = str;
    char *right = str + len - 1;
    while (left < right) {
        char temp = *left;
        *left = *right;
        *right = temp;
        left++;
        right--;
    }
}

// 判断回文(用 std::string)
bool isPalindrome(const string &s) {
    int left = 0, right = s.length() - 1;
    while (left < right) {
        if (s[left] != s[right]) return false;
        left++;
        right--;
    }
    return true;
}

int main() {
    // 1. 字符数组反转
    char text[] = "HelloWorld";
    reverseStr(text);
    cout << "反转后: " << text << endl;  // dlroWolleH

    // 2. 回文判断
    string words[] = {"level", "hello", "上海自来水来自海上"};
    for (const string &w : words) {
        cout << "\"" << w << "\" " << (isPalindrome(w) ? "是回文" : "不是回文") << endl;
    }
    return 0;
}

运行示例:

复制代码
反转后: dlroWolleH
"level" 是回文
"hello" 不是回文
"上海自来水来自海上" 是回文

总结

本篇讲解了数组(一维/多维/范围 for)、字符串(C 风格与 std::string)、指针基础(地址/解引用/运算/野指针)、指针与数组的关系、引用与指针的区别、const 限定(顶层/底层),并用字符串反转和回文判断串联实战。重点掌握:数组越界的危害、std::string 的常用操作、指针算术、引用与指针的选择。

下一篇将讲解结构体、枚举与类初探,正式进入面向对象世界,敬请期待!

相关推荐
啦啦啦啦啦zzzz1 小时前
共享内存的网络聊天室
linux·服务器·网络·c++·网络编程
pluviophile_s1 小时前
数据结构:第7讲:图
数据结构·笔记
林森lsjs1 小时前
零基础吃透二叉树:定义、遍历与高频算法 —数据结构柒
java·开发语言·数据结构·算法·二叉树
船厂电气自动化ai大模型1 小时前
AI大模型与数学·第56课 快速傅里叶变换FFT:DFT高效优化算法,图像、音频、扩散模型工程加速核心工具
数据结构·人工智能·深度学习·算法·机器学习
203号居民1 小时前
【无标题】LeetCode hot 100 —54. 螺旋矩阵
算法
蜕变的土豆1 小时前
SQLite文件加密完整教程:从零学会SQLCipher全库加密(C++实战)
c++·sqlite
ai产品老杨1 小时前
越界检测算法接入 AI 视频分析平台的流程与误报优化指南
人工智能·算法·音视频
bluesky9612221 小时前
malloc 和 realloc C/C++
c语言·c++·算法
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<二十三>:图像平滑之非线性滤波
c++·人工智能·opencv·计算机视觉