20.指针和一维数组

20.指针-指针和数组-指针和一维数组


20.1 知识点

指针和一维数组的关系

数组在内存中是连续存储的,因此我们只要利用指针获取到数组一开始的位置,就可以通过指针的自增减获取到数组中的所有信息。

建立关系

关键点:

  • 指针类型需要和数组类型一致。
int array[4] = { 1, 2, 3, 4 };
int* ptr = nullptr;
  • 获取数组一开始的位置:

    • 方式一:数组变量名即可表示数组的首地址。

      ptr = array;
      cout << ptr << endl; // 000000204E6FF928
      
    • 方式二:获取数组第一个元素的地址。

      ptr = &array[0];
      cout << ptr << endl; // 000000204E6FF928
      

利用指针获取数组元素

  • 首地址便是第一个元素。

    cout << *ptr << endl; // 1
    
  • 指针自增减可以移动元素类型占用内存空间的单位。

    cout << *++ptr << endl; // 2
    cout << *++ptr << endl; // 3
    cout << *++ptr << endl; // 4
    cout << *--ptr << endl; // 3
    
  • 还可以直接加减,但是如果没有赋值或者使用 +=-= 的话,指针变量是不会改变的。

    cout << *(ptr - 1) << endl; // 2
    cout << *ptr << endl; // 3
    
  • 也可以直接减 n 个单位。

    cout << *(ptr -= 2) << endl; // 1
    cout << *(ptr += 3) << endl; // 4
    
  • 一定注意,不能越界,否则得到的是非期望的值。

    cout << *(ptr + 1) << endl; // -858993460
    
  • 普通指针指向数组,直接用 [x] 访问元素,当成数组用。数组下标访问 p[x] 本质上是语法糖,它实际上等价于 *(p + x)。只要指针 p 是有效的,并且指向数组或内存块,p[x] 都是合法的操作。

    ptr = array;
    cout << ptr[0] << endl; // 1
    cout << ptr[1] << endl; // 2
    cout << ptr[2] << endl; // 3
    cout << ptr[3] << endl; // 4
    
  • ptr 和数组的区别:

    • 虽然指针可以像数组一样通过下标访问,但指针和数组是不同的概念:
      • 数组变量本质上是一个固定的内存地址,大小和范围是固定的。
      • 指针变量则是一个可以动态更改地址的变量,它的范围取决于其指向的内存。
      • 注意:如果指针没有指向有效的内存(如未初始化),使用 ptr[x] 会导致未定义行为。
    int* q1 = nullptr;
    // cout << q1[0] << endl; // 错误:未定义行为,指针未初始化指向有效内存
    int* q2;
    q2 = (int*)malloc(sizeof(int) * 4); // 分配了数组内存
    q2[0] = 10; // 合法,操作动态分配的内存
    cout << q2[0] << endl; // 10
    free(q2); // 使用 free 释放之前通过 malloc 分配的内存
    

数组名当做指针变量使用

数组名不能像刚才指针一样去改变它指向的内存地址。我们只能利用它的地址来进行计算,不能改变它的指向。

cout << *array << endl; // 1
cout << *(array + 1) << endl; // 2
cout << *(array + 2) << endl; // 3
cout << *(array + 3) << endl; // 4

利用指针遍历数组

  • 注意:我们无法直接通过指针获取到数组的容量,因为指针其实只是记录了一个地址而已,通过地址并不能知道数组的容量。
int* p = array;
int* p2 = array;

for (int i = 0; i < sizeof(array) / sizeof(int); i++)
{
    // 以下是多种打印数组元素的方式
    // 第一种 去偏移计算
    cout << *(p + i) << endl;
    // 第二种 去自增
    cout << *p2++ << endl;
    // 第三种 把数组名当成指针用,但是不能改变数组名的指向
    cout << *(array + i) << endl;
    // 传统方式
    cout << array[i] << endl;
}
// 输出4次1234

总结

  • 指针变量类型需要和数组保持一致。
  • 数组名字和数组首元素的地址都可以获取到数组开始的位置(首地址)。
  • 利用指针的自增自减(加减运算)可以获取到数组中不同的元素,需要注意越界问题。
  • 数组名可以用来当作指针使用,但是我们不能去改变数组指向的内容(不能对数组进行重定向)。

