首页 > 其他分享 >简单理解【指针数组】与【数组指针】

简单理解【指针数组】与【数组指针】

时间:2024-07-23 20:55:31浏览次数:7  
标签:10 arr int 地址 理解 数组 指针

2024-07-22 笔记 - 4
2024-07-23 补充笔记

【指针数组】

顾名思义,就是用来存储地址的数组,所有的元素全中存储的全部都是地址,每一个元素都可以理解为是一个指针变量。

char* arr[10];
int* arr2[10];
char** arr3[5];
① 举例,多种访问方式
int arr[5] = {1, 2, 3, 4, 5}, arr2[5] = {6, 7, 8, 9, 10}, arr3[5] = {11, 12, 13, 14, 15};
int* a[3] = { arr, arr2, arr3 };
//这个指针变量中每一个元素的类型都是int*;
//可以这样访问
for(int i; i < 3; i ++)
{
    for(int j; j < 5; j ++)
        printf("%d ", *(a[i] + j) );
  //Or: printf("%d ", *(*(a + i) + j) );
  //Or: printf("%d ", *(*(a + i) + j) );
  //Or: printf("%d ", a[i][j] );
    printf("\n");
}
//结果为:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15

拓:其实[]可以当做偏移量,就如上面的a[i][j]
下面再举例子:

int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;

访问方式(两种):比如访问a[0]

p[0];
*( p + 0 );
char** ch[5]是什么意思??

声明了一个指针数组,该数组包含5个元素,每个元素都是一个指向char类型的指针。也可以说是,它是一个可以存储5个字符串的数组。

简单来说,就是存储字符串首地址的数组。

char arr[5], arr2[5], arr3[5];//声明字符串数组
char* parr[3] = { arr, arr2, ar3 };//声明指针数组

//或者
char* ch[5]; // 声明指针数组
// 分配内存并初始化
ch[0] = "Hello";
ch[1] = "World";
ch[2] = "This";
ch[3] = "Is";
ch[4] = "C";

【数组指针】

  • 整型指针 - 指向整型的指针 - 存放整型的地址
  • 字符指针 - 指向字符的指针 - 存放字符的地址
  • 数组指针 - 指向数组的指针 - 存放数组的地址

数组指针简单来说就是指向【整个一维数组】的指针。

一定要记住:下面会用到,很关键,【数组指针】p 存放的是 【整个】【一维数组】的地址!!!

1)区分 【指针数组】 和 【数组指针】
指针数组 - int* parr1[10];
数组指针 - int (*parr2)[10];
  • 指针数组 - 存放指针的数组,存放地址的数组
  • 数组指针 - 指向一个和数组的指针
2) 疑惑?int*p, arr[10]; p = arrint (*pa)[10], arr[10];pa = &arr性质上面来说不是一样的吗???

是不一样的。

注意中括号中的数字是不能去掉的,必须添加,否则报错

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* p;
p = arr;//p 指向的是arr首元素a[0]的地址

因为 arr 本质上还是首元素的地址,而 p 接收到的实际上是元素地址,并不是所谓的整个数组的地址。(首元素的地址 和 整个数组的地址 虽然说表示的地址的含义不太一样,但是打印出来却是一样的)

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*pa)[10];
pa = &arr; // pa 现在指向 arr 整个数组的地址

而 &arr 其实表示的是整个数组的地址,然后 p 接收到的实际上是 arr整个数组的地址,而非首元素的地址。(首元素的地址 和 整个数组的地址 虽然说表示的地址的含义不太一样,但是打印出来却是一样的)

3) 数组名表示的不同的含义???(三种)
  • 数组名 - 首元素地址(一般情况下,表示的都是首元素的地址)

  • 数组名 - sizeof,单独放一个数组名,计算的是整个数组的的大小 (特殊)

    如果写的是arr+0,+1这种不算,这不算数组名

  • 数组名 - &数组名,数组名表示的依然是整个数组,所以&数组名取出的是整个数组的地址 (特殊)

下方例子从值的角度去讲,是一样的,打印的地址是一样的。

