【C语言】指针数组|数组指针|数组指针数组|如何区分?

目录

1.字符指针

2.指针数组

3.数组指针

3.1 数组指针的定义

3.2数组指针的使用

4.数组指针数组

5.数组名

6.数组传参,指针传参

6.1一维数组传参

6.2二维数组传参

6.3一级指针传参

6.4二级指针传参


1.字符指针

int main()
{
    char ch = 'w';
    char* pc = &ch;
    *pc = 'a';

    return 0;
}
上述代码中,我们创建了一个字符变量,以及一个字符指针,并且通过字符指针修改了字符变量的内容

当有一个指针变量时,我们可以通过对其解引用找到它所指向的变量
#include<stdio.h>
int main()
{
    const char* p = "abcdef";
    printf("%s\n", p);

    return 0;
}
对于上述代码,该如何理解呢?是把一个字符串存放到p中吗?
实际上,是把字符串中的首字符a的地址存放到p中,p的类型是char*

  • 我们可以通过p找到这个字符串,并且使用%s打印这个字符串
  • 但是*p='w'这种操作是非法的,编译器会报错访问权限冲突
  • 因为对于"abcdef"这个常量字符串来说,它存储在内存的只读数据区,只能读取使用,不能更改,也就是相当于,"abcdef"这个字符串中,每一个元素都是const char类型的,我们知道被const 修饰的变量都不能被修改
  • char* p = "abcdef";在VS编译器下报错,我们必须将p的指针类型与字符串中的元素类统一型

接下来我们分析一段代码

#include<stdio.h>
int main()
{
    const char* p1 = "abcdef";
    const char* p2 = "abcdef";
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";
    if (p1 == p2)
        printf("p1 == p2\n");
    else
        printf("p1 != p2\n");

    if (arr1 == arr2)
        printf("arr1 == arr2\n");
    else
        printf("arr1 != arr2\n");

    return 0;
}

分析:
对于p1和p2,它们 指向的是在内存数据只读区的同一个常量字符串的首字符,所以p1==p2
对于arr1和arr2,这是 两个完全独立的数组,在内存中开辟不同的两块空间,数组名代表首元素的地址,不同的内存区域,地址不同,所以arr1 != arr2

2.指针数组

整型数组:存放整型数据的数组,对于一个整型数组来说,它的每一个元素都是int类型的
字符数组:存放字符数据的数组,对于一个字符数组来说,它的每一个元素都是char类型的
类比整型数组和字符数组,我们可以知道
指针数组:存放指针的数组,对于一个指针数组来说,它的每一个元素都是指针类型的

我们可以使用指针数组模拟一个二维数组
#include<stdio.h>
int main()
{
    int arr1[] = { 1,2,3,4,5 };
    int arr2[] = { 2,3,4,5,6 };
    int arr3[] = { 3,4,5,6,7 };
    int* parr[3] = { arr1,arr2,arr3 };
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;
        for (j = 0; j < 5; j++)
        {
            printf("%d ", *(*(parr + i) + j));
        }
        printf("\n");
    }

    return 0;
}

分析:因为数组名表示数组首元素地址,即arr1,arr2,arr3都是int*类型的指针,所以我们可以使用arr1,arr2,arr3来初始化这个指针数组

因为数组名代表数组首元素地址,所以parr指向首元素arr1,我们通过*parr就可以找到arr1,同理,我们通过*parr+1可以找到元素arr2,*parr+2可以找到元素arr3
对于数组arr1,通过*arr1可以找到首元素1,*arr1+1可以找到元素2,*arr1+2可以找到元素3
... ...
综上所述,我们可以通过*(*(parr))找到arr1中的元素1,*(*(parr+1)+1)找到arr2中的元素2... ...
这样看来我们只需要使用双层的for循环,外层循环实现遍历指针数组parr,内层循环实现遍历每一个arr数组即可模拟出一个二维数组
但是对于模拟出来的这个二维数组,它并不具有 二维数组在内存中连续存放的特性
因为这三个arr数组在内存中是相互独立的,它们开辟的内存空间不一定连续

3.数组指针

3.1 数组指针的定义

整型指针:指向整型数据的指针
字符指针:指向字符类型数据的指针
类比整形指针和字符指针,可以得到
数组指针:指向数组的指针

如何定义一个数组指针呢?

首先,我们需要有一个数组,假设有一个整型数组arr1

然后我们定义一个数组指针,指向这个arr1数组

注:因为[ ]的优先级高于*,所以必须加()使p和*先结合,从而构成一个数组指针

3.2数组指针的使用