20.2 知识点代码

Lesson20_指针_指针和一维数组.cpp

#include <iostream>
using namespace std;
int main()
{
    std::cout << "指针与一维数组\n";

    #pragma region 知识点一 指针和一维数组的关系

    //数组在内存中是连续存储的
    //因此我们只要利用指针获取到数组一开始的位置
    //就可以通过指针的自增减获取到数组中的所有信息

    #pragma endregion

    #pragma region 知识点二 建立关系

    //关键点:
    //1.指针类型需要和数组类型一致
    int array[4] = { 1,2,3,4 };
    int* ptr = nullptr;

    //2.获取数组一开始的位置
    //方式一:数组变量名即可表示数组的首地址
    ptr = array;
    cout << ptr << endl;//000000204E6FF928

    //方式二:获取数组第一个元素的地址
    ptr = &array[0];
    cout << ptr << endl;//000000204E6FF928

    #pragma endregion

    #pragma region 知识点三 利用指针获取数组元素

    //首地址便是第一个元素
    cout << *ptr << endl;//1

    //指针自增减可以移动 元素类型占用内存空间 个单位
    cout << *++ptr << endl;//2
    cout << *++ptr << endl;//3
    cout << *++ptr << endl;//4
    cout << *--ptr << endl;//3

    //还可以直接 + - 但是如果没有赋值 或者使用+= -=的话 指针变量是不会改变的
    cout << *(ptr - 1) << endl;//2
    cout << *ptr << endl;//3

    //也可以直接减n个单位
    cout << *(ptr -= 2) << endl;//1
    cout << *(ptr += 3) << endl;//4

    //一定注意 不能越界 否则得到的是非期望的值
    cout << *(ptr + 1) << endl;//-858993460


    //普通指针指向数组 直接用[x]访问元素 当成数组用
    //组下标访问 p[x] 本质上是语法糖,它实际上等价于* (p + x)。只要指针 p 是有效的,并且指向数组或内存块,p[x] 都是合法的操作。
    ptr = array;
    cout << ptr[0] << endl;//1
    cout << ptr[1] << endl;//2
    cout << ptr[2] << endl;//3
    cout << ptr[3] << endl;//4

    //ptr 和数组的区别
    //虽然指针可以像数组一样通过下标访问,但指针和数组是不同的概念:
    //数组变量本质上是一个固定的内存地址,大小和范围是固定的。
    //指针变量则是一个可以动态更改地址的变量,它的范围取决于其指向的内存。
    //注意:如果指针没有指向有效的内存(如未初始化),使用 ptr[x] 会导致未定义行为。

    int* q1 = nullptr;
    //cout << q1[0] << endl;// 错误:未定义行为,指针未初始化指向有效内存
    int* q2;
    q2 = (int*)malloc(sizeof(int) * 4); // 分配了数组内存
    q2[0] = 10; // 合法,操作动态分配的内存
    cout << q2[0] << endl;//10
    free(q2);//使用 free 释放之前通过 malloc 分配的内存

    #pragma endregion

    #pragma region 知识点四 数组名当做指针变量使用

    //数组名不能像刚才指针一样 去改变它指向的内存地址
    //我们只能利用它的地址来进行计算 不能改变

    cout << *array << endl;//1
    cout << *(array + 1) << endl;//2
    cout << *(array + 2) << endl;//3
    cout << *(array + 3) << endl;//4

    #pragma endregion

    #pragma region 知识点五 利用指针遍历数组

    //注意:我们无法直接通过指针获取到数组的容量
    //因为指针其实只是记录了一个地址而已,通过地址并不能知道数组容量

    int* p = array;
    int* p2 = array;

    for (int i = 0; i < sizeof(array) / sizeof(int); i++)
    {
        //以下是多种打印数组元素的方式
        //第一种 去偏移计算
        cout << *(p + i) << endl;
        //第二种 去自增
        cout << *p2++ << endl;
        //第三种 把数组名当成指针用 但是不能改变数组名的指向
        cout << *(array + i) << endl;
        //传统方式
        cout << array[i] << endl;
    }
    //输出4次1234

    #pragma endregion

    #pragma region 总结

    //1.指针变量类型需要和数组保持一致
    //2.数组名字和数组首元素的地址 都可以获取到数组开始的位置(首地址)
    //3.利用指针的自增自减(加减运算)可以获取到数组中不同的元素,需要注意越界问题
    //4.数组名可以用来当作指针使用,但是我们不能去改变数组指向的内容(不能对数组进行重定向)

    #pragma endregion

}