int arr[10];
printf("%p\n", arr);//元素的地址
printf("%p\n", &arr[0]);//元素的地址
printf("%p\n", &arr);//整个数组的地址
4) &arr

再次强调一遍:&arr - 表示的是整个数组的地址

int arr[10];
&arr - 在这表示的是整个数组的地址

那怎么存 &arr 呢???

这时候就会用到【数组指针】了,下面是用法

//注:数组和数组指针中的中括号中的数字必须是相同的。

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int (*pa)[10] = &arr;
//pa = &arr;

而至于(*pa)[10]的意思就是,定义了一个指针,这个指针指向一个长度为 10 的存放int的数组。

而这个数组指针pa的类型是是什么呢???

pa的类型就是**int (*)[10]就是这个数组指针的类型。**

5) 【数组指针】指向【指针数组】
int* arr[10] = {0};
int* (*p)[5] = &arr;

解释:【数组指针p】 指向一个**【指针数组】,而这个指针数组的类型就是int*,所以表示指针数组的时候,类型不能写为int**,必须要写为int*

6) 数组指针的【错误】用法与【正确】用法??*pa是什么???
错误用法 - 用在【一维数组】

*pa就是数组首元素的地址,下面是解释。

首先,pa是指向整个数组的,指向的是arr,存放的地址是&arr
其次,*pa 就是 *(&arr),意思就是arr数组名,所以,*pa意思就相当于数组名,数组名是什么呢? - 数组名是数组首地址。

或者我一个老师这么教我,这个叫做降级,将行地址降级成为元素地址,就是首地址了。

注:这样写只是知道这样是正确的,并且有这个写法就可以了,但是建议不要这么写,因为很麻烦,很别扭,并不推荐。
数组指针不是这么用的。

正确用法 - 用在【二维数组】

下面更一步去阐述如何使用数组指针

在一维数组中,如果想代表【整个一维数组】的地址,就要写成 &arr

而在二维数组中,想表示【一维数组】的地址(一行的地址)则不需要这样,arr就是表示第0行【整个一维数组】的地址,arr ± 数字,是加减的整个数组,比如,arr表示的额就是arr[0],arr + 1 –> 表示的是加一个数组(行+1) == &arr[1]

下面演示的是直接 解引用 二维数组的 地址 去访问元素:

