C++ 不可重入函数之static变量

本文通过示例分析了C++中static变量在多线程环境下的行为。当static变量在函数内部声明并初始化时,它只会在第一次调用时初始化,后续调用将保留之前的值。这可能导致在并发环境中出现预期之外的结果。同时,讨论了不同初始化方式下static变量的值如何被更新。

之前写过一篇文章:localtime的多线程问题_原十的博客-优快云博客_localtime 多线程工作中遇到的问题:设备需要上传图片到云平台,在上传失败时会进行重传,但在重传时却找不到对应的文件(文件命令方式为:日期_时间_id.jpg,如:2021-04-19_09_48_33_123456.jgp,文件名中的id是唯一的)。文件名中的日期-时间是从数据库里取出,然后再组成文件名字符串,再取文件进行上传。问题就是:id跟上传失败的文件id一样,但日期时间却不一样,本应该取上传失败的文件进行重传,结果文件名不一样导致找不到文件。原因:在从数据库中取得文件信息后,信息里有文件的时间戳,当用localthttps://blog.youkuaiyun.com/tianyexing2008/article/details/115857693?spm=1001.2014.3001.5502

今天来看下面这个例子:以下都是cpp代码

#include <stdio.h>
#include <stdlib.h>

int *func(int x);

int main()
{
    int *p1 = func(10);
    int *p2 = func(20);
    printf("p1 addr = %p, value = %d\n", p1, *p1);
    printf("p2 addr = %p, value = %d\n", p2, *p2);
    
    return 0;
}

int *func(int x)
{
    //c语言中如果要初始化则必须为常量
    static int value = x;
    return &value;
}

 结果输出是什么呢?

返回的地址是一样的,因为是static变量,存放在bss段,是同一个变量,所以地址是一样的。

那为什么value的值都是10呢?这里涉及到static变量初始化的问题,我们知道static变量的生命周期跟程序一样长,其只会初始化一次(网上搜索相关的也是这样描述)。我们实际用gdb跟踪查看:

单步调试进入函数查看,第二次调用func(20)时,value还是10,即static int value = x 这句 初始化只会执行一次,所以两次调用结果都是10。

那如果改成如下代码呢?

#include <stdio.h>
#include <stdlib.h>

int *func(int x);

int main()
{
    int *p1 = func(10);
    int *p2 = func(20);
    printf("p1 addr = %p, value = %d\n", p1, *p1);
    printf("p2 addr = %p, value = %d\n", p2, *p2);
    
    return 0;
}

int *func(int x)
{
    //c语言中如果要初始化则必须为常量
    static int value;
    value = x;
    return &value;
}

结果又是什么呢?

 结果是最后一次调用的值,这个很好理解,因为两次调用value都是同一个地址,后一次调用的值覆盖了前一次调用的值。

 

 

### C/C++ 函数内部 `static` 变量的作用和用法 #### 静态变量的特性 当在一个函数内声明了一个带有 `static` 关键字的变量时,该变量具有静态存储持续时间。这意味着此类型的变量仅会在程序启动时初始化一次,并在整个程序运行期间保持存在,直到程序结束为止[^3]。 #### 初始化行为 对于函数内的 `static` 变量来说,在首次执行到含有此类变量定义的位置之前不会发生任何操作;一旦到达这一点,则会按照指定的方式对其进行初始化处理——这通常发生在第一次调用包含这些变量函数之时。之后每次再次进入这个函数体内时就不会重复执行初始化过程了。 #### 生命周期管理 考虑下面的例子来说明这个问题: ```cpp #include <iostream> using namespace std; void countCalls() { static int callCount = 0; // 这里是一个静态局部整型数 ++callCount; cout << "This function has been called " << callCount << " times." << endl; } int main(){ for(int i=0;i<5;++i){ countCalls(); } } ``` 上述代码展示了如何利用 `static` 来创建一个计数器功能。每当 `countCalls()` 被调用的时候,其中的 `callCount` 就会被更新并打印出来。由于它是静态分配内存空间给定的名字对象,所以即使离开了作用范围也不会丢失数据,而是继续保存着上一次的状态等待下一轮访问到来。 #### 构造与析构时机 特别值得注意的是,如果静态变量本身也是一个类的对象的话,那么它的构造函数将在初次遇到该语句处被执行,而对应的析构函数则是在整个应用程序终止前才会被触发。例如: ```cpp class MyString { public: MyString(const char* str): data(str) {} ~MyString(){} private: string data; }; void foo() { static MyString plonk("When will I die?"); // ... other code ... } // 当foo() 第一次被调用时, MyString 对象 'plonk' 创建. // 应用程序退出时销毁 'plonk'. ``` 这种机制使得我们可以方便地实现一些需要持久化状态的功能模块而不必担心它们的数据会在不同次调用间消失不见。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值