【进阶C语言】进阶指针--学会所有指针类型

简介: 字符指针是一种指针,是众多指针类型中的一种。

一、字符指针

前言:字符指针是一种指针,是众多指针类型中的一种。

1.字符指针的三种形式

(1)指向单个字符

#include<stdio.h>
int main()
{
  char ch = 'w';
  char* pc = &ch;
  printf("%c\n", *pc);
  printf("%d\n",*pc);
  return 0;
}

(2)指向一个字符数组

#include<stdio.h>
int main()
{
  char arr[] = "abcdef";
  char* pc = arr;
  printf("%s\n",pc);
  return 0;
}

(3)直接指向一个字符串

#include<stdio.h>
int main()
{
  char* pc = "abcdef";
  printf("%s\n",pc);
  return 0;
}

这里是把整个字符串放到pc的指针变量里面了吗?其实并不是,字符串跟数组一样,首元素就是首地址。只是把字符串第一个字符的地址存入指针变量中,通过首地址就可以找到整个字符串。

image.png

2.指向字符串和指向字符数组指针的区别

我们看下面的代码:

#include <stdio.h>
int main()
{
  char str1[] = "abcdef.";//指向字符数组
  char str2[] = "abcdef.";//的字符指针
  const char *str3 = "abcdef.";//直接指向字符串
  const char *str4 = "abcdef.";//的字符指针
  if(str1 ==str2)
printf("str1 and str2 are same\n");
  else
printf("str1 and str2 are not same\n");
  if(str3 ==str4)
printf("str3 and str4 are same\n");
  else
printf("str3 and str4 are not same\n");
  return 0;
}

结果:

image.png

第一组:

比较的是地址,虽然数组str1和数组str2中的内容一模一样,但是他们的地址不一样,也就是所,地址所指向的内存不是同一块

image.png

第二组:

1.C/C++会把常量字符串存储到单独的一个内存区域,当

几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。

2.str3和str4比的是地址,地址是存放别人的地址,也就是字符串的地址

image.png

二、指针数组

指针数组是数组,里面存放的数组都是指针类型---前面已经提到过

格式:

int* arr1[5];
char* arr2[6];

指针数组的应用:模拟二维数组、存放字符串

1.指针数组传参

当指针数组传参时,形参写成二级指针的形式。

三、数组指针

1.数组指针的定义

(1)数组指针,是指向数组的一种指针。

(2)数组指针的表示形式

#include<stdio.h>
int main()
{
  int arr1[10] = { 0 };
  int(*p1)[10] = &arr1;
  char* arr2[5] = { 0 };
  char* (*p2)[5] = &arr2;
  return 0;
}

image.png

数组名不就是首地址吗?为什么还需要取地址?因为&arr和arr的意义不同!我们通过下面的代码验证。数组指针初始化必须指定数组大小
#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  printf("arr = %p\n", arr);
  printf("arr+1 = %p\n", arr + 1);
  printf("&arr= %p\n", &arr);
  printf("&arr+1= %p\n", &arr + 1);
  return 0;
}

结果展示:

image.png

我们知道,指针的类型决定了指针+1或者-1的时候跳过多少字节。

比如:整形指针+1会跳过4字节,而数组指针的类型是数组,那么+1肯定是需要跳过一个数组(上述一个数组为40字节)。


因为&arr的含义是取出整个数组的地址,而arr只是数组首元素的地址,+1只会跳过一个数组类型。


2.数组指针的使用

前言:常用于二维数组传参


(1)了解二维数组的特性

image.png

   1.二维数组==一维数组的数组

2.二维数组的数组名==二维数组中第一行的地址

(2)二维数组的传参问题--引出数组指针

1)形参是二维数组的形式

#include <stdio.h>
void test(int arr[3][5],int r,int c)
{
  int i = 0;
  for (i=0;i<r;i++)
  {
    int j = 0;
    for (j=0;j<c;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} };
  test(arr,3,5);
  return 0;
}

这是二维数组传参,形参部分也是二维数组的形式(行号可以省略,但是列不能省)

2)形参是数组指针的形式

#include <stdio.h>
void test(int (*arr)[5], int r, int c)
{
  int i = 0;
  for (i = 0; i < r; i++)
  {
    int j = 0;
    for (j = 0; j < c; 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} };
  test(arr, 3, 5);
  return 0;
}

1.二维数组的数组名是第一行元素的地址

2.第一行是一个一维数组,传的是一维数组的地址

3.是地址,所以是指针;是一维数组,要求类型是数组类型;结合起来就是数组指针(的类型)。(类比:整形变量传地址,形参需要用整形指针的类型来接收)

image.png

数组指针的一个作用:作为二维数组传参的形式参数


四、函数指针

前言:函数指针是指向函数的指针,也属于指针类型的一种

1.函数指针的定义

(1)了解函数名

&函数名与函数名的区别

