深究C语言3.指针篇-下

简介: 引子:众所周知,指针就是存放地址的一个变量,我们之前的学习中,指针里面存放的就是一些基础变量的地址,而我们现在就来深度研究一下。既然指针是用来存放地址,那么,数组也有地址,函数也有地址,甚至包括我们后面要学习的结构体也有地址,那指针能不能存放他们的地址呢?

咳咳,好久没更新了,最近比较懒,我会慢慢改正的,尽量早点继续和大家分享,也真的希望大家能一起分享和学习。好了,现在开始我们接下来的学习。  


引子:众所周知,指针就是存放地址的一个变量,我们之前的学习中,指针里面存放的就是一些基础变量的地址,而我们现在就来深度研究一下。既然指针是用来存放地址,那么,数组也有地址,函数也有地址,甚至包括我们后面要学习的结构体也有地址,那指针能不能存放他们的地址呢?


答案当然是肯定的,反过来来想,那数组能不能用来存放指针呢?哈哈哈,我们往下面学一学就知道了。

一,指针数组和数组指针


我们通过前面的学习知道,数组是用来存放同种类型元素的自定义类型。而且我们也知道,所有指针都是一种类型的,就是一个普通变量,只不过里面存储的是地址罢了。


所以我们大胆猜测,是一定存在一种数组里面的元素是指针的这样一类数组。


那我们接下来讲一讲。


一,指针数组


故名思意,指针数组就是一个数组里面的元素都是指针,其实也没啥难的,只要你数组学的够扎实,这个内容对你而言就是很简单的。


我也没啥好讲的,直接上例子吧。


#define _CRT_SECURE_NO_WARNINGS 1
//随机发牌(指针的综合应用)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
struct card {
  int face;
  int suit;//suit是花色,face是点数
};
//发牌
void deal(struct card* wdeck) {
  int i, m, t;
  //发牌标记0:未发  1:已发
  static int temp[52] = { 0 };
  //产生随机数
  srand(time(NULL));//括号里面的是种子,函数根据种子的值得到随机数。对rand函数进行初始化,不然rand函数无法产生随机值
  for (i = 0; i < 52; i++) {
    while (1) {
      //函数并不能做到真正的随机,所以函数的返回值又叫做伪随机数。
      m = rand() % 52;//产生从0~51的随机值。
      //判断随机产生的牌发过没有,如果未发过,跳出循环,再随机分配花色和点数
      if (temp[m] == 0)
        break;
    }
    //再将此牌的值定为1,代表已经发过了
    temp[m] = 1;
    //有四个玩家,一个人13张牌,我也不知道这个式子怎么来的,反正带进去是对的
    t = (i % 4) * 13 + (i / 4);
    //对应下面的指针数组
    wdeck[t].suit = m / 13;
    wdeck[t].face = m % 13;
  }
}
int main() {
  int i;
  //52张扑克牌
  struct card deck[52];
  const char* suit[] = { "Heart","Diamond","Club","Space" };
  const char* face[] = { "A","K","Q","J","10","9","8","7","6","5","4","3","2" };
  //调用函数,参数为结构体指针
  deal(deck);
  for (i = 0; i < 52; i++) {
    if (i % 13 == 0) {
      printf("Player %d :\n", i / 13 + 1);
    }
    printf("%s of %s\n", face[deck[i].face], suit[deck[i].suit]);
  }
  return 0;
}


原谅我选出这个题目的时候不地道的笑了,咳咳,这个题目难度确实比较大,但是我相信你们,你们是最棒的,我记得前几天的时候这个随机发牌问题还是热榜头条,哈哈哈。好好看看,确实找不到啥好的例子了。


用指针数组的时候,你只需要搞清楚,它的本质是数组,也就是说,它的便利性和实用性大部分都是数组提供的,那么,数组有啥优越性?


一个是提供了选择的便利性,再一个就是存储的功能。其实好像也没啥。但是就是因为选择的优越性,可以使题目变得更加简便。


二,数组指针


这个东西就也是顾名思义了,就是一个指针指向了一个数组,一个数组那么长,指针怎么去存放那么多地址呢?


这里我们就要知道,C语言中数组是连续存放的,即,只要知道该数组的首地址,我们就可以通过首地址找到该数组中任何一个元素,所以,数组指针同样只是存放了数组首元素的地址,用不同的指针,指向各种元素。常用于下面要讲的嵌套里面,再常用一点的地方就是二维数组里面,为什么不用在一维数组里面呢?你猜。


之前的数组篇里面我专门讲过,二维数组的数组名表示的是第一行的地址,那么此时,我们就可以用一个数组指针指向这个二维数组的数组名,该指针指向的就是二维数组的第一行。


int(parr*)[]=&arr;


这就是数组指针的表达形式。


#include<stdio.h>
#define N 3
int main(){
  int arr[N][N],j,i,sum=0;
  int (*p)[3]= &arr;
  for(i=0;i<N;i++)
  for(j=0;j<N;j++){
    scanf("%d",*(*(p+i)+j));
    if(i==j)
    sum+=a[i][j]
  }
  printf("%d",sum);
  return 0;
}


