【C进阶】指针进阶(1)_二次复习版(中)

简介: 【C进阶】指针进阶(1)_二次复习版(中)

2.3使用3个一维数组,模拟实现一个二维数组

定义了三个整型数组arr1、arr2和arr3,分别存储了一组连续的整数。然后定义了一个整型指针数组arr,它包含了arr1、arr2和arr3三个数组的首元素地址。

for循环用于遍历arr数组中的每一个元素,即每个数组的首地址,然后在内层循环中,遍历了每个数组中的所有元素,并使用指针算术运算访问了每个元素的值并打印输出。

#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* arr[] = { arr1,arr2,arr3 };
  int i = 0;
  for (i = 0; i <3 ; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      //printf("%d ", arr[i][j]);
      printf("%d ", *(arr[i] + j));
           printf("%d ", *(*(arr+i) + j));
    }
    printf("\n");
  }
  return 0;
}


*(*(arr+i) + j) == *(arr[i] + j) == arr[i][j] 三种写法等价

首元素地址加偏移量找到下标是这个元素的地址了,再解引用就找到此元素。

执行:

71df1e986e124f5a92961fd97a85102f.png

按自己的理解再写一个

图解:

150064ce5a2a4f60859aaab179647758.png

用一个数组名表示首元素地址,把a,b,c放到arr中说明是把首元素地址存进去了,因为是个整型地址,所以是个整型指针,所以是int*arr[3]每个元素的类型是int*,本质是用一个指针数组,来管理3个int数组

#include<stdio.h>
int main()
{
  int a[] = { 1,2,3,4 };
  int b[] = { 2,3,4,5 };
  int c[] = { 3,4,5,6 };
  int* arr[3] = { a,b,c };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 4; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

执行:

473636edc75c49f9bf843e3d602a6d50.png

arr[i][j]可写成*(arr[i]+j),一个整型指针(地址)加j向后偏移j个元素


2.4例题:

int main()

{

   int* arr[3][3] = { 1 };

   int p = **arr;

   printf("%p", p);

}

134f431fa9bd4208a072dceed22a1386.png


3.数组指针


3.1 数组指针的定义

数组指针是指针?还是数组?

答案是:指针。


我们已经熟悉:

整形指针: int * pint; 能够指向整形数据的指针。

浮点型指针: float * pf; 能够指向浮点型数据的指针。

那数组指针应该是:能够指向数组的指针。


类比:

整型指针 -- 指向整型的指针int a = 10;

int* p = &a;

字符指针-指向字符的指针char ch = 'w';

char* pc = &ch;

数组指针–指向数组的指针

int arr[10] ;//数组

int (*pa)[10] = &arr;//取出的是数组的地址

char arr[10] --  char (*pc)[10] = &arr;

int*arr[5];--       int*  (*pc ) [5]  =&arr


下面代码哪个是数组指针?

int *p1[10];

int ( * p2 )[ 10 ];

//p1, p2 分别是什么?

答:int (*p2)[10];


p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个 指针,指向一个数组,叫数组指针,[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。


易错点:

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


对上述代码总结:

1.*说明pa是指针,int说明pa指向的a变量是整型

2.&arr取出的是数组的地址,只有数组的地址才需要数组来接收,类型是int[10]

3.arr是不能放进左边指针(类型是int[10])里面的,arr本身的类型是int*


3.2arr 和 &arr 有什么区别?

数组名绝大部分情况下是数组首元素的地址

但是有2个例外:

1. sizeof(数组名) - sizeof内部单独放一个数组名的时候,数组名表示的整个数组,计算得到的是数组的总大小

2. &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度来讲和数组首元素的地址是一样的,但是意义不一样

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

e6ccd340664f4f42a35f0fd8a5622c3a.png

上图可看出arr==&arr[0](地址值相等),也说明指针类型决定了指针+1到底加的是几,即为数组名是数组首元素地址,而&arr是整个数组的起始地址

我们已知的两种访问数组的方式:

int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  //使用指针来访问
  int* p = arr;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(p + i));
  }
  printf("\n");
  //下标的形式访问数组
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}