我们看一段打印二维数组的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void print1(int arr[3][5], int row, int col)
{
    int i = 0;
    //控制行
    for (i = 0; i < row; i++)
    {
        int j = 0;
        //控制列
        for (j = 0; j < col; j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
}
void print2(int(*p)[5], int row, int col)
{
    int i = 0;
    //控制行
    for (i = 0; i < row; i++)
    {
        int j = 0;
        //控制列
        for (j = 0; j < col; j++)
        {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
    printf("print1函数输出:\n");
    print1(arr, 3, 5);
    printf("print1函数输出:\n");
    print2(arr, 3, 5);
    
    return 0;
}

print1和print2函数都可以实现二维数组的打印,它们的区别在于参数
对于print1函数:
void print1(int arr[3][5], int row, int col)
二维数组传参,我们使用一个二维数组接收
printf("%d ",arr[i][j]);
打印时通过下标访问二维数组,这是最常见的打印方式
对于print2函数:
void print2(int(*p)[5], int row, int col)
二维数组传参,我们使用一个数组指针来接收
int(*p)[5]:这是一个数组指针,指向一个有5个int类型元素的数组
可以发现,这其实是二维数组的第一行,也就是一个有5个int类型元素的一维数组
所以,对于一个 二位数组,数组名其实表示的是第一行的地址

printf("%d ", *(*(p + i) + j));
我们已经知道p指向二维数组的第一行,那么p+1呢
p指向一个有5个int类型元素的一位数组,所以p+1跳过这样一个数组,如图所示

所以我们通过p+i可以找到二维数组的地i行
*(p+i)就是二位数组的第i行,这是一个一维数组
那我们如何找到某行某列的一个具体元素呢?以第一行的第二个元素2为例,下标查找即arr[0][1]
p表示的是第一行的地址,*p即第一行的一维数组
对于一维数组,数组名表示数组首元素的地址
*p即表示这个一维数组的首元素1的地址对于*p,它指向的元素是int类型的,*p+1跳过一个整型元素

*p+1指向第一行数组的第二个元素,所以*(*p+1)即元素2
*(*p+1)=*(*(p+0)+1)----->arr[0][1]
同理可知 对于二维数组中的任意一个元素arr[i][j],我们可以通过*(*(p+i)+j)访问

4.数组指针数组

数组指针数组:即存放数组指针的数组
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
    int arr1[5] = { 1, 2, 3, 4, 5 };
    int arr2[5] = { 2, 3, 4, 5, 6 };
    int arr3[5] = { 3, 4, 5, 6, 7 };
    int(*parr3[10])[5] = { &arr1, &arr2, &arr3 };
    
    
    return 0;
}
int(* parr3[10])[5] = { &arr1, &arr2, &arr3 };
parr3[10]即为一个数组指针数组,存放了三个元素
这个数组中每一个元素的类型是int(* )[5],即为一个数组指针类型

5.数组名

我们先看一段代码:

#include<stdio.h>
int main()
{
    int arr[10] = { 0 };
    printf("%p\n", arr);
    printf("%p\n", arr + 1);
    printf("%p\n", &arr[0]);
    printf("%p\n", &arr);
    printf("%p\n", &arr + 1);
    printf("%d\n", sizeof(arr));

    return 0;
}

可以发现:
1.arr 和&arr[0] 和&arr的输出结果相同
2.arr+1 和&arr[0]+1的输出相同,但和&arr+1的结果不相同
3.sizeof(arr) = 40
C语言中,数组名通常表示的都是数组首元素的地址,但有两个例外
1️⃣sizeof(数组名):这里数组名表示的是整个数组,计算整个数组的大小,单位是字节
2️⃣&数组名:这里去粗的是整个数组的地址
我们使用图示可以更加清楚

6.数组传参,指针传参

6.1一维数组传参

我们已经知道一维数组名表示数组首元素的地址,现在列出二维数组可行的传参方式

int arr[10] = { 0 };//定义一个整型数组
int* arr2[20] ={ 0 }; //定义一个指针数组
void test(int arr[]);//一维数组传参,一维数组接收,数组大小可以省略
void test(int arr[10]);//一维数组传参,一维数组接收
void test(int *arr);//一维数组名表示数组首元素的地址,使用指针接收
void test(int* arr2[20]);//数组传参,数组接收, 注意数据类型统一
void test(int** arr2);
//对于arr这个指针数组,它的首元素是一个指针,arr2指向首元素,即arr2是一个指向指针的指针,所以使用二级指针接收

6.2二维数组传参

我们知道二维数组名表示第一行一维数组的地址,现在列出二维数组可行的传参方式

int arr[3][5] = { 0 };//定义一个二维数组
void test(int arr[3][5]);//二维数组传参,二维数组接收
void test(int arr[][5]);//二维数组传参,二维数组接收, 行可以省略,列不能省略
void test(int (*arr)[5]);
//二维数组名表示第一行一维数组的地址,即一个一维数组,所以使用以个数组指针接收

6.3一级指针传参

指针就是地址,现在列出一级指针可行的传参方式

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr; //注意区分arr和&arr
int a = 10;
int * = &a;
void test(int* p);//这里我们主要搞明白可以传什么参数
test(p);✅
test(pa);✅
test(&a);✅
test(arr);✅

6.4二级指针传参

二级指针就是指向一级指针的指针,现在列出二级指针可行的传参方式

int n = 10;
int* p = &n;
int** pp = &p;
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[3] = { arr1,arr2,arr3 };//指针数组
void test(int** p);//这里我们主要搞明白可以传什么参数
test(pp);
test(&p);
test(parr); ✅//parr是指向指针数组首元素(一级指针)的指针
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值