int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
for (int i; i < 4; i++)
{
    for (int j; j < 4; j++)
        printf("%d", *(*(arr + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
    printf("\n");
}
//逐个拆分解释
arr - 第0行整个一维数组的【地址】(行地址) == arr[0]
*arr - 第0行整个一维数组的【首地址】(首元素的地址) - 解引用了第0行一维数组的地址 == &arr[0][0]
*(*arr) - 第0行,第0个元素,这就不是地址了,而是指的元素 == arr[0][0]

注:*(*(arr + i) + j) == *(arr[i] + j) == arr[i][j]

下面进一步用【数组指针】去访问二维数组中的元素:
要记住一点,数组指针(*p)[10],p存放的一定要是整个数组的地址。所以在一维数组的时候要存入&arr,而在二维数组的时候就是arr了

int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
int (*p)[4] = arr;//在这里,p中存储的就是&arr[0], 即整个第0行数组的地址
//Or: p = arr;
for (int i; i < 4; i++)
{
    for (int j; j < 4; j++)
        printf("%d", *(*(p + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
    printf("\n");
}
下面这几种都可以表示为元素
//*(*(p + i) + j) == *(p[i] + j) == p[i][j]

注:*(*(p + i) + j) == *(p[i] + j) == p[i][j]

7) 二级指针变量(***)

【二级指针变量】就是存放一级指针变量地址的指针变量。

如下就是,

p1 - 一级指针 - 存放的是变量的地址。
p2 - 二级指针 - 存放的是一级指针的地址,所以要写两个星号。

eg: 
char ch = 'a';
char* p1 = &ch;
char** p2 = &p1;
8) int (*parr[3])[5] 是什么???

可以理解为存放多个数组指针的数组,或者存放多个数组的地址的数组。

之前我们知道,数组指针可以存放一个数组的地址,那么我们可不可以有一个数组专门去存放好几个数组的地址呢???

那么就用到了int (*parr[3])[5]

int(*)[5]就是表示的可以存放元素类型为int并且长度为5的【整个一维数组】的地址。

  • 如果写成int (*p)[5],就表示了存放的是【1个】【一维数组的地址】
  • 如果写成int (*p[3])[5],就表示了存放的是【3个】【一维数组】的地址

下面是具体用法:

用于【一维数组】时:

int arr1[5], arr2[5], arr3[5];
int (*parr[3])[5] = {&arr1, &arr2, &arr3};

如何访问呢???假设每个数组中已经存入了相应的数值,打印每个数组中的元素

for(int i = 0; i < 3; i++)
{
    printf("第%d个数组:\n", i + 1);
    for(int j = 0; j < 5; j++)
    {
        printf( "%d, ", *(*parr[i] + j) );
    }
    printtf("\n");
}

用于【二维数组】时:

int arr1[5][5], arr2[5][5], arr3[5][5];
int (*parr[3])[5] = {arr1, arr2, arr3};

如何访问呢???假设每个数组中已经存入了相应的数值,打印每个数组中的元素

for(int i = 0; i < 3; i++)
{
    printf("第%d个二维数组数组:\n", i + 1);
    for(int j = 0; j < 5; j++)
    {
        for(int k = 0; k < 5; k ++)
            printf( "%d, ", *(*(parr[i] + j) + k) );
    }
    printtf("\n\n\n");
}
9) 【指针数组】 – 总结
int arr[4][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
int (*p)[4] = arr;//在这里,p中存储的就是&arr[0], 即整个第0行数组的地址
//Or: p = arr;
for (int i; i < 4; i++)
{
    for (int j; j < 4; j++)
        printf("%d", *(*(p + i) + j) );//这样也可以正确打印,效果和arr[i][j]一样
    printf("\n");
}
下面这几种都可以表示为元素
//*(*(p + i) + j) == *(p[i] + j) == p[i][j]
  1. 重要:【数组指针】 p 存放的是【整个一维数组】的地址,一定谨记:存放的不是首元素的地址!!!!

  2. S数组指针的类型如何表示呢?? –> int (*)[4]

  3. 二维数组中,下面举例去理解

    arr == &arr[0] - 第 0 行整个数组的地址

    *arr == &arr[0][0] - 首元素的地址

    **arr == arr[0][0] - 首元素

    arr + i == &arr[i] - 第 i 行整个数组的地址

    *(arr + i) == &arr[i][0] - 第 i 行 首元素的地址

    *(arr + i) + j == &arr[i][j] - 第 i 行 第 j 列的元素的地址

    *(*(arr + i) + j) == arr[0][0] - 第 i 行 第 j 列的元素

  4. int (*p)[4] 如何用呢???怎么给数组指针 p 赋值呢???

    • 一维数组 p = &arr(整个一维数组的地址)

    • 二维数组 p = arr(第0行整个一维数组的地址)

      这样就可以让p当做arr直接去访问二维数组了。

  5. p ± 数字 到底 ± 偏移的量是多少???

    拿指arr为例吧,理解数组指针的时候,将p当做arr即可

    arr + 1: 因为arr代表的意思是第 0 行的【整个一维数组】的地址,所以 +1 所代表的就是偏移了整个数组,就偏移到了第 1 行 的【整个一维数组】的地址
    *(arr + i) + 1: 意思就是 第 i 行的数组的 第 j 个元素 的地址,可以理解为就是&arr[i][j]
    *(*(arr + i) + 1): 解引用*(arr + i) + 1,得出来就是arr[i][1]元素
    
  6. int (*parr[3])[5] 存放【整个数组的地址】的数组,或者说是存放【数组指针】的数组
    //存放整个一维数组的地址
    int arr1[5], arr2[5], arr3[5];
    int (*parr[3])[5] = {&arr1, &arr2, &arr3};
    
    //存放整个二维数组的地址
    int arr1[5][5], arr2[5][5], arr3[5][5];
    int (*parr[3])[5] = {arr1, arr2, arr3};
    

标签:10,arr,int,地址,理解,数组,指针
From: https://blog.csdn.net/m0_60605989/article/details/140621704

相关文章

  • 二分查找(数组的练习)
    一、什么是二分查找    二分查找(又叫折半查找)是一种查找算法,它能使查找的速度更快,但要求查找的序列必须有序。        如果我们按顺序在一个序列中查找一个数,当这个数在靠前的位置,查找的速度还好;那么当这个数在很靠后的位置呢?甚至是一个很长的数组,要查找......
  • day8 字符数组
    字符数组用来存放字符数据的数组叫字符数组。字符数组中每一个元素存放一个字符。字符数组主要用于处理c中字符串的使用。字符串:“helloworld”用双引号引用起来的就是字符串,在双引号中,字符串中的字符不可更改。字符串在内存当中按照先后次序排列起来c语言中,规定了字符串......
  • 字符串数组
    一、二分查找法将一个有序的数列取中值,判断数在哪一段,每次筛选原来的一半,重复多次二、字符串数组(容器,用来存放字符)1.初始化内容:chars[100]=“hello”(字符串常量)字符串结束标志:\0(空字符)单一性、连续性、有序性2.输出字符串puts(s)=puts(&s[0])3.输入字符串scanf......
  • 数组和指针的关系,const修饰指针
     数组和指针的关系 const修饰指针总结:const修饰谁谁就不能变      const修饰*( const在* 前)          不能改变*p的值,可以改变p的指向     const修饰p(const在*后)          不能改变p的指向,可以改......
  • 条件编译,指针
    条件编译 指针指针的常见用法   int a = 100;    int b = 200;    int *p = NULL;    int *q = NULL;    p = &a;    q = &b;假设a的地址为0x00000001,b的地址为0x00000005,p指向a,q指向b含义abpq*p*qp = &b让p......
  • SA & SAM 后缀数组 & 后缀自动机
    终于来到大名鼎鼎的后缀结构了,后缀结果可以解决许多子串问题。后缀结果是字符串经常考察的点,需要重点学习。SA后缀排序,是指这个对字符串\(s\)的每一个后缀字符串进行排序,通过处理每个后缀的前缀来解决子串问题。\(SA\):排名为\(i\)对应原字符串下标,\(rk\):下标为\(i\)的后缀排名。......
  • 字符数组啊啊啊啊
    字符数组的定义字符数组是专门用来存放字符串的容器字符串“”,用双引号引起来且按顺序的叫字符串字符串的结束标志是‘\0’表示方式最简为s[100]=“helloworld”;输出字符串函数:int puts(constchar*s)括号内直接填写字符数组的数组名,puts(s)输入字符串函数:char*gets......
  • 一维数组啊啊啊啊啊啊
    一维数组的定义结构:类型说明符数组名[常量表达式];类型说明符可以是已有的数据类型(唯独不能是void)一维数组元素的使用[]是下标,下标运算符在定义数组时的[]不是下标运算符,是类型说明符,为了说明n为数组数组的数组名代表数组的首元素的地址地址为一个地址值常量不能作为左......
  • 代码随想录算法训练营Day5、6 | Leetcode 242 有效字母的异位词 Leetcode 349 两个数
    前言因为昨天休息所以把两天合并成一天来写,昨天把上周的题又重写了一遍,发现一些细节还是要注意。今天的题目都是查找,也涉及到了最近正在学的STL。Leetcode242有效字母的异位词 题目链接:https://leetcode.cn/problems/valid-anagram/description/代码随想录题解:代码随想......
  • KMP算法中next数组以及nextval的求解(简单,通俗易懂版)
    以一个题为例:计算上图中next[j]以及nextval[j]的值。【本文中j的下标从1开始。】最长公共前后缀:···前缀:不包含最后一个字符的所有以第一个字符开头的连续子串。···后缀:不包含第一个字符的所有以最后一个字符结尾的连续子串。先看next[j],(1)j的下标......