进阶C语言 第二章-------《进阶指针》 (指针数组、数组指针、函数指针、回调指针)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏(一)

简介: 进阶C语言 第二章-------《进阶指针》 (指针数组、数组指针、函数指针、回调指针)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏(一)

绪论

       书接上回,通过对数据类型进阶的认识,你肯定对各种数据类型在内存中如何存储有了了解。虽然说,这方面可能对你的编程能力没什么进步。但是,他是一本内功秘籍,当我们遇到了这方面的问题时我们可以知道可能是哪一方面出了问题。本章进阶指针,可能会有更多的知识点和更复杂的问题,但其实只要你对初阶的知识有很好的认识他也是小ks。

最后祝大家新年快乐,万事如意!

image.png

所以安全带系好,发车啦(建议电脑观看)。


思维导图:

image.png

要XMind思维导图的话可以私信哈


目录


1.字符指针

2.指针数组

2.1指针数组的定义和使用方法

3.数组指针

3.1  定义

3.2  &数组名与数组名

3.3数组指针的使用

4.数组传参、指针传参

4.1 一维数组和指针数组传参

4.2 二维数组传参

4.3一级指针传参

4.4二级指针传参

5.函数指针

5.1.1函数指针的创建

5.1.2函数指针的使用

6.函数指针数组

6.1函数指针数组定义

7.指向函数指针数组的指针

8.回调指针

8.1.1回调函数的定义:

1.字符指针

1.1

知识点:

字符指针常见的的使用情况:

int main()
{
    char a = 'a';
    //字符指针
    //1.
    char * pa = &a;
    //2.
    char * pa = "abcd";
    return 0;
}

在第一种情况时:是直接将a的地址存进了一个指针中这个指针叫字符指针

在第二种情况时:是将字符串"abcd"中的首元素的'a'字符的地址存进一个字符指针中 (在常量字符串中他产生的值就是'a'字符的地址即"abcd" == &'a' )

细节(注意点):

常量字符串不能被修改、两相同的常量字符串完全相同

即当char * pa= "abcdef"时

*pa = 'w'(这样时错误的)

再如:

char arr[] = "abcdef";

arr = "cdefg";

这样同样是不行的

练习:

#include<stdio.h>
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";
    const char *arr3 = "abcdef";
    const char *arr4 = "abcdef";
    if(arr1 == arr2)
        printf("same\n");
    else
        printf("no same\n");
    if(arr3 == arr4)
        printf("same\n");
    else
        printf("no same\n");
    return 0;
}

分析:

首先,有2个数组变量arr1 arr2存放着字符串,并且还有两个字符指针存放着常量字符串的首元素地址

其次,因为两个数组会创建两个不同的空间即就会导致其数组名表示的首元素地址不同;而对于arr3 和 arr4 来说虽然他们将‘a’字符的地址存放在不同的空间,但是其指针变量的变量名就表示其内存中所存的地址(3、4都常量字符串的首元素地址)

最后,通过上面标红的字就可以推出最终将打印 no same   、 same        

image.png

附这里我们需要注意的是arr1 arr2  arr3 arr4 他们分别代表着意思

数组名:首元素的地址

指针变量的变量名:代表着存放的地址

2.指针数组

2.1指针数组的定义和使用方法

知识点:

指针数组,字如其名是一个存放了多个指针的数组

我们不妨那整形数组、字符数组对应了来看

image.png

而我们有知道指针(变量)使用用来存放地址的,所以我们在为int * arr[4] 初始化时应该存放地址进去

细节(具体):

对于指针数组的使用:

#include<stdio.h>
int main()
{
  char* arr[4] = { "abcd","hehe","blog","nb" };//常量字符串产生的是其首元素的地址
  for (int i = 0; i < 4; i++)
  {
    printf("%s ", arr[i]);
  }
  return 0;
}

image.png

练习:

通过指针数组来模拟实现二维数组:

image.png

