C语言如何跨文件调用函数定义中声明的变量

简介: 前几天向老师请教问题,结果被打击的体无完肤,评价为基础太差。于是敝人埋头研究了几日,颇有所得,之前困扰我许久的作用域问题也都不攻自破,不由得感叹,有个老师指导真好hhh
  • 变量的作用域

我们知道,变量根据其作用域有全局变量和局部变量之分。全局变量作用域是整个文件,并且可以使用关键字 extern 达到跨文件调用的目的。但是局部变量值作用于它当前所在的块(即该变量所处最内层中括号之间的区域),当函数执行完并离开当前块后,局部变量及其所处内存空间的值就会销毁。

  • static 关键字

static + 变量声明 表示声明的这个变量是静态变量。静态不是说它的值不会被改变,而是它在内存中的地址静止不动,这也就意味着它的值从一直到函数运行结束之前,都不会被销毁。有趣的是,它声明的的变量只会声明一次,再次调用声明语句时,会自动跳过这条语句。如下面这段代码:

#include <stdio.h>
int main()
{
    int i = 0;
    printf("num1\tnum2\n");
    for(i = 0;i<4;i++)
      { static int num1 = 4;
        int num2 = 4;
        num1 ++;
        num2 ++;
        printf("%d\t%d\n",num1,num2)      
      }
}  

运行结果为:

screenshot
可以看到static初始化的num1变量,虽然我们放在循环中,看起来好像每次都会对变量进行初始化,但是结果却出人意表,num1在循环中没有受到初始化语句的影响,可以正常的进行运算。

  • 指针

指针是一个值为内存地址的变量(或数据对象)。指针本身是不具有内存的,但是它可以通过赋值,指向其他变量的地址进而得到它的内存。因为在计算机中,所有的变量都会有单独一块内存空间,所以理论上,只要使用指针指向变量的地址,就可以在程序的任何位置调用变量。所以,指针是我们实现跨文件跨域调用变量最重要的手段。

  • 使用指针跨文件调用变量

有了上面的基础,我们就可以开始调用变量了。为了表现跨文件的功能,我们定义一个头文件 test.h ,并且在里面放入一个函数声明 void Input(); ,然后我们再创建一个 test.c 文件来作为头文件的实现文件。最后在主文件 main.c 中调用它,函数代码如下:
test.c文件

#include "test.h" 
int *ptr;        //声明一个外部指针用来调用变量
void Input()
{
     //初始化一个静态的数组
     static int nums[10] = {1,2,3,4,5,6,7,8,9,10};
     ptr = nums;  //将外部指针指向数组首地址 
}

main.c文件

#include "test.h"       //引用我们自定义的头文件
#include <stdio.h>
   extern int *ptr;       //调用我们的外部指针
   int main()
  {   
     Input();             //初始化数组并给指针赋值
     //下面打印结果,验证是否成功调用变量
     for(int i = 0;i<10;i++) 
       {
        printf("%d\t",*(ptr + i));
       }
   }

运行结果为
screenshot
可以看到,我们通过一个外部指针,在 Input() 函数内部将指针指向变量的地址,成功的调用了在其他文件的函数内部声明的数组/变量。
事实上,在这里面,static 关键字起到了至关重要的作用。我们尝试将nums[]数组改成普通的自动(auto)变量,看看运行结果:
运行结果为
screenshot
可以看到,循环打印出来的是一片混乱的数值,这意味着指针向内存的数值已经被销毁了

目录
相关文章
|
19天前
|
存储 编译器 C语言
【C语言】C语言的变量和声明系统性讲解
在C语言中,声明和定义是两个关键概念,分别用于告知编译器变量或函数的存在(声明)和实际创建及分配内存(定义)。声明可以多次出现,而定义只能有一次。声明通常位于头文件中,定义则在源文件中。通过合理组织头文件和源文件,可以提高代码的模块化和可维护性。示例包括全局变量、局部变量、函数、结构体、联合体、数组、字符串、枚举和指针的声明与定义。
45 12
|
24天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
112 14
|
28天前
|
C语言
【C语言】全局搜索变量却找不到定义?原来是因为宏!
使用条件编译和 `extern` 来管理全局变量的定义和声明是一种有效的技术,但应谨慎使用。在可能的情况下,应该优先考虑使用局部变量、函数参数和返回值、静态变量或者更高级的封装技术(如结构体和类)来减少全局变量的使用。
34 5
|
28天前
|
编译器 C语言
【C语言】宏定义在 a.c 中定义,如何在 b.c 中使用?
通过将宏定义放在头文件 `macros.h` 中,并在多个源文件中包含该头文件,我们能够在多个文件中共享宏定义。这种方法不仅提高了代码的重用性和一致性,还简化了维护和管理工作。本文通过具体示例展示了如何定义和使用宏定义,帮助读者更好地理解和应用宏定义的机制。
45 2
|
1月前
|
存储 算法 C语言
C语言中常见的字符串处理技巧,包括字符串的定义、初始化、输入输出、长度计算、比较、查找与替换、拼接、截取、转换、遍历及注意事项
本文深入探讨了C语言中常见的字符串处理技巧,包括字符串的定义、初始化、输入输出、长度计算、比较、查找与替换、拼接、截取、转换、遍历及注意事项,并通过案例分析展示了实际应用,旨在帮助读者提高编程效率和代码质量。
97 4
|
1月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
115 3
|
2月前
|
存储 编译器 C语言
如何在 C 语言中判断文件缓冲区是否需要刷新?
在C语言中,可以通过检查文件流的内部状态或使用`fflush`函数尝试刷新缓冲区来判断文件缓冲区是否需要刷新。通常,当缓冲区满、遇到换行符或显式调用`fflush`时,缓冲区会自动刷新。
|
2月前
|
存储 编译器 C语言
C语言:文件缓冲区刷新方式有几种
C语言中文件缓冲区的刷新方式主要包括三种:自动刷新(如遇到换行符或缓冲区满)、显式调用 fflush() 函数强制刷新、以及关闭文件时自动刷新。这些方法确保数据及时写入文件。
|
2月前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
|
2月前
|
存储 C语言
【c语言】数据类型和变量
本文介绍了C语言中的数据类型和变量。数据类型分为内置类型和自定义类型,内置类型包括字符型、整型、浮点型等,每种类型有不同的内存大小和取值范围。变量分为全局变量和局部变量,它们在内存中的存储位置也有所不同,分别位于静态区和栈区。通过示例代码和图解,详细阐述了这些概念及其应用。
58 1