C语言里变量的生命周期

举报
DS小龙哥 发表于 2023/07/26 13:08:15 2023/07/26
【摘要】 在 C 语言中,变量的生命周期指的是该变量存在的时间段,理解变量的内存释放时机,设计程序才能少出问题。

在 C 语言中,变量的生命周期指的是该变量存在的时间段,理解变量的内存释放时机,设计程序才能少出问题。

在程序执行期间,变量会经历以下三个阶段:

(1)定义阶段(定义变量):在定义变量时,编译器会为该变量分配内存空间。此时变量的值是不确定的。

(2)使用阶段(赋值、读取变量):在程序执行过程中,可以对变量进行赋值或读取操作。此时变量的值是确定的,并且会随着程序执行的进度而变化。

(3)销毁阶段(变量被销毁):在变量的作用域结束时,该变量就会被销毁。在这个过程中,编译器会自动释放该变量所占用的内存空间。

根据变量的定义位置和作用域,C 语言中的变量可以分为以下两种类型:

(1)局部变量:定义在函数内部或代码块内部的变量称为局部变量。局部变量只能在其定义所在的函数或代码块内部使用,并且在函数或代码块结束时被销毁。局部变量的生命周期受限于其所处的函数或代码块的生命周期。

(2)全局变量:定义在函数外部或文件顶部的变量称为全局变量。全局变量可以在整个程序中使用,其生命周期从程序开始到程序结束。全局变量在程序运行期间一直存在,并且在程序结束时才被销毁。

除了上述两种变量类型之外,C 语言还提供了另外一种特殊的变量类型——静态变量。静态变量定义在函数内部或代码块内部,但其生命周期与局部变量不同。静态变量在函数或代码块结束时不会被销毁,而是继续存在于内存中,并保留其上一次赋值的值,直到下一次被修改。

在 C 语言中,变量的生命周期是由其作用域和定义位置决定的。正确地管理变量的生命周期对于程序的正确性和性能都至关重要,程序员需要深入了解变量的生命周期,遵循正确的使用规则,确保程序的正确性和健壮性。


以下是使用代码进行举例说明变量的生命周期:

(1)定义阶段

在定义变量时,编译器会为该变量分配内存空间。

例如,在函数内部定义一个整型变量 a,其定义语句如下:

void foo() {
    int a;  // 定义变量
}

此时变量 a 就被分配了内存空间,但其值是不确定的。

(2)使用阶段

在程序执行过程中,可以对变量进行赋值或读取操作。

例如,在上述定义变量的基础上,给变量 a 赋值并读取其值的代码如下:

void foo() {
    int a;  // 定义变量
​
    a = 10;  // 给变量赋值
    printf("a = %d\n", a);  // 打印变量的值
}

此时变量 a 的值已经确定为 10,并被输出到控制台。

(3)销毁阶段

在变量的作用域结束时,该变量就会被销毁。在这个过程中,编译器会自动释放该变量所占用的内存空间。例如,在上述定义变量和使用变量的代码基础上,添加一个条件语句使得变量 a 在条件成立之后被销毁,示例代码如下:

void foo() {
    int a;  // 定义变量
​
    a = 10;  // 给变量赋值
    printf("a = %d\n", a);  // 打印变量的值
​
    if (a > 5) {
        int b = 20;  // 定义变量
        printf("b = %d\n", b);  // 打印变量的值
    }
​
    printf("a = %d\n", a);  // 打印变量的值,此时变量依然存在
}

在上述代码中,当条件 a > 5 成立时,程序会在条件中定义并使用一个新的整型变量 b,但该变量在条件结束后就被释放了。而变量 a 的生命周期则受限于函数 foo() 的作用域,即在函数结束时被销毁。


(4)子函数返回地址(指针)

如果子函数返回指针变量,需要注意指针变量的生命周期问题,以避免指针失效和内存泄漏等问题。

假设有一个子函数 get_string(),该函数返回一个动态分配的字符串指针。函数定义及示例代码如下:

char* get_string() {
    char* str = (char*) malloc(10 * sizeof(char));
    str[0] = 'H';
    str[1] = 'e';
    str[2] = 'l';
    str[3] = 'l';
    str[4] = 'o';
    str[5] = '\0';
    return str;
}
​
int main() {
    char* s = get_string();
    printf("%s\n", s);  // 输出 "Hello"
​
    // 此处应该手动释放内存
    free(s);
​
    return 0;
}

在上述代码中,函数 get_string() 动态分配了一个长度为 10 的字符数组 str,并返回了该数组的首地址,该指针是在堆(heap)上分配的。由于是动态分配的内存空间,因此需要手动释放。在 main() 函数中对指针进行操作后,也需要手动释放该指针所指向的内存空间,以避免内存泄漏。

以下是一个错误的示例,用于和前面正确示例进行对比,帮助理解返回指针的生命周期问题:

char* get_string() {
    char str[] = "Hello";
    return str;
}
​
int main() {
    char* s = get_string();
    printf("%s\n", s);  // 输出 "Hello"
​
    return 0;
}

在这个示例中,函数 get_string() 返回了一个局部数组 str 的首地址。由于 str 是在函数内部定义的局部变量,其生命周期仅限于函数调用过程中。当函数 get_string() 执行完毕后,str 的生命周期已经结束,其内存空间已被回收,此时返回的指针变量 s 已经成为了野指针,指向了无效的内存空间,进而会导致未定义的行为。

尽管该函数定义的返回类型是 char*,但是由于返回了一个局部变量的指针,会导致指针失效、访问非法内存等问题,从而产生程序崩溃等错误行为。

总结:如果一个子函数需要返回指针变量,需要确保返回的指针指向的内存空间在使用期间有效,否则会导致严重的问题。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。