【新手解答4】深入探索 C 语言:全局变量声明、全局函数声明 + 宏定义

C语言的相关问题解答

写在最前面

一位粉丝私信交流,回想起了当初的我C语言一题写一下午的,而且很多概念糊糊的不清楚。借助这次机会,整理了相关 C 语言中最常见而关键的疑惑,并对概念进行解答。

本篇博客的前身:
【新手解答1】深入探索 C 语言:变量名、形参 + 主调函数、被调函数 + 类和对象 + 源文件(.c 文件)、头文件(.h 文件)+ 库
【新手解答2】深入探索 C 语言:变量名、变量 + 函数声明 vs 函数定义 + main的声明 + 头文件和源文件的关系
【新手解答3】深入探索 C 语言:头文件提供必要的接口、源文件保持实现细节的私有性 + 进一步学习的方向 + 如何快速编写程序并最终能制作小游戏

如有不对的地方欢迎指出!好久没接触C了hh

问题一:全局声明

全局声明又是什么呢?

全局声明在编程中通常指的是在程序的任何地方都可以访问的变量或函数的声明。在 C 语言中,全局声明可以是全局变量的声明或者函数的声明。这些声明放在所有函数之外,通常放在源文件的顶部或者头文件中。这样做的目的是为了让这些变量或函数在整个程序中都是可见的和可用的。

全局变量的声明

全局变量在所有函数之外声明,通常在文件的开始处。它们在整个程序的执行期间都存在,并且在程序的任何地方都可以被访问和修改。

例子:

c 复制代码
#include <stdio.h>

int globalVar = 10; // 全局变量声明

void display() {
    printf("Global variable: %d\n", globalVar);
}

int main() {
    display();
    return 0;
}

在这个例子中,globalVar 是一个全局变量。它在 main 函数和 display 函数中都是可见的。

全局函数的声明

全局函数的声明意味着函数的原型在所有函数之外声明,这样在不同的源文件中的其他函数就可以调用这些函数。通常,函数的原型放在头文件中,然后在需要的源文件中包含这个头文件。

例子:

c 复制代码
// math_functions.h
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

int add(int a, int b); // 函数声明

#endif

// math_functions.c
#include "math_functions.h"

int add(int a, int b) {
    return a + b;
}

// main.c
#include <stdio.h>
#include "math_functions.h"

int main() {
    int result = add(5, 3);
    printf("Result: %d\n", result);
    return 0;
}

在这个例子中,add 函数在 math_functions.h 头文件中声明,然后在 math_functions.c 中定义。main.c 包含了 math_functions.h 头文件,所以它可以调用 add 函数。


全局声明使得变量和函数在程序的多个部分中都可以被访问。这对于需要在多个文件或函数间共享的数据和功能非常有用。然而,过度使用全局变量可能会导致代码难以维护,因为它们可以在程序的任何地方被改变,这可能导致预料之外的副作用。全局函数的使用则有助于代码的模块化和重用。

问题二:返回值&函数声明

(一个朋友说)返回由函数本身处理,而不是指向函数的声明

那假如一个函数的函数体中包含着一个函数声明,那这时函数声明,后面是否需要跟一个返回值来返回函数体?

哦,貌似找到答案了,返回值因为函数体存在而存在,因此,函数中无论引用什么声明之类的......都不会影响到返回值以及函数体本身,是这样的么?

是的,你的理解是正确的。在 C 语言中,函数的返回值是由函数自身的定义(函数体)决定的,而不是由函数的声明或函数体内部的其他声明决定的。让我更详细地解释这一点:

函数定义和返回值

  1. 函数定义(函数体):函数定义包含了函数的实现代码。它告诉编译器和程序如何执行该函数的特定任务。

  2. 返回值 :函数定义还规定了该函数的返回类型。在函数体中,通常会有一个 return 语句来返回一个值,这个值的类型必须与函数声明的返回类型相匹配。

函数声明和函数体内的声明

  • 函数声明:函数声明告诉编译器函数的名称、返回类型和参数。声明本身不包含任何关于如何执行函数的信息。

  • 函数体内的声明:虽然在函数体内部声明另一个函数是不常见(也不推荐)的做法,但即使这样做了,它也不会影响到包含它的函数的返回值或行为。函数体内部的任何声明都是独立的,不会影响外围函数的返回行为。

