图片[1]yxqzC语言的指针yxqz小余塘

作者:余小小

日期:2021-10-02

指针概念

  • 每一个变量都有一个内存位置

  • 每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

  • 指针的接收符号:%p

  • 定义指针变量:新号

  • 指针的获取方式: & 运算符(地址运算符)

打印一个指针

为了方便读懂,我这里改了改demo的变量名字

#include <stdio.h>
​
int main ()
{
   int demo = 10;
   int *zhizhen;              // 定义指针变量
   zhizhen = &demo;
​
  printf("demo 变量的地址: %p\n", zhizhen);
  return 0;
}

 

代码分析:

  1. 启动程序,执行main函数

  2. 定义了一个叫做demo的变量,值是10,在内存中开辟了一处空间,放了这个值,并且给这个空间的地址赋予了一串十六进制的代码,类似于你游戏的激活码

  3. 定义了一个指针变量,一定要注意这里的*号,是区别指针和基本数据类型的变量的依据。

  4. 定义的指针变量的初始值,这里暂时不考虑

  5. 给定义的指针变量的指针赋值,赋予的是demo变量的地址值(不是demo的值哦)

  6. 打印输出,结束

image-20211130231701767

什么是指针?

  • 指针也就是内存地址

  • 完整的指针变量是有带了个新号的哦

  • 指针变量的变量名是用来存放内存地址(变量指针)变量指针,是指针变量没有新号的变量

  • 指针变量是用来存储指针所在的位置的值(指针变量)

  • 要注意,和其他变量的区别是一个新号

  • 指针也是有数据类型的,不是的单独的一个数据类型

就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。

指针的声明语法伪代码:

数据类型 *指针变量名字;
int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

 

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同

指针和值没有关系,和地址关系

 

 

使用指针

#include <stdio.h>

int main ()
{
  int  var = 20;   /* 实际变量的声明 */
  int  *ip;        /* 指针变量的声明 */

  ip = &var;  /* 在指针变量中存储 var 的地址 */

  printf("var 变量的地址: %p\n", &var );

  /* 在指针变量中存储的地址 */
  printf("ip 变量存储的地址: %p\n", ip );

  /* 使用指针访问值 */
  printf("*ip 变量的值: %d\n", *ip );

  return 0;
}

 

可以看到指针的值和地址值是一样的哦

image-20211130232918981

NULL 指针

  • 赋为 NULL 值的指针被称为指针。

  • 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。

  • NULL 指针是一个定义在标准库中的值为零的常量

#include <stdio.h>

int main ()
{
  int  *ptr = NULL;

  printf("ptr 的地址是 %p\n", ptr );

  return 0;
}

 

  • 每个编译器的结果不一样哦,我这里是十进制的0

  • 如果简写,则是0x0,0x(数字零和字母x)代表十六进制

image-20211130233106887

  • 大多数的操作系统上,程序不允许访问地址为 0 的内存

  • 因为该内存是操作系统保留的。

  • 然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。

  • 但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

检查空指针

在其他语言中,我们习惯的直接判断变量,比如在前端js中,最常用的就是用if直接判断变量是否存在,存在则执行某些东西,也是用到指针判断。其实就是判断地址是否存在。

if(ptr)     /* 如果 p 非空,则通过 */
if(!ptr)    /* 如果 p 为空,则通过 */

 

指针的算术运算

  • 指针因为也是一个数值,所以可以执行数值可以执行的 各种运算,比如四则运算等等

  • 指针的每次运算的单位,都是根据当前的数据类型,比如一个int字节是4个字节,short,是2个字节,char是1个字节,double是8个字节,float是4个字节,汉字是3个字节,long是8个字节。

  • 指针的每一次递增,它其实会指向下一个元素的存储单元。

  • 指针的每一次递减,它都会指向前一个元素的存储单元。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。

这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。

所以说,

  • 指针的跳跃范围,一定要看数据类型

  • 指针的跳跃范围,一定要看数据类型

  • 指针的跳跃范围,一定要看数据类型

递增一个指针

在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量

数组变量名字就是装的数组第一个元素的地址值

#include <stdio.h>

const int MAX = 3;

int main ()
{
  int  var[] = {10, 100, 200};
  int  i, *ptr;

  /* 指针中的数组地址 */
  ptr = var;
  for ( i = 0; i < MAX; i++)
  {

     printf("存储地址:var[%d] = %p\n", i, ptr );
     printf("存储值:var[%d] = %d\n", i, *ptr );

     /* 指向下一个位置 */
     ptr++;
  }
  return 0;
}

 

代码解析:

  1. 动态初始化了一个数组

  2. 定义了一个和数组相同数据类型的变量i,和指针变量ptr

  3. 直接将数组的第一个元素的地址赋值给了指针变量ptf(没有用到地址符号,因为数组的名字就是装的元素的首位地址)

  4. 直接循环遍历

  5. 打印出地址的值(十六进制的,我们看不懂,所以这里暂时没有参考意义),注意,这里是用的变量指针

  6. 打印数组存储属性的值:这里使用的指针变量,则是变量,装的是当前指针指向的地址中的值

  7. 指针跳跃:按照数据类型进行跳跃,这里是int类型,所以一次跳跃4个字节,但是是直接跳跃到下一个指针的位置,从索引来看,则是跳跃了1个距离。

指针的比较

指针可以用关系运算符进行比较

如 ==、< 和 >。

如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

#include <stdio.h> 
const int MAX = 3; 
int main (){   
    int  var[] = {10, 100, 200};  
    int  i, *ptr;    /* 指针中第一个元素的地址 */  
    ptr = var;  
    i = 0;   
    while ( ptr <= &var[MAX - 1] )  
    {      
        printf("存储地址:var[%d] = %p\n", i, ptr ); 
        printf("存储值:var[%d] = %d\n", i, *ptr );  
        /* 指向上一个位置 */     
        ptr++; 
        i++;  
    }  
    return 0;
}

 

代码分析:

  1. 定义了一个数组

  2. 定义了一个变量

  3. 定义了一个指针变量

  4. 将数组的首位地址,赋值给变量指针ptr

  5. 使用循环遍历,条件是指针变量是在数组的元素地址之内,简单 的说,就是变量指针的跳跃范围,是在数组元素地址所在的范围之内

  6. 以为变量指针的初始值是数组的第一位元素的地址,所以我觉的应该是跳跃到下一个指针的位置哦

函数指针

  • 函数指针 是指向函数的 指针变量。

  • 通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

  • 函数指针可以像一般函数一样,用于调用函数、传递参数。

  • 函数指针,是函数

  • 指针函数,是指针

  • 函数的名字,也是存储的函数地址值

  • 指针变量,是指针

  • 变量指针,是变量

  • 使用函数指针或者变量指针,或者是xxx指针,都是不用带星号的,因为不是当作指针来使用,都是当作原来的数据来使用。

函数指针的格式

返回值类型  (*函数指针名字)(传入参数的数据类型); // 声明一个指向同样参数、返回值的函数指针类型
#include <stdio.h>
int max(int x, int y){
return x > y ? x : y;
}
int main(void){
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}

 

  • 函数指针和直接调用函数一样

回调函数

执行一个函数后还要执行一个你规定的函数,比如定时炸弹,时间到了,炸弹就触发了

触发炸弹,就是用 的指针来触发,是通过传入指针函数来触发实现触发炸弹的函数指针

#include <stdlib.h>
#include <stdio.h>
// 回调函数
void dingshizhadan(void (*didi)(void))
{
printf("定时炸弹开始启动了\n");
// 调用传入函数指针
didi();
}
// 获取随机值
void chufa(void)
{
printf("炸弹触发,啵啵啵\n");
}int main(void) {
dingshizhadan(&chufa);
return 0;
}

 

  • 我是参考其他教程来写,这里的main中的括号中的void可写可不写,每个c的版本不一样,有些语法规则有点点不同,不影响的哦

  • 我之类先调用了定时炸弹的函数,传入的是一个函数的地址,建议在刚开始学习的时候,把地址符号加上

  • 在回调函数中,我传入了一个自定义的dd指针函数参数。记住了哦,我这里是传入的指针哦(地址)

  • 括号里的void可以不用管哦,因为我也没带有参数。

  • 然后定时炸弹开始启动,然后再调用指针函数的函数指针,这里的dd实际上映射的就是chufa函数。

文章版权声明 1、本网站名称:小余塘
2、本站永久网址:https://www.yxqz.top/
3、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长QQ2457431511进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容