#include<stdio.h>
void test()
{
  printf("hhhh\n");
}
int main()
{
  test();
  printf("%p\n",test);
  printf("%p\n",&test);
  return 0;

运行结果:

image.png

   1.函数名就是函数的地址

2.&函数名与函数名没有区别,都是函数的地址

(2)函数指针变量

函数指针变量就是存放函数的地址,跟正常的指针变量一样

函数指针的格式:

#include<stdio.h>
void test()
{
  printf("hhhh\n");
}
int main()
{
  test();
  void (*p)() = &test;函数指针
  return 0;
}

图解:

image.png

1.函数指针的格式与数组指针的格式及其相似

2.去掉变量的名字,剩下的就是变量的类型

下面来认识函数指针的初步使用:

(3)通过函数指针调用函数

第一种:

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int (*p)(int,int) = &Add;
  int ret = 0;
  ret = (*p)(3, 5);//第一种
  printf("%d\n",ret);
  return 0;
}

若通过*解引用操作,必须有括号(*号可以有很多个或者没有,对结果没有影响)

第二种:

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int (*p)(int,int) = &Add;
  int ret = 0;
  ret = p(3, 5);//第二种
  printf("%d\n",ret);
  return 0;
}

通过函数的地址(与Add(3,5)直接调用类似)调用函数

五、函数指针数组

1.函数指针数组定义

1.首先,是一个数组

2.是一个存放函数指针的数组,也就是存放函数地址的数组。

(1)代码定义:

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int Sub(int x,int y)
{
  return x - y;
}
int main()
{
  int (*p1)(int, int) = &Add;
  int (*p2)(int,int) = &Sub;
  //p1、p2是两个函数指针
  int (*p[4])(int, int) = {Add,Sub};
  //p就是函数指针数组
  return 0;
}

(2)图解:

image.png

2.函数指针数组的用途(转移表)

应用:做一个计数器

先看没有使用函数指针数组前的代码:

#include<stdio.h>
void menu()
{
  printf("****************************\n");
  printf("***  1. add      2. sub  ***\n");
  printf("***  3. mul      4. div  ***\n");
  printf("***  0. exit             ***\n");
  printf("****************************\n");
}
//加法
int Add(int x, int y)
{
  return x + y;
}
//减法
int Sub(int x, int y)
{
  return x - y;
}
//乘法
int Mul(int x, int y)
{
  return x * y;
}
//除法
int Div(int x, int y)
{
  return x / y;
}
int main()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Add(x, y);
      printf("ret = %d\n", ret);
      break;
    case 2:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Sub(x, y);
      printf("ret = %d\n", ret);
      break;
    case 3:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Mul(x, y);
      printf("ret = %d\n", ret);
      break;
    case 4:
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = Div(x, y);
      printf("ret = %d\n", ret);
      break;
    case 0:
      printf("退出计算器\n");
      break;
    default:
      printf("选择错误, 重新选择\n");
      break;
    }
  } while (input);
  return 0;
}

1.该计数器可以实现的功能:加减乘除

2.我们可以发现上面的代码很冗长,很多啰嗦的代码

观察发现:每个功能函数的返回值类型、函数参数类型及个数都一样,属于同类型函数,这就可以联想到数组。存放函数地址的数组,自然而然就是函数指针数组。

改进后:

#include<stdio.h>
void menu()
{
  printf("****************************\n");
  printf("***  1. add      2. sub  ***\n");
  printf("***  3. mul      4. div  ***\n");
  printf("***  0. exit             ***\n");
  printf("****************************\n");
}
//加法
int Add(int x, int y)
{
  return x + y;
}
//减法
int Sub(int x, int y)
{
  return x - y;
}
//乘法
int Mul(int x, int y)
{
  return x * y;
}
//除法
int Div(int x, int y)
{
  return x / y;
}
int main()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    //函数指针数组 - 转移表
    int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
    // 为了与菜单选项对应上      0     1     2   3    4
    if (0 == input)
    {
      printf("退出计算器\n");
    }
    else if (input >= 1 && input <= 4)
    {
      printf("请输入2个操作数:");
      scanf("%d %d", &x, &y);
      ret = pfArr[input](x, y);//函数指针数组的使用
      printf("ret = %d\n", ret);
    }
    else
    {
      printf("选择错误,重新选择!\n");
    }
  } while (input);
  return 0;
}

   1.经过对比,明显改进后的更加简洁

2.函数指针数组的使用:直接通过数组下标的引用,再传参即可

image.png

六、指向函数指针数组的指针(了解)

1.给出定义(类比)

(1)指向整形指针数组的指针

#include<stdio.h>
int main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  //整形指针数组
  int* arr[3] = {&a,&b,&c};
  //指向整形指针数组的指针
  int* (*p)[3] = &arr;
    return 0;
}

p为指向整形指针数组的指针变量,*说明p是指针。把*p去掉,剩下就是该指针变量指向的类型。

(2)指向函数指针数组的指针

image.png


相关文章
|
24天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
77 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
24天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
48 9
|
24天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
121 13
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
28天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
100 3
|
28天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
28天前
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
45 2
|
28天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
38 1
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。