C语言八股——全局变量和局部变量

一、定义位置与作用域

全局变量

定义位置:在所有函数外部定义,例如文件开头或函数之间

作用域:从定义位置开始到整个源文件结束有效,可被同一文件内的所有函数访问。若需跨文件使用,需通过 extern 声明。

int globalVar = 10;  // 全局变量
void func() {
    printf("%d", globalVar);  // 可访问
}

局部变量

定义位置:在函数内部或复合语句(如{}代码块)内定义,包括形式参数。

作用域:仅限于定义它的函数或代码块内部,离开后无法访问。

void func() {
    int localVar = 20;  // 局部变量
    printf("%d", localVar);  // 仅在func()内有效
}

二、生命周期与存储位置

全局变量

生命周期:从程序启动时初始化,到程序结束时释放,贯穿整个运行周期

存储位置:位于静态存储区(数据段),内存由编译器静态分配。

初始化:若未显式初始化,默认值为0(整型)或NULL(指针)。

局部变量

生命周期:仅在函数调用时创建函数返回后销毁。

存储位置:位于栈区,内存动态分配且随函数调用自动回收。

初始化:未初始化时值为随机(取决于栈内存残留值),需手动赋值。

其实学过C逆向的,很容易理解。函数调用时,需要把各种寄存器入栈,而这些寄存器是决定了栈上的变量参数的。调用返回后,要把寄存器出栈。

三、使用特性

重名冲突处理

若局部变量与全局变量同名,函数内部优先操作局部变量,全局变量被暂时“隐藏”。

int x = 10;  // 全局变量
void func() {
    int x = 5;  // 局部变量
    printf("%d", x);  // 输出5,而非全局的10
}

因为函数是调用栈的,优先从栈区获取变量值。

代码维护性影响

全局变量:便于跨函数共享数据,但滥用可能导致耦合性高、调试困难。
局部变量:封装性好,函数独立性高,但需通过参数传递数据。
 

内存管理差异

全局变量占用固定内存,可能增加程序体积。

局部变量动态释放,内存利用率更高。

使用建议

 全局变量:慎用于需跨模块共享数据的场景,建议通过static限制作用域。

局部变量:优先使用以减少副作用,通过参数和返回值传递数据。

其他

全局变量,位于静态存储器(数据段)中,局部变量则位于栈区。

        初始化为0的全局变量通常会被分配到程序的BSS(Block Started by Symbol)段。BSS段是用于存放未初始化或初始化为0的全局变量和静态变量的一部分内存空间。在程序加载时,系统会自动将BSS段中的变量初始化为0。

        已经明确初始化为非零值的全局变量会被分配到程序的数据(Data)段。Data段用于存放已经初始化的全局变量和静态变量。

        总结来说,初始化为0的全局变量通常会被分配到BSS段,而已初始化为非零值的全局变量则会被分配到Data段。

四、值传递和指针传递

由于上面讲局部变量的时候,提到了函数。那么扩展值传递和指针传递的区别。其实就是栈区操作的区别了。

值传递

主调函数将实参的值复制到栈中(如push eax,假设eax存储参数值)。

被调函数通过[ebp+8]、[ebp+12]等偏移量访问参数的副本。

因为操作的是栈上的副本,修改形参是不影响实参的。

但是由于是在栈上,那么传递的值应为小型数据。

指针传递

主调函数将实参的地址压入栈中(如push &a)。

被调函数通过[ebp+8]获取地址,再通过解引用(如mov eax, [ebp+8])操作实际内存。

因为是获取地址后,对该地址进行操作,操作的就是实际的内存了。

会导致被调函数是能够直接修改主调函数中的变量的。

当然还有引用,其实和指针传递差不多。

五、指针函数和函数指针

因为上面扯到了指针和函数,那么就提一下让人头疼的这部分。

指针函数

定义:本质是一个函数,其返回值类型为指针(如int *func())。
作用:用于返回动态分配内存的地址、静态变量地址或通过参数传递的有效地址。

栈区操作:

若函数内部定义局部变量并返回其地址(如int *a = ...; return a;),

该地址指向栈帧中的局部变量。但由于函数返回后栈帧会被销毁,此时返回的地址成为野指针,访问会导致未定义行为。

安全做法是返回静态区(static变量)、全局区或堆区(malloc分配)的地址。

int *unsafeFunc() {
    int x = 10;     // 栈区局部变量
    return &x;      // 危险:返回栈地址
}
int* addIntegers(int a, int b) {
  int* result = (int*)malloc(sizeof(int));
  *result = a + b;
  return result;
}

后者的话,用malloc分配就安全了。 

函数指针

定义:本质是一个指针变量,指向函数的入口地址(如int (*ptr)(int, int))。

作用:通过指针间接调用函数,常用于回调函数、动态绑定等场景。

栈区操作:

函数指针变量(如int (*ptr)(int))存储在栈区,其值为目标函数的入口地址(代码区地址)。栈帧结构与普通函数调用一致。

int add(int a, int b) { return a + b; }
int main() {
    int (*ptr)(int, int) = add;  // 函数指针存储于栈区
    ptr(3, 4);                   // 参数压栈后跳转执行
}
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值