结论

  • 一个函数的返回行为完全由该函数的定义(函数体内的代码)决定。这包括它返回什么类型的值,以及在何种条件下返回。

  • 函数体内部的其他声明(例如另一个函数的声明)不会影响该函数的返回行为。

  • 总是建议将函数的声明放在头文件或源文件的顶部,而不是在另一个函数的定义中。

你的理解是正确的,函数的行为和返回值完全由它自己的定义决定,与函数体内的其他声明无关。

问题二延伸:优先采用全局声明

唔,那如果有这类需要(一个函数体内声明另一个函数),会怎么办?

唔......我朋友说要优先采用全局声明

如果朋友提到"优先采用全局声明",可能是指在编程实践中应该优先考虑将函数的声明放在全局范围内,也就是说,通常放在头文件中或者源文件的顶部。

在日常编程实践中,确实应该尽可能地将函数声明放在头文件或源文件的顶部,以提高代码的可维护性和清晰度。只有在有充分理由的情况下才考虑局部声明。

原因

"优先采用全局声明"这样做有几个重要的理由和好处:

1. 易于维护和查找

将函数声明放在全局范围(如头文件)使得这些函数可以被多个源文件共享。这样做的好处是,当需要修改函数原型或接口时,只需在一个地方进行更改,而不需要在多个文件中重复修改。

2. 避免重复声明

在 C 语言中,在同一个文件或代码范围内重复声明同一个函数是不必要的,也可能导致混淆。全局声明确保了每个函数只被声明一次。

3. 保持代码的清晰和组织性

将所有重要的函数声明放在一个集中的地方(如头文件)有助于保持代码的组织性和清晰性。这使得其他开发者(或未来的你)更容易理解程序的结构和可用的接口。

4. 支持模块化编程

全局声明支持模块化编程,使得代码更易于管理和重用。例如,你可以创建一个头文件,其中声明了一系列相关的函数,然后在需要这些功能的任何源文件中包含这个头文件。

在特殊情况下的内部声明

虽然一般推荐全局声明,但在某些特殊情况下,可能需要在函数体内部进行局部声明,比如声明局部函数或特定于某个功能的类型。这些情况通常比较少见,并且应谨慎使用以避免造成混淆。

问题三:宏定义

对了,查了一下宏的定义,字符串是宏体,标识符是宏名,预处理中会用宏体替换宏名,但这里的宏却仅仅指的是宏定义,那么宏声明呢?

在 C 语言中,宏主要是通过预处理指令 #define 来定义的。不像函数那样有声明和定义之分,宏通常只有定义,没有所谓的"宏声明"。宏定义在预处理阶段就被处理了,它们的作用是在编译之前,指示预处理器将所有宏名替换为对应的宏体。

它们是强大的工具,但也需要谨慎使用,因为不当的使用可能会导致代码维护和调试的困难。

宏定义的基本形式

宏定义的一般形式如下:

c 复制代码
#define 宏名 宏体

这里,"宏名"是你希望定义的宏的名称,而"宏体"是当宏名在代码中出现时要被替换成的文本。

例子

  1. 定义常量

    c 复制代码
    #define PI 3.14159

    在这个例子中,每当预处理器遇到 PI,它都会将其替换为 3.14159

  2. 定义宏函数

    c 复制代码
    #define SQUARE(x) ((x) * (x))

    这里定义了一个宏函数 SQUARE,用于计算一个数的平方。在代码中使用 SQUARE(5) 时,它会被展开为 ((5) * (5))

宏的特点

  • 文本替换:宏仅仅是简单的文本替换工具,预处理器不会检查宏体的语法或者进行类型检查。

  • 没有类型:与函数不同,宏不涉及数据类型。

  • 编译前处理:宏是在编译前处理的,因此它们不占用程序运行时的内存。

  • 调试困难:由于宏只是在预处理阶段进行文本替换,所以在调试时可能会带来不便,因为宏在编译后的代码中不再可见。


希望这些解释能帮助更清晰地理解这些概念!如果还有其他疑问或需要进一步的解释,随时欢迎提问。

相关推荐
百事老饼干3 分钟前
Java[面试题]-真实面试
java·开发语言·面试
霍格沃兹测试开发学社测试人社区20 分钟前
软件测试学习笔记丨Flask操作数据库-数据库和表的管理
软件测试·笔记·测试开发·学习·flask
今天我又学废了36 分钟前
Scala学习记录,List
学习
幸运超级加倍~40 分钟前
软件设计师-上午题-16 算法(4-5分)
笔记·算法
杨荧41 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。1 小时前
c++多线程
java·开发语言
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript