热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C语言指针形参(指向指针的指针形参)

一、通过指针形参在子函数改变常量大家都知道,C语言子函数的形参,是可以为普通数据类型,也可以为指针的。最初遇到这问题,是在学习STM32的库函数的使用。当初刚接触库函数,对于函数初

一、通过指针形参在子函数改变常量

大家都知道,C语言子函数的形参,是可以为普通数据类型,也可以为指针的。最初遇到这问题,是在学习STM32的库函数的使用。当初刚接触库函数,对于函数初始化接口,如:

GPIO_Init(GPIOA, &GPIO_InitStructure);
为什么要取初始化结构体变量的地址传递进库函数(&GPIO_InitStructure),而不是直接将结构体变量本身(GPIO_InitStructure)传递进去,不甚了解。直到后来,程序中指针用得多了才有所理解,在这里做个记录。先上代码:

#include
void AddNum1(int data)
{
  data++;
}
void AddNum2(int *data)
{
  (*data)++; //自增运算符“++”优先级高于取值符“*”
  printf("AddNum2()运行结果:\r\n\r\n*data = %d\r\n", *data);
  printf(" data = 0x%08X\r\n", data);
  printf("&data = 0x%08X\r\n\r\n", &data);
}
int main(void)
{
  int Num1 = 1;
  //测试AddNum1
  AddNum1(Num1);
  printf("AddNum1()运行结果:\r\n\r\nNum1 = %d\r\n", Num1);
  Num1 = 1;
  printf("\r\n\r\n");
  //测试AddNum2
  AddNum2(&Num1);
  printf(" Num1 = %d\r\n", Num1);
  printf("&Num1 = 0x%08X\r\n", &Num1);
  getchar();
}

运行结果如图:
技术分享图片

 

编译环境为VS2015。

可知,AddNum1没有改变Num1的值,而AddNum2将Num1的值自增了1。分析:

(1)对于子函数形参的理解:

        主函数中的代码“AddNum1(Num1);”。实质上,它将Num1的值赋值给了子函数的形参“data”。

        可将“AddNum1(Num1);”代码理解为运行了以下代码:

void AddNum1()
{
  int data = Num1;
  data++;
}

通俗的解释就是,子函数声明了一个整型常量“data”,用“data”缓存“Num1”的值。函数中的其他代码,是针对“data”进行运算的,而“Num1” 除了把它自身的值传递给“data”外没有其他任何操作。所以,“AddNum1();” 这个函数并有没改变“Num1”的值。

(2)指针形参的作用:

        我们在对常量,或者是指针进行操作的时候,实质上是对其对应的内存进行操作。对“AddNum2(&Num1);”运行结果以 内存分布图诠释如下:

技术分享图片

1、可知,Num1的地址是0x00600FFA0C,”AddNum2(int *data)“;声明了一个指针data,并且将Num1的地址赋值给了指  针 data,相当于执行了”data = 0x00600FFA0C;“,此时”*data“ 等同于”Num1“。

2、接下来的”(*data)++;“,操作的是指针data指向的内存”0x00600FFA0C“,这行代码使这个内存块上存储的常量自    增了1,所以”*data = 2“。由1可知,”*data“ 等同于 ”Num1“,所以“*data = 2 = Num1”。

 总结:通过将变量地址传递进子函数,在子函数内操作该地址的内存上存储的数据可达到改变变量的目的。

二、通过指向指针的指针在子函数改变指针的值

这种情况我用得比较少。不过在调用内存管理函数的时候可能会用到。如下代码:

主函数声明了一个指向0x00000001地址的char型指针pMemory,并通过子函数申请内存,将申请得到的地址赋值给pMemory

#include
#include
void GetMemory1(char *pAddr)
{
  pAddr = (char *)malloc(sizeof(char) * 100);
}
void GetMemory2(char **pAddr)
{
  *pAddr = (char *)malloc(sizeof(char) * 100);
  printf(" *pAddr = 0x%08x\r\n", *pAddr);
  printf(" pAddr = 0x%08x\r\n", pAddr);
  printf(" &pAddr = 0x%08x\r\n\r\n", &pAddr);
}
int main(void)
{
  char *pMemory = 0x00000001;
  printf(" pMemory = 0x%08x\r\n", pMemory);
  printf("&pMemory = 0x%08x\r\n\r\n", &pMemory);
  GetMemory1(pMemory);

  printf("/*******GetMemory1();********/\r\n\r\n");
  printf(" pMemory = 0x%08x\r\n", pMemory);
  printf("&pMemory = 0x%08x\r\n\r\n", &pMemory);
  printf("/*******GetMemory2();********/\r\n\r\n");
  GetMemory2(&pMemory);
  printf(" pMemory = 0x%08x\r\n", pMemory);
  printf("&pMemory = 0x%08x\r\n", &pMemory);


  getchar();
}
运行结果:

 技术分享图片

如上,GetMemory1();并不能将地址赋值给pMemory,而GetMemory2();成功将申请得到的地址赋值给pMemory。分析:

(1)GetMemory1();

        类似于“AddNum1();”,声明了一个指针pAddr,然后将pMemory的值赋值给pAddr。后续代码改变的是pAddr的数据 而没有改变pMemory。所以没有成功地将申请得到的地址赋值给pMemory。

(2)GetMemory2();

        先上图:

 技术分享图片

  进入函数后,pMemory的地址0x008FF718赋值给了pAddr。malloc();申请得到的内存空间的地址0x02BB4D80赋值给了pAddr所指向的内存“*pAddr”(0x008FF718)。又因为*pAddr = pMemory; ,所以申请得到的内存地址成功赋值给了pMemory。

  综上所述,当数据被传递进子函数,如需通过子函数改变数据的值,需将它的地址作为形参传递进函数(无论常量亦或是指针)。


推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • andr ... [详细]
  • 本文介绍如何使用Python进行文本处理,包括分词和生成词云图。通过整合多个文本文件、去除停用词并生成词云图,展示文本数据的可视化分析方法。 ... [详细]
  • 在Java中,this是一个引用当前对象的关键字。如何通过this获取并显示其所指向的对象的属性和方法?本文详细解释了this的用法及其背后的原理。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • CMake跨平台开发实践
    本文介绍如何使用CMake支持不同平台的代码编译。通过一个简单的示例,我们将展示如何编写CMakeLists.txt以适应Linux和Windows平台,并实现跨平台的函数调用。 ... [详细]
  • 本文介绍如何使用 NSTimer 实现倒计时功能,详细讲解了初始化方法、参数配置以及具体实现步骤。通过示例代码展示如何创建和管理定时器,确保在指定时间间隔内执行特定任务。 ... [详细]
  • VPX611是北京青翼科技推出的一款采用6U VPX架构的高性能数据存储板。该板卡搭载两片Xilinx Kintex-7系列FPGA作为主控单元,内置RAID控制器,支持多达8个mSATA盘,最大存储容量可达8TB,持续写入带宽高达3.2GB/s。 ... [详细]
  • 本文介绍了几种不同的编程方法来计算从1到n的自然数之和,包括循环、递归、面向对象以及模板元编程等技术。每种方法都有其特点和适用场景。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文详细探讨了C语言中指针的概念,特别是指针在变量和数组中的应用。通过实例讲解,帮助读者更好地掌握指针的使用方法。 ... [详细]
  • C语言标准及其GCC编译器版本
    编程语言的发展离不开持续的维护和更新。本文将探讨C语言的标准演变以及GCC编译器如何支持这些标准,确保其与时俱进,满足现代开发需求。 ... [详细]
author-avatar
剡亚军_191
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有