进阶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

相关文章
|
22小时前
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
17 5
|
4天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
10天前
|
存储 人工智能 BI
C语言:数组的分类
C语言中的数组分为一维数组、多维数组和字符串数组。一维数组是最基本的形式,用于存储一系列相同类型的元素;多维数组则可以看作是一维数组的数组,常用于矩阵运算等场景;字符串数组则是以字符为元素的一维数组,专门用于处理文本数据。
|
7天前
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。
|
10天前
|
C语言
C语言数组
C语言数组
12 0
|
4月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
13天前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
15 0
|
1月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
2月前
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
|
2月前
|
C语言
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)
【C初阶——指针4】鹏哥C语言系列文章,基本语法知识全面讲解——指针(4)