20.3 练习题

声明一个包含 5 个整数的数组 arr 并使用指针访问数组中的每一个元素,输出它们的值

int arr[5] = { 4, 5, 6, 7, 8 };
int* p = &arr[0];
int* p2 = arr;

for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
{
    cout << *p++ << endl;
    cout << *(p2 + i) << endl;
}

编写一个程序,使用指针遍历一个整数数组,统计数组中大于10的元素数量,并将这些大于10的元素放置到数组的前部,其余元素保持不变

注意:不能使用额外的数组
提示:使用两个指针(首尾指针)

int array[10] = { 11, 4, 5, 12, 6, 55, 3, 77, 1, 2 };

//首部地址
int* p = array; // &array[0]

//尾部地址
int* p2 = array + 9;

//用来记录有几个数大于10
int num = 0;

//没有交叉位置 才继续循环
//地址中存储的16进制数比大小
while (p < p2)
{
    //头部指针指向的值大于10
    if (*p > 10)
    {
        num++;
        p++;
    }
    //尾部指针指向的值大于10
    else if (*p2 > 10)
    {
        //记录一个大于10的情况
        num++;
        //交换首尾地址指向空间中的值
        int tmp = *p;
        *p = *p2;
        *p2 = tmp;

        //交换完毕后
        //头部往尾部移动
        p++;
        //尾部往头部移动
        p2--;
    }
    //尾部指针的值 小于10的 直接往前移动即可
    else
    {
        //尾部往头部移动
        p2--;
    }
}

for (int i = 0; i < 10; i++)
{
    cout << array[i] << ", ";
}
cout << endl;
cout << "大于10的有" << num << "个" << endl;

输出:

11, 77, 55, 12, 6, 5, 3, 4, 1, 2,
大于10的有4个

20.4 练习题代码

Lesson20_练习题.cpp

#include <iostream>
using namespace std;
int main()
{
    std::cout << "指针和一维数组练习题\n";

    #pragma region 练习题一

    /*声明一个包含 5 个整数的数组 arr
    并使用指针访问数组中的每一个元素,输出它们的值*/

    //int arr[5] = { 4,5,6,7,8 };
    //int* p = &arr[0];
    //int* p2 = arr;
    //for (int i = 0; i < sizeof(arr)/sizeof(int); i++)
    //{
    //    cout << *p++ << endl;
    //    cout << *(p2 + i) << endl;
    //}

    #pragma endregion

    #pragma region 练习题二

    /*编写一个程序,使用指针遍历一个整数数组,统计数组中大于10的元素数量,
      并将这些大于10的元素放置到数组的前部,其余元素保持不变
    注意:不能使用额外的数组
    提示:使用两个指针(首尾指针)*/

    int array[10] = { 11, 4, 5, 12, 6, 55, 3, 77, 1, 2 };
    //首部地址
    int* p = array;//&array[0]
    //尾部地址
    int* p2 = array + 9;
    //用来记录有几个数大于10
    int num = 0;
    //没有交叉位置 才继续循环
    //地址中存储的16进制数比大小
    while (p < p2)
    {
        //头部指针指向的值大于10
        if (*p > 10)
        {
            num++;
            p++;
        }
        //尾部指针指向的值大于10
        else if(*p2 > 10)
        {
            //记录一个大于10的情况
            num++;
            //交换首尾地址指向空间中的值
            int tmp = *p;
            *p = *p2;
            *p2 = tmp;

            //交换完毕后
            //头部往尾部移动
            p++;
            //尾部往头部移动
            p2--;
        }
        //尾部指针的值 小于10的 直接往前移动即可
        else
        {
            //尾部往头部移动
            p2--;
        }
    }

    for (int i = 0; i < 10; i++)
    {
        cout << array[i] << ", ";
    }
    cout << endl;
    cout << "大于10的有" << num << "个" << endl;
    //11, 77, 55, 12, 6, 5, 3, 4, 1, 2,
    //大于10的有4个

    #pragma endregion

}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