下面的就是一些常见的引用操作,这个字不是我写的,懂?


4ffd7779e09b4de8a5fbfe99af391695.jpg

a449e00385c943f0a2955a070b126ef1.jpg


二,函数指针和指针函数


我们还是和前面一样,我们先来分析,函数指针或者指针函数究竟有什么用呢?我们首先来单独分析一下函数这个概念。


函数能给我们带来什么好处呢?简单来说可以概括成以下几点: 1、降低复杂性:使用函数的最首要原因是为了降低程序的复杂性,可以使用函数来隐含信息,从而使你不必再考虑这些信息。


2、避免重复代码段:如果在两个不同函数中的代码很相似,这往往意味着分解工作有误。这时,应该把两个函数中重复的代码都取出来,把公共代码放入一个新的通用函数中,然后再让这两个函数调用新的通用函数。通过使公共代码只出现一次,可以节约许多空间。


因为只要在一个地方改动代码就可以了。这时代码也更可靠了。


3、限制改动带来的影响:由于在独立区域进行改动,因此,由此带来的影响也只限于一个或最多几个区域中。


4、隐含顺序:如果程序通常先从用户那里读取数据,然后再从一个文件中读取辅助数据,在设计系统时编写一个函数,隐含哪一个首先执行的信息。


5、改进性能:把代码段放入函数也使得用更快的算法或执行更快的语言(如汇编)来改进这段代码的工作变得容易些。


6、进行集中控制:专门化的函数去读取和改变内部数据内容,也是一种集中的控制形式。


7、隐含数据结构:可以把数据结构的实现细节隐含起来。


8、隐含指针操作:指针操作可读性很差,而且很容易引发错误。通过把它们独立在函数中,可以把注意力集中到操作意图而不是集中到的指针操作本身。


9、隐含全局变量:参数传递。转载于:https://www.cnblogs.com/fhrtr/p/3193685.html


这些其实我都不清楚多少,了解一下就好。


好了,还有一点要知道的就是,数组名≠&数组名。但是,函数名=&函数名,&函数名取得就是该函数的地址。


一,函数指针


这个我有例子哦,就是前面就写过的模拟实现计算器的第二种方法。


但是这个是函数指针数组嵌套使用的,这些东西通常都是嵌套使用的,学到现在,大家也都应该 知道,越往后,越都是那些复杂的东西,需要实现的东西多了,需要的语句也就多了,嵌套便是比较简单的一种操作方法了,常见且重要。


提一下,函数指针数组也叫做转移表,这个应该在后面的算法里面会讲到。


#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu(void) {
  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() {
  //这里并没有用switch结构,这里跟第一个程序不同,上一个程序中input定义在了switch外面,所以没啥问题
  //但是在这里,函数的主体就只有一个循环,由于我们用的是do-while循环,最后while后面的input是在循环体外面的,
  //如果在里面定义input的话,作用域就到不了外面,故,我们把input定义在外面。
  int input = 0;
  do {
    //这里我们采用函数指针数组的方法。
    int (*parr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
    int x, y, ret;
    x = y = ret = 0;
    //输出菜单
    menu();
    //选择方法
    printf("请选择:>");
    scanf("%d", &input);
    if (input >= 1 && input <= 4) {
      //初始化数据
      printf("请输入2个操作数:");
      scanf("%d%d", &x, &y);
      //用函数指针的方法,用指针调用函数
      ret = (parr[input])(x, y);
      printf("ret =%d\n", ret);
    }
    else if (input == 0)
      printf("程序结束。");
    else
      printf("选择错误,请重新选择\n");
  } while (input);
  return 0;
}


建议大家还是去我之前写的文章里去看一看解析,不然我怕你看不懂,嘿嘿。好好理解一下函数指针数组的优越性吧,或者说函数指针的优越性,因为是需要传参的,传进去哪个函数的地址,就实现那个函数的的功能。


返回类型 函数名(参数类型){}
int (*p)(int ,int){}//函数指针
int fun(int x,int y){}//普通函数


二,指针函数


指针函数的意思就是一个函数它的返回值是一个指针,也就是一个地址。


写法也与上面及其的相似。


int* fun(int x,int y){}


发现没有,就是少了一个括号,我也挺无语的。这个式子代表的就是,一个叫做fun的函数的返回值是一个int类型的指针。


三,最后需要讲一下


最后讲一下的就是这个二级指针的问题


说实话,现在的学习中很少很少能遇到二级指针,根本很难用到它。


int *pa=&a;//一级指针
int **ppa=pa;//二级指针
ppa=&pa;
*ppa=&a;
**ppa=a;//二级指针的引用


唯一需要着重讲一下的就是,二级指针加减1,代表的是走了4/8个字节,依配置而定,但是走的长度一定是固定的,因为二级指针中存放的是一维指针的地址,而一维指针占4/8个字节。


别问为什么就这么点,什么东西就是基础最重要,把前面的那些搞懂,后面的东西是很好理解的。


希望大家能学到一些东西,欢迎大家评论发言。

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