为什么可以直接写成arr[][]的形式,是因为你看arr[ i ][ j ]第一个  arr[i] 就是找到了 存放在指针数组中各个数组的地址(即数组名) 当i为0时(arr[ 0 ]  == arr1 ):arr1 [ j ]  是不是就是变成了数组的表示方法。

当然我们也可以吧arr [ i ] [ j ]表示成 *(arr[ i ] + j )

image.png

3.数组指针

3.1  定义

知识点:

同整形指针、字符指针一样他是一种存放数组的指针

整形指针:int * p;

字符指针:char * p;

不难发现他们是由 所指向地址的类型     +     * (表示变量是一个指针)  +   变量名  组成的(这点很重要会贯穿后面指针的内容次处称为指针的3步原则)

所以同理,数组指针他也是如此组成的:

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

int (*parr)[ 4 ] = &arr;

此时的地址数组的类型为int  [ 4 ] 、 *代表其为一个指针,加上括号是因为防止因为[ ] 的优先级比 * 的优先级高而导致变成 指针数组   、 再加上变量名 就成了数组指针

3.2  &数组名与数组名

知识点:

数组名在讲数组内容处已经提过为:这个数组的首元素的地址

&数组名:如int arr[ 10 ] = {0}; arr表示数组名,那么取地址的数组名就真正的表示这个数组

细节:

下面通过代码来更加详细的解释:

int main()
{
  int arr[10] = { 0 };
  printf("%p\n", &arr[0]);//首元素的地址
  printf("%p\n", arr);//数组名
  printf("%p\n", &arr);//整个数组的地址
  return 0;
}

image.png

此处可以发现他们三个的地址都相同,但是有什么不同地方呢?让我们继续往下

  int main()
  {
    int arr[10] = { 0 };
    printf("%p\n", &arr[0]);//首元素的地址
    printf("%p\n", &arr[0]+1);//首元素的地址
    printf("%p\n", arr);//数组名
    printf("%p\n", arr+1);//数组名
    printf("%p\n", &arr);//整个数组的地址
    printf("%p\n", &arr+1);//整个数组的地址
    return 0;
  }

image.png

此处当我们+1后,可以发现数组名的结果和&arr[0]的效果(加了4byte)是一样的都是,而&arr却加了40(70-48 == 28,十六进制不够接16,转化成十进制就是40byte)

所以,最终得出结论就是

arr(除了两种特别情况外)都表示首元素的地址

&arr 则直接表示整个数组

他们的不同处在于,代表的意义不一样

最终与上面所讲到的数组指针结合:

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

int (*parr)[ 4 ] = &arr;

此处的数组指针和指针数组就有着明显的区别:

一个是存整个数组的地址的,一个是存数组中的各个数的地址的。

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

int (*p)[4] = &arr;

3.3数组指针的使用

直接通过代码注释的形式来展示;

//对于一维数组时
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int(*p)[10] = &arr;
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", (*p)[i]);//通过(*p)找到整个数组 * &arr等于arr再通过下标引用找到对应
  }
        //而对于一维数组来说这样写反而有点冗余了,不如直接用指针*(p+i)
  return 0;
}
//对于二维数组来说
void test(int(*p)[4], int r, int c)//将二维数组的首元素传进其表示二维数组的第一行即一整个数组
{
  int i = 0;
  for (i = 0 ;i < r; i++)//i表示访问的行
  {
    int j = 0;
    for (j = 0; j < c; j++)
    {
      printf("%d", (*(p + i))[j]);//访问第i行j列
          //printf("%d", p[i][j]);p[i] == *(p+i)
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
  test(arr, 3, 4);
}

附:当在函数内接收一个二维数组时,方块内放到是二维数组的列宽

image.png

注意点:

数组指针*p = * &arr = arr

int (* parr[10])[5]:

image.png

相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
38 3
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
140 13
|
1月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
1月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
50 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
40 1
|
2月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。