11d4024d05e44aa3b8927850b24e71aa.png


3.3 数组指针的使用

使用数组指针访问并打印一维数组

理解:p == &arr,*p == *&arr,*p == arr

int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int (* p)[10] = &arr;//注意这个地方不是arr,&arr是整个数组的地址,所以要用数组指针接收
  int i = 0;
  //p  --- &arr
  //*p --- *&arr
  //*p --- arr
  //虽然对,但是不推荐
  for (i = 0; i < sz; i++)
  {
    printf("%d ", (*p)[i]);
  }
  printf("\n");
  //虽然对,但是不推荐
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *((*p) + i));
  }
  return 0;
}


ef39c4928fe947709dc1f55402a50534.png

(*p)+i的意思:下标为i的元素的地址,*((*p)+i)得到下标为i的元素。

以上两种写法均正确,但是不推荐。


在自定义函数内打印二维数组

二维数组接收

打印二维数组传参的时候可以形参用二维数组接收。

void print1(int arr[3][5], int r, int c)
{
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      printf("%d ", arr[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};
  /*二维数组的数组名,也表示首元素的地址
  二维数组的首元素是第一行
  首元素的地址就是第一行的地址,是一个一维数组的地址*/
  //
  print1(arr, 3, 5);
  return 0;
}

495c51b57bf546f1a8c33c15f859b161.png


数组指针接收

也可以用数组指针接收,并遍历数组

二维数组的数组名,也表示首元素的地址

二维数组的首元素是第一行

首元素的地址就是第一行的地址,是一个一维数组的地址

void print(int(*arr)[5], int r, int c)
{
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      //printf("%d ", *(*(arr + i) + j));
      printf("%d ", arr[i][j]);
         //printf("%d ", (*(arr + 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};
  /*二维数组的数组名,也表示首元素的地址
  二维数组的首元素是第一行
  首元素的地址就是第一行的地址,是一个一维数组的地址*/
  //
  print1(arr, 3, 5);
  return 0;
}

*(*(arr + i) + j)--> arr 是二维数组的数组名,数组名是数组首元素地址,也就是二维数组的首元素第一行的地址,arr+i就是二维数组第 i 行地址,*(arr+i)得到二维数组第i行,也就是第i行的数组名,也就是arr[i],arr[i]数组名又相当于数组首元素地址,也就是第i行第一个元素的地址,即为&arr[i][0]


也就是说,     *(arr+i) == arr[i] == &arr[i][0]

也可以这样写:    printf("%d ", (*(arr + i))[j]);
(*(arr+i))[j])意思是访问完第i行之后再访问[j]列,可以写成arr[i][j]

5e47fdff568f458ba80c5a7aaa50efcb.png

易错点:

1.int arr[5];整型数组,数组有10个元素

2.int *parr1[10];指针数组,数组10个元素,每个元素都是int*类型的

3.int(*parr2)[10];parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型

4.int (*p)[10]=arr,如何强转?(int(*)[10])arr

5.int* p=&num,对p解引用的类型int

6.int(*p)[10]=&arr,&arr赋给p,p--arr,*p=*&arr=arr


7.int(*parr3[10])[5]

parr3是数组,数组中存放的指针,该指针指向的又是数组。

3a95ef103d7b4ecab1b9573472ccde25.png

parr3和[10]结合说明parr3是数组,大小存10个元素,每个元素的类型是int(*)[5],

可以理解为指针数组里面存放的是数组指针。

相关文章
|
6月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
6月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
6月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
6月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
6月前
|
搜索推荐
指针进阶(2)
指针进阶(2)
54 4
|
6月前
指针进阶(3)
指针进阶(3)
46 1
|
6月前
|
C++
指针进阶(1)
指针进阶(1)
47 1
|
6月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
52 2
|
6月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
53 0
|
6月前
|
存储 安全 编译器
C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
48 0