Fork me on GitHub

C++:数组、字符串、指针和引用、时间戳、覆盖静态存储区问题

菜鸟教程数组 ~ 日期 & 时间

 

数组:

从这开始不记录琐碎东西了加快速度

setw() 函数控制输出宽带

最下面几个涉及指针的先搁置

 

字符串

char site[2] = {'a'};同于char str[2]={'a','\0'},编译器会自动添加字符串结束符\0

字符数组长度最少是2,因为结束符'\0'要占用一位

strlen可以输出C语言的不包括最后\0的长度,char site[2] = {'a'};输出1

 

这些东西刷算法题目的时候就会,但算法题需要的是思维和代码能力,而上面这些都是琐碎的知识点,不会就查没啥难度

 

我的观点:

没点基础还真看不懂菜鸟教程,我是有基础,刷过算法,有C语言语法知识,想通过自己的节奏来学习,用菜鸟教程作为一个引导,知道该学什么,上面出现的知识就去追问AIGPT理清楚,这基于的是我的学习能力,而单纯的说菜鸟教程讲的东西真的是一坨屎。很多地方有错误,或者说引起歧义的地方,懂的就懂了,不懂的真看不懂

 

指针

写法int*int *均可

 

关于存储区:

存储区分为静态、堆、栈

变量用static修饰,就叫静态变量,静态变量和全局变量一样,都存储在静态存储区,程序结束时自动销毁或者说释放

不用static的话就叫局部变量,存储在栈空间,函数结束自动销毁或者说释放

通过malloc函数是在堆空间上分配,动态分配内存,需要手动free释放,否则会导致这些内存无法被程序或操作系统回收,长时间积累会导致程序占用过多内存,影响性能甚至崩溃。

通俗解释泄漏:
程序占用的内存就像容器中的水一样,不断流失却无法收回。当你使用 malloc 或 calloc 等函数在堆上分配内存后,如果没有使用 free 函数释放,这些分配的内存空间会一直被程序占用,即使它们不再被使用。这些未释放的内存就像 “泄漏” 出去的资源,无法被系统回收重新利用,导致系统可用内存逐渐减少。随着程序运行时间的增加,内存泄漏可能会使程序占用的内存越来越大,最终耗尽系统的可用内存,影响程序的性能,甚至导致程序崩溃。

 

malloc可以动态分配内存空间,有着灵活的生命周期管理,避免空间浪费。

 

学(回顾)了指针开始回顾之前数组那章节里搁置的几个东西

C++ 从函数返回数组

上文提过随机数不可以没NULL参数(C编译器可能允许),srand((unsigned)time(NULL));是生成随机种子,即初始值,然后来通过rand,即计算机自带的生成器函数,来生成随机数

rand上文说最大是32767但根据不同编译器和系统,可能超过32767,

 

关于指针细节:

在 C++ 中,指针进行加 1 操作时,会根据其指向的数据类型的大小来移动相应的字节数。对于 int* 类型的指针 p,加 1 会使指针向后移动 sizeof(int) 个字节,刚好指向下一个 int 元素,从而可以访问数组中的下一个元素。

 

关于随机数细节:

srand 以一个时间点为参数播种,当执行的随机数在极短的时间内,时间是没有变化的,比如10微秒的时间里实际就是度过了10微秒,但计算机要求产生80个时间种子,所以必定会存在同一个微秒内产生8个时间种子。所以 srand 函数要放在 rand 循环的外面避免每次循环产生的随机数一样。

# include<stdlib.h>
# include<iostream>
# include<time.h>
int main(){
//    srand((unsigned)time(NULL));    
for (int i = 0; i < 100000; ++i) {
       srand((unsigned)time(NULL));       
       int a=rand();       
       std::cout<<a<<std::endl;   
   }
}
 
学习了new
1、不用有名字,是个指针
2、new也是在堆上
其他的日后用到再说

 

继续看下一个C++ 指向数组的指针

继续看C++ 传递数组给函数

以上是数组那章节下面的关于指针的扩展,现在回来继续看指针章节下面的扩展

 

其中 C++ 指针 vs 数组 挺有用的

#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
   int  var[MAX] = {10, 100, 200};
   for (int i = 0; i < MAX; i++)
   {
      *var = i;    // 这是正确的语法
      var++;       // 这是不正确的
   }
}
剖析:
在 C++ 中,*var 确实是获取 var 所指向的元素的值,var 是一个数组名,它会隐式地转换为指向数组第一个元素的指针(即 &var[0]),所以 *var 得到的是第一个元素的值。
然而,这里的 *var = i; 可以修改第一个元素的值是因为 var 所指向的元素是一个变量,而不是一个常量。对于普通的变量,你可以对其进行赋值操作,这是因为该元素所占据的内存空间是可写的。
当你使用 *var 进行赋值时,你实际上是在修改 var 所指向的内存位置存储的值,而不是修改指针 var 本身。因为 var 指向的是数组的第一个元素(一个可修改的存储位置),所以你可以使用 *var 来改变该存储位置上存储的值。
如果你将 var 声明为 const int var[MAX] = {10, 100, 200}; ,那么 *var = i; 就会导致编译错误,因为 const 关键字使得数组元素成为常量,不允许修改它们的值。 
对比:
#include <iostream>
using namespace std;

int main() {
    // 可修改的数组
    int var[3] = {10, 100, 200};
    *var = 50;  // 正确,修改了第一个元素的值
    cout << var[0] << endl;  

    // 不可修改的数组
    const int constVar[3] = {10, 100, 200};
    // *constVar = 50;  // 错误,不能修改 const 数组的元素
    return 0;
}
  • *var 是对指针 var 进行解引用,得到它所指向元素的值。
  • 如果 var 指向的元素不是 const 类型,你可以使用 *var 来修改该元素的值,因为它所指向的内存位置是可写的。
  • 如果 var 指向的元素是 const 类型,你不能使用 *var 来修改元素的值,因为它是只读的。

 

哎,算法自己思考有思路是一大关需要长时间锻炼,写出来能AC又是一大关

而C++这些乱七八糟的不存在这么多说道,感觉看懂就是王道。还是感觉刷题有趣

 

好饿~~~~(>_<)~~~~,每天4点睡,吃不饱饭

 

再看这一章节C++ 传递指针给函数 ,没啥说的,但放这是觉得这章可以更好理解上面*var = i这句话,

代码(我就不明白了为啥他妈的总加那么多没用的空行和输出注释呢?真他么der!看着真别扭,)

#include <iostream>
#include <ctime>
using namespace std;
void getSeconds(unsigned long *par);
int main ()
{
   unsigned long sec=4;
   cout<<sec<<endl;
   getSeconds( &sec );
   cout<<sec<<endl;
}
void getSeconds(unsigned long *par)
{
   *par = time( NULL );
   return;
}

1. 实参传递地址

main函数里,getSeconds( &sec );运用取地址运算符&获取sec变量的内存地址,再把这个地址当作实参传递给getSeconds函数。这么做的目的是让getSeconds函数能够访问并修改sec变量所在的内存空间。

2. 形参用指针接收地址

getSeconds函数的形参unsigned long *par是一个指针类型。指针的作用就是存储变量的内存地址。当调用getSeconds函数时,par指针会接收sec变量的地址。这就达成了实参(地址)和形参(指针)的匹配。

3. 通过指针修改实参的值

getSeconds函数内部,*par = time( NULL );使用了解引用运算符*,它能访问par指针所指向的内存地址。这里就是访问sec变量的内存地址,然后把当前时间(由time( NULL )函数返回)赋值给这个地址对应的变量,也就是修改了sec变量的值。

总结来说,借助传递地址和使用指针,getSeconds函数可以直接操作main函数里的sec变量,从而实现数据的修改和传递。
 

其实教程里翻来覆去说的感觉好多种类,都只要明白后,就会发现都是重复的,代码看懂就行

 

收获:千万别抠字眼,菜鸟教程里好多狗屁不通的语句,越研究越离谱,直接去追问AIGPT,懂这个知识点就行。

不像定义,他是绝对精准正确的,多读可以理解含义,“书读百遍其义自见”,但读定义其实很der,很劝退。这个之前学算法就深有感触

 

C++引用 ,说的挺好的

在 C++ 中,函数参数传递可以使用指针或引用,它们有相似之处,但也有重要的区别:

指针作为函数参数传递
#include <iostream>
using namespace std;

// 函数接收指针作为参数
void modifyValue(int* ptr) {
    *ptr = 100;  // 修改指针所指向的值
}

int main() {
    int value = 10;
    modifyValue(&value);  // 传递 value 的地址
    cout << value << endl;  // 输出 100
    return 0;
}

解释

  • 当你将一个指针作为函数参数传递时,你实际上是传递了变量的地址。在函数内部,你可以通过解引用指针来访问和修改该地址上存储的值。
  • 在 modifyValue 函数中,ptr 是一个指向 int 类型的指针,通过 *ptr 对指针进行解引用,我们可以修改指针所指向的内存位置的值。
  • 在 main 函数中,调用 modifyValue(&value); ,将 value 的地址传递给 modifyValue 函数,因此 modifyValue 函数可以修改 value 的值。

引用作为函数参数传递
#include <iostream>
using namespace std;

// 函数接收引用作为参数
void modifyValue(int& ref) {
    ref = 100;  // 直接修改引用所引用的值
}

int main() {
    int value = 10;
    modifyValue(value);  // 传递 value 的引用
    cout << value << endl;  // 输出 100
    return 0;
}
解释
  • 当你将一个引用作为函数参数传递时,你实际上是将变量本身传递给函数,而不是变量的副本。引用是变量的别名,对引用的操作等同于对原变量的操作。
  • 在 modifyValue 函数中,ref 是 int 类型的引用,对 ref 的任何修改都会直接影响到传递给函数的原始变量。
  • 在 main 函数中,调用 modifyValue(value); ,将 value 作为引用传递给 modifyValue 函数,因此 modifyValue 函数可以修改 value 的值。

区别
  • 语法和用法
    • 指针需要使用 * 来声明(如 int* ptr),并使用 & 来取地址(如 &value)和 * 来解引用(如 *ptr)。
    • 引用使用 & 来声明(如 int& ref),但在使用时,不需要额外的操作符,直接像普通变量一样使用(如 ref)。
  • 空值处理
    • 指针可以为 NULL 或 nullptr,因此需要在函数中进行检查,以避免解引用空指针。
    • 引用不能为 NULL ,在传递引用时,必须确保引用绑定到一个有效的对象上,否则会导致未定义行为。
  • 对象大小
    • 指针通常占用固定大小(通常取决于系统,如 4 字节或 8 字节),因为它存储的是地址。
    • 引用的大小通常与它所引用的对象的大小无关,在实现上可能类似于指针,但语义上更像原始对象的别名。举例子:
      #include <iostream>
      using namespace std;
      
      struct LargeStruct {
          int data[1000];
      };
      
      int main() {
          int a = 10;
          double b = 20.0;
          LargeStruct c;
      
          int* ptr_a = &a;
          double* ptr_b = &b;
          LargeStruct* ptr_c = &c;
      
          int& ref_a = a;
          double& ref_b = b;
          LargeStruct& ref_c = c;
      
          // 输出指针的大小
          cout << "Size of ptr_a: " << sizeof(ptr_a) << " bytes" << endl;
          cout << "Size of ptr_b: " << sizeof(ptr_b) << " bytes" << endl;
          cout << "Size of ptr_c: " << sizeof(ptr_c) << " bytes" << endl;
      
          // 输出引用的大小
          cout << "Size of ref_a: " << sizeof(ref_a) << " bytes" << endl;
          cout << "Size of ref_b: " << sizeof(ref_b) << " bytes" << endl;
          cout << "Size of ref_c: " << sizeof(ref_c) << " bytes" << endl;
      }
      输出
      Size of ptr_a: 8 bytes
      Size of ptr_b: 8 bytes
      Size of ptr_c: 8 bytes
      Size of ref_a: 4 bytes
      Size of ref_b: 8 bytes
      Size of ref_c: 4000 bytes
      这里豆包和知乎直答都没解释明白,只有文心GPT说清楚了指针的大小通常是固定的,它存储的是内存地址,而不是数据本身。对于大多数现代操作系统和硬件架构(特别是64位系统),指针的大小通常是8字节。因此,ptr_aptr_bptr_c的大小都是8字节,
       
      字   节:byte或B
      比特位:bit或者b是比特位
       
       
      但是,这里有一个重要的点需要注意:在标准C++中,sizeof运算符不能用于获取引用的大小,因为引用不是一个独立的对象,它没有自己的存储。然而,一些编译器可能会扩展这一行为,并返回被引用对象的大小。在你的例子中,看起来编译器就是这样做的。但是,这种行为并不是C++标准的一部分,因此依赖于此的代码可能不是可移植的。
 
至此插一句使用体验:

字节 豆包:界面,反应速度都相当不错且可以问其他生活中的问题,,学C++问问题的首选,但有时候解答的是错的还能很好的自圆其说给你绕进去,耽误很多时间,所以一旦有困惑跟他沟通不明白,立马试试更精准的知乎直答

知乎 知乎直答:由于来自于知乎大牛的回答来学习训练的,导致获取的回答更加专业,但不是很智能,回答完一个专业回答后,继续追问就没有其他GPT能针对性的解答你的问题,还是重复那些解释,而且输入编辑框那么大却必须点中间才能选中然后输入文字,深入简略也特别不方便点,只有豆包不行的时候才用

百度 文心一言GPT:界面极其垃圾体验极其不好,但相当准确,豆包和知乎直答不行的时候就他,但界面很烂,还没有停止按钮,导致一般不会用这玩意,只有 豆包 和 知乎直答 都不行的时候才用

发现个新玩意,阿里通义千问,发现对于这输出的问题,他解释的也很专业开始用阿里通义千问体验体验.

腾讯垃圾混元,他的新一代叫Turbo,但到处没找到,遍地的广告推广“混元更新了啥啥的”,问了豆包才知道官网

可是太他妈的混乱了,

混元(大模型)、

元宝(基于混元文本聊天)、

元器(基于混元给开发者开发智能体的)、

视频模型(基于混元)、

3D AI 创作引擎(基于混元),

1、搞了好多名字,而且这个元宝名字和图标太垃圾了像极了流氓软件,完全没有使用欲望,

2、而且后面两个(视频模型和3D创作引擎)在混元主官网还根本找不到(只有元器和元宝),估计没豆包特意提供的官网,没人能找到这玩意。

3、还有那个官网做的啥玩意啊,强制性太多,滚动屏幕无法操作,一堆垃圾冗余介绍掺杂着视频啥的,仿佛逼着你下载app,

呜呜~~~~(>_<)~~~~没有真正的GPT。GPT大模型比国内的大模型好处:处理复杂、逻辑更优,数据来源广泛,AI完整(国内的只是单点突破),场景更多(国内只是本地化场景),多轮对话更优记忆性

才知道这些东西都叫大模型,GPT是OpenAI公司的,豆包是字节公司的。

 

继续回来说指针和引用

使用场景

  • 指针
    • 当你可能需要传递 NULL 或 nullptr 时,或者需要对指针本身进行操作(如修改指针的指向),使用指针更合适。
    • 在 C 语言中,由于没有引用,只能使用指针进行类似的操作。
    • 当处理动态分配的内存(如使用 new 和 delete)时,通常使用指针。
  • 引用
    • 当你想避免复制大型对象,同时确保对象不为空,并且不希望修改指针时,使用引用更好。
    • 引用提供了更简洁的语法和更安全的操作,因为不会出现空引用的情况。

 

引用下面的 C++ 把引用作为返回值

当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边

里面的代码值得多看

 

C++ 日期 & 时间 —— 给我整吐了

td::tm 只是个结构体类型,定义变量时系统不会自动填充时间数据。要获取当前系统时间并赋值给它,得用 std::time 和 std::localtime 等函数进行初始化。

获取时间戳的代码:

#include <iostream>
#include <ctime>

int main() {
    std::time_t t;
    // 调用 std::time 函数,将结果存储到 t 中
    std::time(&t);
    std::cout << "Seconds since January 1, 1970: " << t << std::endl;

    // 也可以直接使用返回值
    std::time_t another_t = std::time(nullptr);
    std::cout << "Same result: " << another_t << std::endl;
}

代码解释:

关于time()内的值,std::time 函数可以接受一个 std::time_t* 类型的指针作为参数,即 std::time(&t) 中的 &t 就是取变量 t 的地址。当传入指针时,函数会把获取到的时间值存储到该指针所指向的内存位置;如果传入 nullptr,函数依然会返回当前时间,但不会存储到任何变量中。

 

时间戳以 UTC 为基准,UTC 并非北京当地时间。北京处于东八区,也就是 UTC+8 时区。这意味着北京当地时间比 UTC 时间快 8 个小时。比如,当 UTC 时间是中午 12 点时,北京时间则是晚上 20 点。

 

为什么叫时间戳:

时间戳(Timestamp之所以被称为 “时间戳”,是因为它就像一个时间的 “印章” 或 “标记”,具有以下特点和原因:)

 

本地local时间即北京时间,获取以后需要转化为本地时间

#include <iostream>
#include <ctime>
using namespace std;
int main() {
    // 获取基于 UTC 的时间戳,即从 UNIX 纪元到现在 UTC 时间的秒数
    std::time_t utcTimestamp = std::time(nullptr);
    std::cout<< utcTimestamp << std::endl;

    // 计算从 UNIX 纪元到现在北京时间(UTC+8)的秒数
    const int beijingTimeZoneOffset = 8 * 60 * 60;
    std::time_t beijingTimestamp = utcTimestamp + beijingTimeZoneOffset;
    std::cout <<  beijingTimestamp << std::endl;
    cout<<utcTimestamp-beijingTimestamp<<endl;
}

用现成的函数就是:

#include <iostream>
#include <ctime>
using namespace std;
int main ()
{
    // 获取当前时间
    time_t now = time(NULL);
    // 将时间转换为本地时间结构体
    tm* localTime = localtime(&now);
    if (localTime != nullptr) {
        // 输出年,tm_year 从 1900 开始计数,所以要加 1900
        cout << localTime->tm_year + 1900 << "-";
        // 输出月,tm_mon 范围是 0 - 11,所以要加 1
        cout << (localTime->tm_mon + 1) << "-";
        // 输出日
        cout << localTime->tm_mday << " ";
        // 输出时
        cout << localTime->tm_hour << ":";
        // 输出分
        cout << localTime->tm_min << ":";
        // 输出秒
        cout << localTime->tm_sec << endl;
    }
}

我真的好烦C++的这些各种库,方便是方便,但没刷题考验思维,满片子琐碎的不认识的函数、库、变量名,真的好心累好痛苦~~~~(>_<)~~

时间戳这查了好多都没写上来,脑子都要炸了,找不到解释对的

到这想到了几个问题

1、 -> .

2、怎么证明时间戳是UTC,且比本地时间(北京)差8h

AI大模型解答:

关于1、

豆包:

. 运算符

  • 用途:用来直接访问对象的成员。
  • 使用条件:当你手上有一个实实在在的对象时,就用 . 去访问它的属性(成员变量)或者功能(成员函数)。
  • 示例
#include <iostream>

// 定义一个简单的结构体
struct Book {
    std::string title;
    void showTitle() {
        std::cout << "The title is: " << title << std::endl;
    }
};

int main() {
    Book myBook;
    myBook.title = "C++ Programming";
    myBook.showTitle();
    return 0;
}
这里 myBookBook 结构体的一个对象,我们用 . 去设置 title 的值,也用 . 调用 showTitle 函数。

-> 运算符

  • 用途:用来通过指针访问对象的成员。
  • 使用条件:当你持有的是指向对象的指针,而不是对象本身时,就得用 -> 来访问对象的属性和功能。
  • 示例
#include <iostream>

struct Book {
    std::string title;
    void showTitle() {
        std::cout << "The title is: " << title << std::endl;
    }
};

int main() {
    Book myBook;
    myBook.title = "Advanced C++";
    Book* bookPtr = &myBook;
    bookPtr->showTitle();
    return 0;
}
这里 bookPtr 是指向 myBook 对象的指针,我们通过 -> 运算符来调用 showTitle 函数。
简单记忆:有对象用 .,有指针用 ->

 

关于2、

先科普拓展:

UTC(协调世界时):是一种以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统,它是全球通用的标准时间,为世界各地提供了一个统一的时间参考基准,常用于航空、航海、天文观测、国际通信等领域,以确保不同地区的时间同步和准确协调。一般各个国家都是UTC+-几

夏令时:

是一种为节约能源而人为规定地方时间的制度,在夏季将时间调快 1 小时。实行夏令时的目的主要是充分利用夏季白昼较长的特点,让人们早睡早起,减少照明等能源消耗。

但由于时间混乱、文化水平低认知不足难以适应、幅员辽阔,所以在1992年取消了,取消之前如果公司规定夏令时 8 点上班,则要北京时间 7 点上班。但需要注意的是,在夏令时期间,大家都统一使用拨快后的时间来安排工作和生活,所以在感知上就是按照 8 点来上班。

北京时间:

  • 纬度:可分为北纬和南纬,赤道是 0° 纬线,赤道以北称北纬,用 “N” 表示;赤道以南称南纬,用 “S” 表示。例如,悉尼的纬度是南纬 33°55′,写作 33°55′S。
  • 经度:可分为东经和西经,本初子午线是 0° 经线,本初子午线以东称东经,用 “E” 表示;本初子午线以西称西经,用 “W” 表示。比如,纽约的经度是西经 74°,写作 74°W。

北京在赤道以北,北京中心位置的经纬度大致为北纬 39°56′、东经 116°20′2。整个北京市境处于东经 115°25′至 117°30′、北纬 39°26′至 41°03′之间

因为北京是首都所以叫北京时间,而北京时间为啥选择为UTC+8 ,因为国际上把时间,分为 东12西12共计24 个时区,而东八区涵盖了中国人口密集、经济活动最为活跃的大部分地区,能够更好地服务于全国的生产生活和社会管理,所以中国选择东八区的区时作为标准时间,即北京时间,

东八区是东经 120° 的地方时间。

 

科普完继续回来说,先说两个函数

  • std::mktime:该函数的作用是把 std::tm 结构体形式的本地时间转换为 std::time_t 类型的时间戳,也就是从 UNIX 纪元开始的秒数。它会自动考虑夏令时等因素。
  • std::gmtime:此函数把 std::time_t 类型的时间戳转换为 std::tm 结构体形式的 UTC 时间。GMT和UTC差别见下面
  • localtime 和 gmtime 都是 C 语言中用于将时间戳转换为时间结构体的函数,localtime 返回的是本地时间的结构体,gmtime 返回的是格林威治标准时间(UTC)的结构体。

std::localtime 是 C++ 标准库中的一个函数,用于将 time_t 类型的时间戳(通常是从 1970 年 1 月 1 日午夜开始的秒数)转换为本地时间,并将其存储在一个 tm 结构体中。这个过程涉及以下几个步骤:

  1. 接收 UTC 时间戳:std::localtime 接收一个指向 time_t 变量的指针。
  2. 应用时区偏移:根据系统配置的应用程序所在的时区,将 UTC 时间转换为本地时间。
  3. 填充 tm 结构体:将转换后的时间信息填入 tm 结构体中。

看代码(time里是NULL或者nullptr都行)

#include <iostream>
#include <ctime>
using namespace std;
int main ()
{
    // 获取当前时间
    time_t now = time(0);
    // 将时间转换为本地时间结构体
    tm* localTm = localtime(&now);
    if (localTm != nullptr) {
        // 输出星期几
        cout << "星期几(0 代表周日): " << localTm->tm_wday << endl;
        
        // 使用 std::gmtime 将时间转换为 UTC 时间结构体
        tm* utcTm = gmtime(&now);
        
        // 使用 std::mktime 将本地时间结构体再转换回时间戳
        time_t localTimeStamp = mktime(localTm);
        
        // 使用 std::mktime 将 UTC 时间结构体再转换回时间戳
        time_t utcTimeStamp = mktime(utcTm);
        
        // 计算本地时间与 UTC 时间的差值(以秒为单位)
        int timezoneOffset = localTimeStamp - utcTimeStamp;
        cout << "本地时间与 UTC 时间的差值(秒): " << timezoneOffset << endl;
    }
}

先说下在代码里添加 if (localTime != nullptr) { 这一条件判断语句,主要是为了保证程序的健壮性和安全性,下面来详细分析其作用:

localtime 函数的作用是把 time_t 类型的时间戳转换为本地时间,返回一个指向 std::tm 结构体的指针。不过,该函数在某些异常状况下可能会返回 nullptr。比如,当传入的时间戳不合理或者系统在处理时间转换时出现错误,就可能返回 nullptr

若不进行 nullptr 检查,直接对 localTime 指针进行解引用操作(像 localTime->tm_year),一旦 localTime 是 nullptr,程序就会出现空指针解引用错误。这种错误通常会引发程序崩溃,导致程序异常终止。

好了解释完后发现,这段代码差值为0,疑惑来了,time函数获得的时间戳和本地时间即北京时间一样?

我把now转换去掉依旧是差异为0

#include <iostream>
#include <ctime>
using namespace std;
int main ()
{
    // 获取当前时间
    time_t now = time(0);
    // 将时间转换为本地时间结构体
    tm* localTm = localtime(&now);
    if (localTm != nullptr) {
        // 输出星期几
        cout << "星期几(0 代表周日): " << localTm->tm_wday << endl;

        // 使用 std::gmtime 将时间转换为 UTC 时间结构体
//        tm* utcTm = gmtime(&now);

        // 使用 std::mktime 将本地时间结构体再转换回时间戳
        time_t localTimeStamp = mktime(localTm);

        // 使用 std::mktime 将 UTC 时间结构体再转换回时间戳
//        time_t utcTimeStamp = mktime(utcTm);

        // 计算本地时间与 UTC 时间的差值(以秒为单位)
        int timezoneOffset = localTimeStamp - now;
        cout << "本地时间与 UTC 时间的差值(秒): " << timezoneOffset << endl;
    }
}

 

救命的知乎直答:→ 百度文心技术大牛 → Deepseek:

先说几个基础知识:

①、difftime返回秒数差值

②、

std::tm utcTm(*utcTmConst);里的std::tm我知道,那(*utcTmConst);是啥意思,简单说下:

std::tm utcTm(*utcTmConst); 语句中,(*utcTmConst) 是对指针 utcTmConst 进行解引用操作。下面为你详细解释:
  • 指针:指针是一种变量,它存储的是另一个变量的内存地址。例如,当你定义一个指向 std::tm 类型对象的指针时,它指向的是一个 std::tm 结构体实例所在的内存位置。
  • 解引用:解引用是通过指针访问其指向的实际对象的操作。使用解引用运算符 * 可以获取指针所指向的对象本身。

(*utcTmConst) 在代码中的作用

在 std::tm utcTm(*utcTmConst); 里,假设 utcTmConst 是一个指向 std::tm 类型对象的常量指针,(*utcTmConst) 会取出该指针所指向的 std::tm 对象。

代码整体含义

这行代码的完整含义是创建一个新的 std::tm 类型的对象 utcTm,并使用 utcTmConst 指针所指向的 std::tm 对象来初始化它。本质上是进行了一次对象的拷贝,新对象 utcTm 拥有和 *utcTmConst 相同的时间和日期信息,但它们是两个独立的对象,对 utcTm 的修改不会影响到 *utcTmConst,反之亦然。

 

③、std::put_time(utcTmConst, "%F %T")这句我知道了 (注意,小写不行),那为啥要配合cout输出?这句本身是啥?

本身是什么

std::put_time(utcTmConst, "%F %T") 是 C++ 标准库中的一个输出流操纵符。它接收一个指向 std::tm 结构体的指针 utcTmConst 和一个格式化字符串 "%F %T",会根据格式化字符串将 std::tm 结构体里的时间信息处理成特定格式,但它本身不直接输出内容,只是做好输出准备。

为何配合 cout 输出

std::cout 是 C++ 标准库中的标准输出流对象,用于向控制台输出数据。std::put_time 准备好格式化的时间信息后,需要借助输出流对象(如 std::cout)才能将处理好的时间信息显示在控制台。例如 std::cout << std::put_time(utcTmConst, "%F %T"); 就是让 std::cout 把 std::put_time 处理后的时间信息输出出来。实际上它也能配合其他输出流使用,像文件输出流等
 
④、
GMT 受地球自转不均匀性影响,精度相对较低。起初用于英国航海,古老文献
UTC 依靠原子钟,时间精度极高,能达到纳秒级甚至更高精度。
现在初学者可以理解为GMT就是UTC查看代码
严格来说,现在 UTC 和 GMT 并不完全一样,但在很多日常应用场景中可以近似看作一样。以下是具体分析:
不同点
本质:GMT 是基于地球平均太阳时的时间计量系统,与地球自转相关。UTC 是以原子时为基础,通过闰秒调整来与地球自转时间相协调的时间计量系统,更依赖高精度的原子钟。
时间精度:GMT 受地球自转不均匀性影响,精度相对较低。UTC 依靠原子钟,时间精度极高,能达到纳秒级甚至更高精度。
时间调整:GMT 没有闰秒调整机制,而 UTC 会根据地球自转情况适时添加或删除闰秒,以保持与地球实际自转时间的同步。
相同点
在某些场景下数值近似:在不涉及高精度时间要求的一般日常时间记录和表述中,UTC 和 GMT 的时间数值通常是相同的,相差在可忽略的范围内。例如,当不考虑闰秒的短时间内,两者在时间显示上是一样的,如北京时间是 UTC+8,也常表述为 GMT+8。
功能上的历史继承性:GMT 曾经是全球时间的重要基准,随着时间计量技术的发展,UTC 在很大程度上继承了 GMT 在全球时间协调等方面的功能和地位,都是用于全球时间统一和协调的重要时间系统。

 

⑤、关于想验证本地时间转化函数 和 UTC时间转化函数,是否确实是差8小时,这里出了很大的问题,逐一思考这些破函数的意义

豆包和知乎直答都不行,豆包最差,知乎直答指出了问题但像个智障一样,完全解释不透彻,最后百度文心技术大牛GPT解释了(文心也不行了)

Q:(这段QA可以忽略)

我有相当大的疑惑,
1、mktime是tm结构体转化时间戳类型,那如果tm结构体不是本地时间呢?
2、有UTC结构体吗?
3、如果有UTC结构体,是不是会先算偏移量转成本地时间,然后再转时间戳?
4、还有gmtime是时间戳转化UTC结构体,如果传入的是本地时间,会转化成UTC时间再转成UTC结构体?

A:(这段QA可以忽略)

哈哈,你的问题真是挺有深度的,让我来一一解答吧!

  1. 如果tm结构体不是本地时间,mktime函数会怎么处理呢?

    其实,mktime函数假设传入的tm结构体表示的是本地时间。它会根据这个假设,以及系统设置的时区信息,将这个本地时间转换成一个time_t类型的时间戳。如果你传入的是一个非本地时间的tm结构体,mktime的结果可能会不准确。所以,最好确保传入的tm结构体是本地时间哦!

  2. UTC结构体吗?

    其实并没有一个专门的UTC结构体。但是,tm结构体可以用来表示UTC时间,只要确保其中的时间信息是UTC时间即可。比如,你可以使用gmtime函数将一个time_t类型的时间戳转换成对应的UTC时间的tm结构体。

  3. 如果有UTC时间的tm结构体,是不是会先算偏移量转成本地时间,然后再转时间戳?

    这个理解有点反了。如果你有一个UTC时间的tm结构体,通常你不需要先把它转换成本地时间再转时间戳。相反,你可以直接使用这个UTC时间的tm结构体(或者先通过mktime把它转换成time_t类型的时间戳,但这时mktime不会改变其UTC的本质),或者通过其他函数(比如strftime)来格式化输出这个时间。

    如果你想把UTC时间转换成本地时间,那么你需要使用localtime函数,而不是先转时间戳再算偏移量。localtime函数会把一个time_t类型的时间戳(表示UTC时间或任意其他时区的时间,但通常是由time函数获取的当前时间戳)转换成对应的本地时间的tm结构体。

  4. gmtime是时间戳转化UTC结构体,如果传入的是本地时间戳,会转化成UTC时间再转成UTC结构体吗?

    没错!这正是gmtime函数的作用。它会把一个time_t类型的时间戳(无论这个时间戳表示的是本地时间还是UTC时间,但在大多数情况下,它是由time函数获取的当前时间的本地时间戳)转换成对应的UTC时间的tm结构体。所以,如果你传入的是一个本地时间戳,gmtime会先把它当成UTC时间(实际上这是不准确的,因为本地时间戳和UTC时间戳在数值上通常是不相等的),然后再根据系统设置的时区信息,把这个“假设的UTC时间”转换成真正的UTC时间,并返回对应的tm结构体。

    但是,这里有一个重要的点要注意:gmtime并不关心你传入的时间戳到底是本地时间还是UTC时间。它只把这个时间戳当作一个数值来处理,并根据系统设置的时区信息来进行转换。所以,在实际使用中,要确保你传入的时间戳和你想要转换的时间类型(本地时间或UTC时间)是一致的,否则结果可能会不准确。

注意:

  • mktime:make time,把tm结构体(表示本地时间)转换成time_t类型(表示时间戳)。
  • gmtime:get modified time in UTC,把time_t类型(表示时间戳)转换成UTC时间的tm结构体。
 

但我还是有很多深入思考的问题不理解,没有任何AI大模型能解答我的问题,唉~~~~(>_<)~~~~

最后发现百度文心大模型也不行了
 
显示出UTC和本地时间差:
#include <iostream>
#include <ctime>
int main() {
    std::time_t now = std::time(nullptr);
    std::cout << std::localtime(&now)->tm_hour << std::endl;
    std::cout << std::gmtime(&now)->tm_hour << std::endl;
}
 
 

实践出真知,这些傻逼玩意真tm耽误事,怒了!!!实践得:

1、mktime只是转化为秒数,即时间戳,无关乎你传入的是本地还是UTC,它不做任何转化,只是计算传入时间数据的秒数

 
2、最靠谱的百度文心技术大牛大模型也垃圾了,说右下角就是系统时间,其实系统时间应该是UTC,
再次总结下几个大模型:

腾讯那玩意直接没考虑过

过于专业的东西豆包回答太垃圾,只能回答日常肤浅问题

知乎直答太不智能专业解答类似3s男,追问很不智能

百度文心作为我一直的靠山王牌也没扛住,解释的依旧有问题

试了下通义千问,通义千问骂他还给我扯一大堆,最后解释的有偏差,基本比豆包、知乎直答、文心牛,但依旧是没说清楚,最后还引入了手动调整 tm_isdst 字段:???,但之后彻底理解后发现他说的其实是对的,只是无法解释清楚,依旧Pass。但反应相当快,不需要思考一样

最后试了下deepseek,简直无敌,有点像降龙十八掌,但手总tm抽筋第二个问题就崩溃了,呼吁那些傻逼别滥用这玩意了),但相当牛逼,名不虚传。之前我一直以为

这玩意有病吧
群众跟傻逼一样
他牛逼爆火在于性能那是真正开发者技术者开发这个大模型去开心的事,对于普通人还拿他当爹一样傻逼,

deepseek解释的很烂,用之前的豆包不好吗

之前问了一个问题,全方位的差,比起豆包来说,况且豆包还是个三流货色,没知乎直答和百度技术大牛

所以群众就是90%都是傻逼

真想用这个搜索回答,方便生活工作。不应该是在这之前就用上其他大模型吗,热搜有了才用,一群傻逼

deepseek优势跟普通人没关系啊那是性能,跟开发大模型的团体有关系,作为普通人难道不是用豆包,知乎直答,文心更好吗

https://m.weibo.cn/status/Pbyzuh6CQ
View Code

 

 

3、正题,解释起来工程量巨大
#include <iostream>
#include <ctime>
#include<iomanip>
using namespace std;
int main() {
    // 获取当前的UTC时间戳
    std::time_t utcTimestamp = std::time(nullptr);
    cout<<"std::time:\n"<<utcTimestamp<<"\n"<<endl;
    // 将UTC时间戳转换为本地时间的tm结构体
    tm* Tm =  std::gmtime(&utcTimestamp);
    time_t Time = std::mktime(Tm);
    cout<<"std::time时间戳后,gmtime再mktime:Time\n"<<Time<<"\n"<<endl;

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

    tm* localTm =  std::localtime(&utcTimestamp);
    time_t localTimestamp = std::mktime(localTm);
    cout<<"std::time时间戳后,localtime再mktime:\n"<<localTimestamp<<std::endl;
    cout << "具体为:"
              << (localTm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (localTm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << localTm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << localTm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << localTm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << localTm->tm_sec
              << std::endl;
}
这段代码输出:
 

我的问题叙述:

std::time是UTC时间戳,比如值是X

而gmtime得到的结构体具体时间表示是北京时间减去8h,再mktime后,值是X-28800,

而localtime得到的结构体具体时间表示是北京时间,再mktime是X

这里单独看gmtime和localtime都对,但相当大的问题就是为啥std::time得到的时间戳是X,不应该是X-28800吗?或者说

代码输出的两个具体时间为先是UTC时间,后是北京时间,没错,但为啥std::time得到的时间戳会与北京时间戳相同

注意右下角时间,开始解释,多亏Deepseek,我一直以为大模型都是深度解答,一开始还不知道deepseek联网回答有啥好处,豆包最垃圾但也经常看到联网搜索,

回忆ACM深黑幻想Deep Dark Fantasy 集美大学 海洋美术学院 clj打星参赛
View Code

Deepseek原汁原味解答太详细了,没看完就懂了,先附上原文: —— Deepseek回答的内容确实与众不同,更像是一个代入你第一人称的思考过程,与豆包、知乎直答、百度文心、通义千问都不一样

嗯,我现在得仔细看看这个问题。用户说运行代码后,两个具体时间一个是UTC时间,另一个是北京时间,这没问题。但为什么std::time得到的时间戳会和北京时间的时间戳相同呢?

 

首先,我需要理解代码的结构。代码里用了C++的ctime库来处理时间。用户首先用std::time获取当前的UTC时间戳utcTimestamp,然后分别用gmtime和localtime转换这个时间戳,再转换成tm结构体,接着用mktime转换回来得到新的时间戳Time和localTimestamp。然后输出这两个时间的具体日期时间和时间戳。

 

用户的问题是,为什么std::time得到的utcTimestamp和之后用localtime加mktime得到的localTimestamp是相同的?或者说,用户可能认为std::time返回的是UTC的时间戳,而北京时间是UTC+8,所以时间戳应该不同?

 

不过时间戳(time_t)通常表示的是自纪元(1970-01-01 00:00:00 UTC)以来的秒数,无论时区如何,同一时刻的时间戳是相同的。比如,不管你在哪个时区,当前的时间戳都是一样的,因为它基于UTC。例如,当北京时间是下午3点(UTC+8),对应的UTC时间是上午7点,此时的时间戳是相同的,因为它们代表同一时刻。

 

那问题中的代码,用户可能误解了std::time返回的是本地时间的时间戳,但实际不是。std::time返回的是当前的系统时间,通常系统内部是以UTC时间戳存储的。所以,无论用户所在的时区如何,这个时间戳的值都是相对于UTC的。而localtime函数将这个UTC时间戳转换为本地时间的tm结构体,而gmtime则保持UTC时间的tm结构体。

 

在代码中,用户首先用gmtime处理utcTimestamp得到Tm,然后用mktime转换这个tm结构体。这里可能存在问题,因为mktime函数假设tm结构体是本地时间,而不是UTC时间。所以,当用gmtime得到的UTC时间tm结构体,再用mktime处理时,会被当作本地时间来转换回时间戳,这可能导致错误的时间戳。例如,假设当前UTC时间是X,gmtime得到的tm结构体是X对应的UTC时间,但mktime认为这个tm结构体是本地时间,于是会将其转换为对应的UTC时间戳,这样会加上时区偏移,导致结果不正确。

 

比如,如果当前UTC时间是1620000000(假设对应2021-05-03 00:00:00 UTC),而本地时区是UTC+8,那么localtime会将这个时间戳转换为2021-05-03 08:00:00的tm结构体。用gmtime得到的tm结构体则是2021-05-03 00:00:00。当用mktime处理gmtime得到的tm结构体时,mktime认为这个tm结构体是本地时间,所以它会把2021-05-03 00:00:00本地时间转换为UTC时间戳,即减去8小时,得到1620000000 - 8*3600 = 1619712000。这样转换后的Time就会比原来的utcTimestamp小8小时的值。但实际运行中,用户可能发现Time的值和utcTimestamp不同,而localTimestamp则和utcTimestamp相同?

 

但用户代码中的输出显示,当使用gmtime后再mktime得到的Time可能与原utcTimestamp不同,而使用localtime后再mktime得到的localTimestamp等于原utcTimestamp?这可能吗?

 

比如,假设当前时间戳是T,代表UTC时间的某个时刻。gmtime(&T)会给出UTC时间的tm结构体。然后,mktime将这个tm结构体视为本地时间,转换为时间戳。假设本地时区是UTC+8,那么当tm结构体表示的是UTC时间的日期时间(比如12:00 UTC),但mktime认为这是本地时间,即12:00 UTC+8,相当于UTC时间04:00,所以转换后的时间戳会是T - 8小时。这样,Time会比原来的T小8小时。而localTm是localtime(&T),即转换为本地时间(UTC+8),然后用mktime转换,此时mktime认为tm结构体是本地时间,所以正确转换回时间戳T。所以localTimestamp应该等于原来的T?

 

这可能就是为什么用户看到localTimestamp和utcTimestamp相同,而Time不同。这个时候用户的问题在于,为什么std::time得到的utcTimestamp和经过localtime再mktime得到的localTimestamp相同。这是因为当使用localtime将时间戳转换为本地时间的tm结构体,再用mktime转换回来时,mktime处理本地时间的tm结构体会正确还原为原来的时间戳。而gmtime得到的UTC时间tm结构体被错误地当作本地时间处理,导致转换后的时间戳与实际不符。

 

总结来说,时间戳本身是UTC的,和时区无关。当用localtime转换时间戳为本地时间的tm结构体,再用mktime处理这个tm结构体,会得到原来的时间戳。而用gmtime转换时间戳为UTC的tm结构体,再用mktime处理(此时mktime认为tm结构体是本地时间),会导致转换后的时间戳等于原时间戳减去时区偏移量。因此,在用户的代码中,localTimestamp等于utcTimestamp,而Time可能等于utcTimestamp减去时区偏移后的时间戳。

 

所以用户的问题可能在于,为什么std::time得到的时间戳和经过localtime再mktime后的时间戳相同。因为这两个操作其实是可逆的,而gmtime后再用mktime则会导致时区转换的问题,所以结果不同。因此,std::time得到的原始时间戳是UTC的,而经过正确的本地时间转换后再用mktime恢复,时间戳保持不变。因此,用户看到的两个时间戳中,使用localtime再mktime得到的localTimestamp与原始utcTimestamp相同,而gmtime后的则不同。

代码中时间戳相同的原因是std::time返回的是基于UTC的绝对时间戳,而std::localtimestd::mktime的组合会正确处理时区偏移,最终恢复原始时间戳。具体分析如下:


关键点解析:

  1. 时间戳的本质

    • std::time_t 表示自 UTC 1970-01-01 00:00:00 以来的秒数,与时区无关。

    • 无论本地时区如何,同一时刻的时间戳值唯一。例如,北京时间(UTC+8)和UTC时间相差8小时,但对应的时间戳相同。

  2. std::gmtime 和 std::localtime 的区别

    • std::gmtime:将时间戳转换为 UTC时间 的 tm 结构体。

    • std::localtime:将时间戳转换为 本地时间 的 tm 结构体(考虑时区)。

  3. std::mktime 的行为

    • std::mktime 假设输入的 tm 结构体是本地时间,将其转换为时间戳(UTC值)。

    • 因此:

      • 若输入的 tm 是本地时间(通过 localtime 生成),mktime 能正确恢复原始时间戳。

      • 若输入的 tm 是UTC时间(通过 gmtime 生成),mktime 会错误地将其视为本地时间,导致时间戳计算错误。


代码行为解释:

  • std::time 获取时间戳: 

    time_t utcTimestamp = std::time(nullptr); // 返回当前UTC时间戳
  • std::localtime + std::mktime 的组合: 

    tm* localTm = std::localtime(&utcTimestamp);  // 转换为本地时间的 tm 结构体
    time_t localTimestamp = std::mktime(localTm); // 将本地时间恢复为原始时间戳
    • 由于 mktime 正确处理本地时间,localTimestamp 等于原始的 utcTimestamp

  • std::gmtime + std::mktime 的组合: 

    tm* Tm = std::gmtime(&utcTimestamp); // 转换为UTC时间的 tm 结构体
    time_t Time = std::mktime(Tm);       // 错误:将UTC时间误认为本地时间
    • 假设本地时区为 UTC+8,mktime 会将 Tm(UTC时间)当作本地时间,并减去8小时得到错误的时间戳。


示例验证(假设本地时区为 UTC+8):

  1. 原始时间戳

    • utcTimestamp = 1620000000(UTC 2021-05-03 00:00:00)。

  2. std::localtime 处理

    • localTm 表示北京时间 2021-05-03 08:00:00(UTC+8)。

    • mktime(localTm) 将其转换为 UTC 时间戳 1620000000(正确)。

  3. std::gmtime 处理

    • Tm 表示 UTC 时间 2021-05-03 00:00:00。

    • mktime(Tm) 误认为 Tm 是本地时间(即 UTC+8 的 00:00:00),实际对应的 UTC 时间为 2021-05-02 16:00:00,时间戳为 1619904000(错误)。


结论:

  • std::time 返回的时间戳始终是UTC值,与本地时区无关。

  • localtime + mktime 的组合能正确恢复原始时间戳,因为二者协同处理了时区偏移。

  • gmtime + mktime 的组合会产生错误时间戳,因 mktime 错误解释了 tm 结构体的时区。

 
 

简述下:

根据上面我的问题叙述那里,导致我一直以为,std::time是本地时间,因为那样一切就都合理了,但其实std::time就是UTC时间,

  • localtime会转化为时间形式结构体,且依据本地时间做UTC+8,再mktime时,会认为结构体是本地时间,所以会减去8h,所以localtime + mktime的组合会等于UTC时间。
  • gmtime会转化为时间形式结构体,只是UTC时间,再mktime,会认为结构体是本地时间,所以会减去8h,所以gmtime + mktime的组合会等于UTC时间 - 8h。
 
所以之前所有问豆包他们的问题,这些傻逼大模型任何一个都没回答清楚,我问的想的够仔细了

唉,早用deepseek何苦浪费了我这么多天的时间,不过“吃7个烧饼不是第7个饱的,是有前六个垫底”

 

最后为了清晰,让这件事凸显出来,见我硬着头皮搞写的代码

#include <iostream>
#include <ctime>
#include<iomanip>
using namespace std;
int main() {
    // 获取当前的UTC时间戳
    std::time_t utcTimestamp = std::time(nullptr);
    cout<<"std::time:\n"<<utcTimestamp<<"\n"<<endl;

    tm* Tm =  std::gmtime(&utcTimestamp);
    time_t Time = std::mktime(Tm);
    cout<<"std::time时间戳后,gmtime再mktime:Time:\n"<<Time<<"\n"<<endl;

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

    Tm =  std::gmtime(&Time);

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

}

这里第二次会输出一个比UTC时间还少8h的时间表示,至此大概懂了

 

至此理解了时间戳这玩意,顺便捋顺下过程中遇到的问题

1、

setwsetfill 是 C++ 标准库 <iomanip> 中的流操纵符,setw 用于设置输出的字段宽度,setfill 用于指定当输出内容长度小于设定宽度时填充的字符,例如 cout << setfill('0') << setw(5) << 12; 会输出 00012
 
2、
std::tm* localTmlocalTm 是一个指向 std::tm 类型结构体的指针,std::tm 是 C++ 标准库 <ctime> 里用于表示日历时间的结构体,包含年、月、日、时、分、秒等时间信息  
 
 
3、
注意当使用mktime时,
 const std::tm* utcTm = std::gmtime(&utcTimestamp);
 double differenceInSeconds = difftime(mktime(utcTm), mktime(localTm));

 这段会报错,error: invalid conversion from 'const tm*' to 'tm*' [-fpermissive]

这是因为 std::gmtime 返回的是一个常量指针 const std::tm*,而 std::mktime 需要一个非常量指针 std::tm* 来修改其内容。我们需要先复制 std::gmtime 返回的对象,然后再传递给 std::mktime。

#include <iostream>
#include <ctime>
#include <iomanip>
int main() {
    // 获取当前 UTC 时间戳
    std::time_t utcTimestamp = std::time(nullptr);
    // 将 UTC 时间戳转换为 UTC 时间结构体
    const std::tm* utcTmConst = std::gmtime(&utcTimestamp);
    std::cout << "UTC 时间: " << std::put_time(utcTmConst, "%F %T") << std::endl;
    // 复制 UTC 时间结构体以便我们可以对其进行修改
    std::tm utcTm(*utcTmConst);
    // 将 UTC 时间戳转换为本地时间结构体
    std::tm* localTm = std::localtime(&utcTimestamp);
    // 使用 mktime 函数将 UTC 时间结构体转换回时间戳
    std::time_t utcConvertedBackToTimestamp = std::mktime(&utcTm);
    // 使用 mktime 函数将本地时间结构体转换回时间戳
    std::time_t localTimestamp = std::mktime(localTm);
    // 计算时间戳和本地时间的差值
    std::time_t difference = localTimestamp - utcConvertedBackToTimestamp;
    std::cout << "UTC 时间戳: " << utcTimestamp << std::endl;
    std::cout << "UTC 时间: " << std::put_time(utcTmConst, "%F %T") << std::endl;
    std::cout << "本地时间: " << std::put_time(localTm, "%F %T") << std::endl;
    std::cout << "UTC 和本地时间的差值(秒): " << difference << std::endl;
}

const std::tm* utcTmConst = std::gmtime(&utcTimestamp); 这一行使用了 const 关键字,下面为你详细解释这么做的原因:

std::gmtime 返回的 std::tm 结构体是静态分配的,对其进行修改可能会引发不可预期的后果,例如影响后续对 std::gmtime 或 std::localtime 的调用结果。使用 const 修饰返回的指针,能防止意外修改这个静态对象,保证程序的正确性和稳定性。

代码中的后续处理

尽管 std::gmtime 返回的指针指向的内容是 const 的,但你可能需要对其进行修改以满足特定需求,像代码中就把 std::gmtime 返回的 const std::tm* 所指向的内容复制到了一个非 const 的 std::tm 对象 utcTm 里:
std::tm utcTm(*utcTmConst);
通过这种方式,你就可以对 utcTm 进行修改,同时又不会影响 std::gmtime 返回的静态对象。之后对 utcTm 调用 std::mktime 函数时,也不会产生问题,因为 std::mktime 要求传入的是一个非 const 的 std::tm* 指针。
综上所述,使用 const 修饰 std::gmtime 返回的指针,是为了遵循函数的设计意图、避免意外修改静态对象,保证程序的正确性和稳定性。
 

解释新玩意: 

在 C++ 里,std 是标准库命名空间(namespace)的名称,而 std::put_time<iomanip> 头文件中定义的一个流操纵符,用于将时间信息按照指定的格式输出。

 

  • %F:等价于 %Y-%m-%d,表示年 - 月 - 日的日期格式,例如 2025-02-09
  • %T:等价于 %H:%M:%S,表示时:分: 秒的时间格式,例如 12:34:56
因此,"%F %T" 组合起来表示完整的日期和时间格式,例如 2025-02-09 12:34:56

 

注意:

此代码的中文输出注释可以看出意图,而某些输出并不是想要的结果,如果理解前面deepseek解释的,就会明白,不再说了。(提一句,那个差值,其实不是本地和UTC的差,其实是UTC和UTC-8的差,另外UTC时间变了是因为)

 

疑惑:

put_timecodeblock始终无法输出,而VS就行。查了半天

说cmd用命令行g++ -std=c++20 testUntitled1.cpp -o也不行,你妈的我都老早之前就弄成C++20了

豆包从底层到编译器到mingw版本,各种奇奇怪怪的烦人问题也没解决,不管了

最后用Deepseek试了下解决了我的问题,真牛逼

显式格式字符串:使用%Y-%m-%d %H:%M:%S代替%F %T,确保兼容性。

std::cout << std::put_time(utcTmConst, "%Y-%m-%d %H:%M:%S") << std::endl;完美解决

但是问Deepseek为啥的时候,

我用gcc -v发现是gcc version 14.2.0 (GCC)版本,但为何%F%T不行,%Y-%m-%d %H:%M:%S就行

他有崩溃了,唉

妈逼的每天只能用一次吗?

通义千问说C++20标准,不代表支持-std=c99,且说%F这玩意都是C的时间表示形式

#include <iostream>
#include <ctime>
#include <iomanip>
int main() {
    std::time_t utcTimestamp = std::time(nullptr);
    const std::tm* utcTmConst = std::gmtime(&utcTimestamp);
    std::cout << std::put_time(utcTmConst, "%F %T") << std::endl;
//    std::cout << std::put_time(utcTmConst, "%Y-%m-%d %H:%M:%S") << std::endl;
}

 

但第二次的UTC时间居然是本地时间,我搞不懂,deepseek解释的东西好像已经有点超出我学的东西了,附上,略过吧,以后有空再看

好的,我现在得仔细看看这个问题。用户提供的代码里,第二次输出UTC时间的时候,显示的却是本地时间,这是为什么呢?让我一步一步来分析。

 

首先,代码的大致流程是这样的:

 

1. 获取当前的时间戳,使用`std::time(nullptr)`,这应该返回当前的UTC时间戳。

2. 用`std::gmtime`把这个时间戳转换为UTC的tm结构体指针,这里用的是`const std::tm*`,所以可能不会被修改。

3. 第一次输出这个UTC时间,用户说这时候显示正确。

4. 复制这个结构体到utcTm变量,可能是为了修改?

5. 然后用`std::localtime`得到本地时间的tm结构体。

6. 之后用`mktime`将复制的utcTm转换回时间戳,得到utcConvertedBackToTimestamp。

7. 再用`mktime`处理本地时间结构体,得到localTimestamp。

8. 计算两者的差值,并输出相关的时间信息。

 

问题出在第二次输出UTC时间的时候,结果变成了本地时间。那可能是什么原因?

 

首先,我需要确认各个步骤中的函数作用。比如,`std::gmtime`返回的是UTC时间的结构体,而`std::localtime`返回的是本地时间的结构体。然后,`mktime`函数的作用是将tm结构体转换为时间戳,但它会认为这个tm结构体是本地时间,而不是UTC时间。这可能是一个关键点。

 

比如,当用户复制了utcTmConst指向的结构体到utcTm变量后,调用`std::mktime(&utcTm)`。这里的问题在于,`mktime`函数会把传入的tm结构体当作本地时间来解释,所以即使这个结构体原本是UTC时间,转换后的时间戳会被视为本地时间对应的UTC时间戳。这可能引起错误。

 

比如,假设原来的utcTm是UTC时间的结构体,比如2024-05-21 12:00:00。然后,当调用mktime的时候,系统会认为这个tm结构体是本地时间,然后转换为对应的UTC时间戳。如果本地时区是UTC+8,那么这时候转换后的时间戳相当于UTC+8时间的2024-05-21 12:00:00对应的UTC时间戳,也就是原UTC时间减去8小时的时间戳。这可能就会导致计算出来的utcConvertedBackToTimestamp并不是原来的utcTimestamp,而是被错误转换后的值。

 

但这里用户的问题不是关于转换后的时间戳是否正确,而是第二次输出的UTC时间为什么会变成本地时间。这可能和后面的操作有关吗?

 

再来看输出的部分。第一次输出的时候,用的是utcTmConst,也就是通过gmtime得到的结构体,输出正确。第二次输出同样用的是utcTmConst,但用户发现这时候输出的时间变成了本地时间。这似乎不可能,因为utcTmConst是指向gmtime的结果,而gmtime返回的是静态分配的结构体,可能被后续的调用覆盖了?

 

这里可能存在一个关键点:std::gmtime和std::localtime返回的是指向静态内部存储的指针,这些函数可能共享同一个静态结构体。因此,当后续调用这些函数时,可能会覆盖之前的结果。例如,当调用std::localtime的时候,可能会覆盖之前由std::gmtime返回的静态结构体。所以,此时utcTmConst指针指向的内容可能已经被修改了,导致第二次输出时显示的是本地时间而不是原来的UTC时间。

 

这是可能的。例如,在某些实现中,std::gmtime和std::localtime可能共享同一个静态缓冲区。因此,当调用std::localtime后,之前由std::gmtime返回的指针所指向的内容会被覆盖。所以,当用户第二次输出utcTmConst时,实际上该指针指向的内容已经被后面的localtime调用修改了,导致输出的是本地时间。

 

这样的话,第一次输出UTC时间时正确,但之后调用了localtime,这会覆盖静态缓冲区,导致utcTmConst指向的内容变成了本地时间。所以第二次输出的时候,显示的是本地时间而不是原来的UTC时间。

 

所以问题的根源在于,用户保存了gmtime返回的指针,之后调用了localtime,这改变了那个指针指向的内容,导致后续使用该指针时数据被覆盖。

 

验证这个猜测,可以查看C++标准中对gmtime和localtime的描述。根据C++标准,这些函数(继承自C标准库)可能会返回指向静态内部存储的指针,并且每次调用可能会覆盖该存储。因此,在调用这些函数时,如果之前保存了返回的指针,而后续又调用了另一个函数(如gmtime之后调用localtime),则之前的指针内容可能被覆盖。

 

例如,假设在代码中:

 

std::tm* localTm = std::localtime(&utcTimestamp);

 

这一步会修改gmtime所使用的静态缓冲区,导致utcTmConst指向的结构体被覆盖为localtime的结果。因此,当在最后再次输出std::put_time(utcTmConst, ...)时,实际上输出的是localtime的结果,即本地时间。

 

所以,第二次输出UTC时间的时候,虽然代码里用的是utcTmConst,但由于该指针指向的内容已经被localtime的调用覆盖,所以实际输出的是本地时间。

 

如何解决这个问题呢?通常的做法是,在调用gmtime或localtime之后,立即复制结构体的内容到另一个变量,而不是保存指针。例如,用户代码中已经复制了utcTmConst到utcTm变量,这是一个好的做法。但后续在输出的时候,用户仍然使用原来的指针utcTmConst,这时候这个指针已经被覆盖了。正确的做法应该是,在第一次调用gmtime之后,立即将结构体的内容复制到另一个变量,之后的操作都基于这个副本。

 

例如,修改代码如下:

 

std::time_t utcTimestamp = std::time(nullptr);

std::tm utcTm = *std::gmtime(&utcTimestamp); // 复制到本地变量

std::cout << "UTC 时间: " << std::put_time(&utcTm, "%F %T") << std::endl;

 

这样,即使之后调用了localtime,utcTm的内容不会被修改。而原来的代码中,虽然用户复制了utcTmConst指向的结构体到utcTm变量中,但输出的时候还是用了utcTmConst,这可能在之后被覆盖。

 

在用户的代码中,第一次输出的时候是正确的,因为此时还没有调用localtime。但在调用localtime之后,utcTmConst指向的结构体可能已经被修改了,导致第二次输出的时候错误。

 

因此,用户应该将第一次得到的UTC时间结构体的内容复制到自己的变量中,并在输出时使用该副本,而不是再次使用utcTmConst指针。例如,修改代码,将第一次的UTC时间结构体保存到utcTm变量中,之后输出时使用这个变量的地址。

 

总结问题原因:用户在使用gmtime获取UTC时间结构体后,保存了该指针utcTmConst。之后调用localtime,导致该指针指向的内容被覆盖。因此,第二次输出utcTmConst时,内容已经被修改为本地时间,导致输出错误。

在C++标准库中,std::gmtimestd::localtime返回指向静态内部缓冲区的指针,且这些函数共享该缓冲区。当调用其中一个函数后,再调用另一个时,静态缓冲区会被覆盖

问题分析:

  1. 首次输出UTC时间:调用std::gmtime后,utcTmConst指向静态缓冲区中的UTC时间,输出正确。

  2. 调用std::localtime:此时静态缓冲区的内容被覆盖为本地时间。

  3. 第二次输出UTC时间:虽然代码仍使用utcTmConst,但它指向的缓冲区已被修改为本地时间,因此输出错误。

解决方案:

立即复制tm结构体到独立变量中,避免后续调用覆盖数据:

std::time_t utcTimestamp = std::time(nullptr);
std::tm utcTm = *std::gmtime(&utcTimestamp); // 复制到本地变量
std::cout << "UTC 时间: " << std::put_time(&utcTm, "%F %T") << std::endl;

// 后续操作不再使用 utcTmConst,改用 utcTm

修改后的代码示例:

#include <iostream>
#include <ctime>
#include <iomanip>

int main() {
    std::time_t utcTimestamp = std::time(nullptr);

    // 复制 UTC 时间结构体到独立变量
    std::tm utcTm = *std::gmtime(&utcTimestamp);
    std::cout << "UTC 时间: " << std::put_time(&utcTm, "%F %T") << std::endl;

    // 复制本地时间结构体到独立变量
    std::tm localTm = *std::localtime(&utcTimestamp);

    // 转换回时间戳时注意 mktime 的时区处理
    std::time_t utcConvertedBackToTimestamp = std::mktime(&utcTm); // 此处逻辑可能有问题
    std::time_t localTimestamp = std::mktime(&localTm);

    std::time_t difference = localTimestamp - utcConvertedBackToTimestamp;

    // 输出时使用独立变量的地址
    std::cout << "UTC 时间: " << std::put_time(&utcTm, "%F %T") << std::endl;
    std::cout << "本地时间: " << std::put_time(&localTm, "%F %T") << std::endl;
    std::cout << "UTC 和本地时间的差值(秒): " << difference << std::endl;
}

关键点说明:

  1. 静态缓冲区覆盖std::gmtimestd::localtime共享同一块内存,后续调用会覆盖之前的结果。

  2. mktime的行为mktimetm结构体视为本地时间,若直接传入UTC时间结构体会导致转换错误。正确做法需调整时区或使用其他方法。

  3. 数据独立性:通过立即复制结构体到独立变量,确保后续操作不受其他函数调用影响。

总结:

问题的根本原因是静态缓冲区的覆盖,通过复制结构体到独立变量可避免此问题。同时需注意mktime的时区处理逻辑是否正确。

更新:这段内容太长了,我精简代码:
#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;
int main() {
    std::time_t utcTimestamp = std::time(nullptr);

    const std::tm* utcTmConst = std::gmtime(&utcTimestamp);
    std::cout << "UTC 时间: " << std::put_time(utcTmConst, "%Y-%m-%d %H:%M:%S") << std::endl;

//    std::tm utcTm(*utcTmConst);

    std::tm* localTm = std::localtime(&utcTimestamp);

//    std::time_t utcConvertedBackToTimestamp = std::mktime(&utcTm);

    std::cout << "UTC 时间: " << std::put_time(utcTmConst, "%Y-%m-%d %H:%M:%S") << std::endl;
}
 问豆包后自己整理:
先回顾静态存储区,堆栈存储先不管,我也忘了是啥了,反正全局变量、静态static声明的变量,都放在这里
 
std::tm* localTm = std::localtime(&utcTimestamp);会覆盖 const std::tm* utcTmConst = std::gmtime(&utcTimestamp);的内容,并不是说静态区只有一块,错误理解:gmtime不改变utcTimestamp,将utcTimestamp弄成结构体的值放入utcTmConst,然后localtime使得UTC+8的结构体值放入localTm,咋就改变utcTimestamp了?
 
要知道,
C++ 标准规定 std::gmtimestd::localtime 返回的指针指向一个静态存储期的对象。这意味着这些函数返回的指针指向的内存区域在程序的整个生命周期内都存在,不会因为函数调用结束而被销毁。
 
std::localtime 不会改变 utcTimestamp 的值,但 std::gmtimestd::localtime 返回的指针指向静态存储区里同一块 std::tm 结构体,静态区并非只有一块但这俩函数使用的是同一块来存放结果,所以调用 std::localtime 会覆盖之前 std::gmtime 放入该区域的内容,导致后续使用 utcTmConst 输出的是被覆盖后的本地时间内容而非原来的 UTC 时间内容。
 
 
通俗叙述就是,gmtimelocaltime函数都在std里定义好的,且类型是tm指针,表现形式是时间形式结构体,且底层把这俩函数都指向同一个静态存储区,即同一个程序中有如下两句话: const std::tm* utcTmConst = std::gmtime(&utcTimestamp);std::tm* localTm = std::localtime(&utcTimestamp);
utcTmConstloaclTm这两个tm*类型指针,都指向同一个存储区,记作A区,utcTmConst指针指向的是A区,将A区值弄成UTC结构体,localTm也是指向A区,会将A区值弄成本地时间的结构体? 那我有个疑惑,*tm这种是可以改变存储区值的?

回答:

std::tm* 这种指针类型可以改变其所指向存储区的值。指针本质上就是一个变量,它存储的是内存地址,通过指针可以对其所指向的内存区域进行读写操作。

回顾指针举个例子:

例如 int num = 10; int* ptr = &num; *ptr = 20;,这里 ptr 是指针指向 num 存储区,通过 *ptr = 20 修改其指向存储区的值,就如同 std::tm* 可修改指向 std::tm 结构体存储区的值。
 
 
一般建议将 std::gmtime 返回值用 const 修饰,因为 std::gmtime 返回的 std::tm* 指向的是静态存储区内容且为UTC,修改它可能引发问题且标准没规定可修改;而 std::localtime 返回值可加可不加 const
 
 
改进:用Deepseek 说的
std::time_t utcTimestamp = std::time(nullptr);
std::tm utcTm = *std::gmtime(&utcTimestamp); // 复制到本地变量
std::cout << "UTC 时间: " << std::put_time(&utcTm, "%F %T") << std::endl;

// 后续操作不再使用 utcTmConst,改用 utcTm

std::tm utcTm = *std::gmtime(&utcTimestamp);std::gmtime 返回的静态 std::tm 结构体内容复制到本地变量 utcTm 中,后续用 utcTm 输出就不会受 std::localtime 影响,

区别:

std::tm* localTm = std::localtime(&utcTimestamp); 是将 std::localtime 返回的指向静态 std::tm 结构体的指针赋给指针变量 localTm
std::tm localTm = *std::localtime(&utcTimestamp); 是先调用 std::localtime(&utcTimestamp) 获取指向静态 std::tm 结构体的指针,再通过解引用操作符 * 取出该结构体对象,然后用这个结构体对象对新定义的 std::tm 类型的局部变量 localTm 进行拷贝初始化,将静态存储区中存储的本地时间结构体内容复制到 localTm 中,避免后续操作覆盖数据。
 
之前都是 std::put_time(utcTmConst,避免覆盖后用std::put_time(&utcTm
之前 utcTmConst 是指向静态存储区 std::tm 对象的指针,所以直接将其作为 std::put_time 的参数;而避免覆盖采用复制操作得到的 utcTmstd::tm 类型的局部变量,std::put_time 函数需要接收指向 std::tm 的指针,因此要通过取地址符 & 得到 utcTm 的地址(即 &utcTm)作为参数传入。
 
至此可以理解上面Deepseek的代码了,此文搜“修改后的代码示例
 
那么转成codeblock可行的代码,精简后为
#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;
int main() {
    time_t utcTimestamp = time(nullptr);

    // 复制 UTC 时间结构体到独立变量
    tm utcTm = *std::gmtime(&utcTimestamp);
    cout << "UTC:"<< put_time(&utcTm, "%Y-%m-%d %H:%M:%S")<<endl;

    // 复制本地时间结构体到独立变量
    tm localTm = *std::localtime(&utcTimestamp);

    // 转换回时间戳时注意 mktime 的时区处理
//    time_t utcConvertedBackToTimestamp = mktime(&utcTm); // 此处逻辑可能有问题
//    time_t localTimestamp = mktime(&localTm);


    // 输出时使用独立变量的地址
    cout << "UTC:"<<put_time(&utcTm, "%Y-%m-%d %H:%M:%S") <<endl;
    cout << "本地:" <<put_time(&localTm, "%Y-%m-%d %H:%M:%S")<<endl;
}

 

这里我又想个事:

1、目前两种写法

#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;
int main() {
    time_t utcTimestamp = time(nullptr);

    tm utcTm = *std::gmtime(&utcTimestamp);
    cout << "UTC:"<< put_time(&utcTm, "%Y-%m-%d %H:%M:%S")<<endl;
    
    tm localTm = *std::localtime(&utcTimestamp);

    cout << "UTC:"<<put_time(&utcTm, "%Y-%m-%d %H:%M:%S") <<endl;
    cout << "本地:" <<put_time(&localTm, "%Y-%m-%d %H:%M:%S")<<endl;
}


#include <iostream>
#include <ctime>
#include <iomanip>
using namespace std;
int main() {
    time_t utcTimestamp = time(nullptr);

    tm* utcTm = gmtime(&utcTimestamp);
    cout << "UTC:"<< put_time(utcTm, "%Y-%m-%d %H:%M:%S")<<endl;

    tm* localTm = localtime(&utcTimestamp);

    cout << "UTC:"<<put_time(utcTm, "%Y-%m-%d %H:%M:%S") <<endl;
    cout << "本地:" <<put_time(localTm, "%Y-%m-%d %H:%M:%S")<<endl;
}

首先要知道的是,tm变量是用于存储分解后的日期和时间信息。它的定义大致如下:

struct tm {
    int tm_sec;   // 秒,范围从 0 到 59
    int tm_min;   // 分,范围从 0 到 59
    int tm_hour;  // 小时,范围从 0 到 23
    int tm_mday;  // 一个月中的第几天,范围从 1 到 31
    int tm_mon;   // 月份,范围从 0 到 11(0 表示 1 月)
    int tm_year;  // 自 1900 年起的年数
    int tm_wday;  // 一周中的第几天,范围从 0 到 6(0 表示星期日)
    int tm_yday;  // 一年中的第几天,范围从 0 到 365
    int tm_isdst; // 夏令时标志,正数表示使用夏令时,0 表示不使用,负数表示信息未知
};

Q:我的理解, std::tm* localTm = std::localtime(&utcTimestamp); 是说,有个tm*类型的指针叫localTm,指向的是一个存储区叫做A区,通过localtime函数,使得A区的值,弄成了UTC结构体 std::tm localTm = *std::localtime(&utcTimestamp); 这句话,是弄了一个localTm变量?tm型即时间结构体形式型?然后赋值为UTC结构体的值?这个怎么理解? 还有,第1种写法是公共拥有同一块存储区,第2种写法是分别开了两个变量,各自存储了结构体形式的值?那localtime和gmtime他们本来指向的存储区是啥?没值吗?

A:

  • std::tm* localTm = std::localtime(&utcTimestamp);localTmtm* 类型指针,指向 localtime 函数返回的静态存储区(A 区),此存储区被 localtime 填充为本地时间对应的 tm 结构体内容,而非 UTC 结构体。
  • std::tm localTm = *std::localtime(&utcTimestamp);:创建了一个 std::tm 类型的变量 localTm,把 localtime 返回指针所指静态存储区里的 tm 结构体内容复制到 localTm 这个变量中,也就是把本地时间结构体值赋给它。
  • 第一种写法多个指针可能指向 localtimegmtime 返回的同一块静态存储区,在多线程环境可能有数据竞争问题;第二种写法是分别定义变量存储结构体值,各自独立。
  • localtimegmtime 指向的静态存储区初始无有意义值,调用函数时,它们会根据传入的 time_t 类型时间戳,计算并填充对应(本地时间或 UTC 时间)的 tm 结构体值到该静态存储区。即指向的静态区里存的值,是最后一次的,没啥实际固定的意义,真正有意义的都已经分离存储到localTm和gmtime的tm变量里了

2、之前的理解localtime + mktime抵消那个还成立吗?

 一试便知

此文搜“附上”的第一个,那段代码,在后面加一个输出那段静态存储区,看看是不是会变,即把第一个Tm那个再复制到最下面,确实变了,符合新的这个认知

#include <iostream>
#include <ctime>
#include<iomanip>
using namespace std;
int main() {
    // 获取当前的UTC时间戳
    std::time_t utcTimestamp = std::time(nullptr);
    cout<<"std::time:\n"<<utcTimestamp<<"\n"<<endl;
    // 将UTC时间戳转换为本地时间的tm结构体
    tm* Tm =  std::gmtime(&utcTimestamp);
    time_t Time = std::mktime(Tm);
    cout<<"std::time时间戳后,gmtime再mktime:Time\n"<<Time<<"\n"<<endl;

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

    tm* localTm =  std::localtime(&utcTimestamp);
    time_t localTimestamp = std::mktime(localTm);
    cout<<"std::time时间戳后,localtime再mktime:\n"<<localTimestamp<<std::endl;
    cout << "具体为:"
              << (localTm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (localTm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << localTm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << localTm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << localTm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << localTm->tm_sec
              << std::endl;




              cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

}

3、那个有趣的事,此文搜“让这件事凸显出来”那段代码

 

#include <iostream>
#include <ctime>
#include<iomanip>
using namespace std;
int main() {
    // 获取当前的UTC时间戳
    std::time_t utcTimestamp = std::time(nullptr);
    cout<<"std::time:\n"<<utcTimestamp<<"\n"<<endl;

    tm* Tm =  std::gmtime(&utcTimestamp);
    time_t Time = std::mktime(Tm);
    cout<<"std::time时间戳后,gmtime再mktime:Time:\n"<<Time<<"\n"<<endl;

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

    tm* TTm =  std::gmtime(&Time);
//    Tm =  std::gmtime(&Time);

    cout << "具体为:"
              << (Tm->tm_year + 1900) << "-"  // 年份从1900年开始计数,所以需要加上1900
              << std::setw(2) << std::setfill('0') << (Tm->tm_mon + 1) << "-"  // 月份从0开始计数,所以需要加上1,并使用setw和setfill来格式化输出
              << std::setw(2) << std::setfill('0') << Tm->tm_mday << " "
              << std::setw(2) << std::setfill('0') << Tm->tm_hour << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_min << ":"
              << std::setw(2) << std::setfill('0') << Tm->tm_sec
              << "\n"<<endl;

}

无论Tm还是新命名TTm,最后Tm都变了

也验证了gmtime就是操作同一个存储块 

专业解答:
Q:
tm* Tm = std::gmtime(&utcTimestamp);这句话后再来个
tm* TTm = std::gmtime(&Time); 最后Tm指向的值也会跟着变化,是不是因为gmtime是同一个存储快 一句话回答
 
A:
是的,std::gmtime 返回的是指向静态存储区的指针,多次调用该函数会覆盖该存储区内容,所以最后 Tm 指向的值会跟着变化。
 
Q:
无论起什么新的名字对吧?哪怕tm* TTm,也会一同改变Tm指向的
 
A:
对,std::gmtime 每次调用都复用同一静态存储区,不管新指针叫 TTm 还是其他名字,后续调用改变该存储区内容时,之前指针(如 Tm)指向的值也会变。
 
某人为了学UNIX开发了LINUX 我为了学C++,,,写书?
View Code

 

 
gmtime函数VS会报错:
'gmtime': This function or variable may be unsafe. Consider using gmtime_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
说要么用gmtime_s
要么加#define _CRT_SECURE_NO_WARNINGS去掉警告,
#define _CRT_SECURE_NO_WARNINGS 是一个预处理指令,其作用主要是针对 Microsoft Visual C++ 运行时库(CRT)的安全检查机制。以下为你详细解释:

背景

在 Visual Studio 中,为了提高程序的安全性,Microsoft 对部分传统的 C/C++ 标准库函数进行了检查,认为一些函数存在潜在的安全风险。例如,像gmtimestrcpygets等函数,可能会由于缓冲区溢出、多线程数据竞争等问题引发程序崩溃或产生安全漏洞。当你在代码里使用这些被认为不安全的函数时,编译器就会给出警告,提示你使用更安全的替代函数(如用gmtime_s替代gmtimestrcpy_s替代strcpy )。

含义

#define 是 C/C++ 中的预处理指令,用于定义宏。_CRT_SECURE_NO_WARNINGS 是一个预定义宏,当在代码文件开头添加 #define _CRT_SECURE_NO_WARNINGS 语句时,就相当于告诉编译器:“我知道这些被标记为不安全的函数存在潜在风险,但我还是要使用它们,你别再给我弹出这些安全相关的警告了”。这样一来,编译器在编译过程中就会忽略掉这些函数的安全检查警告。
 
唉好烦啊~~~~(>_<)~~~~
 
 

Q:一句话解释tm* localTm = localtime(&now);

A:这句话定义了一个指向 tm 结构体的指针 localTm,并调用 localtime 函数将以秒为单位表示的当前时间 now 转换为本地时间,localtime 函数返回指向包含该本地时间信息的 tm 结构体的指针,然后将这个指针赋值给 localTm

 
 
备注:

###:

释怀

京杭不知道农发

WYH不知道什么叫全部动起来,什么叫国产化、统信
有时候不是农民文化水平不高,而是解释不清楚


为了学UNIX开发了LINUX
我为了学C++,,,,

###:烦人豆包侧边栏总是鼠标滑过就展开,还回不去,直接屏蔽掉,需要再解开

doubao.com##.sidebar-UeSlOl.collapsed-Xorh_w.open-temporarily-MrUqNU

###:过年回家3天硬座,记录于微信,过年后继续回来学

###:豆包:以下所有问题不特殊说明都一句话回答,且全都用C++

###:VS就是个智障垃圾玩意

操!C 你!妈! 的 傻逼VS的代码补全真他妈恶心,给残疾人用的吗?体验感没codeblock的十万分之一好用,我就加个注释,换做codeblock直接全选ctrl+shift+C就行,而VS不知道用啥,我想用/**/,结果他妈的强制性补全,删除又删不掉

  •  关闭VS C++ 悬停提示 
  1. 打开 Visual Studio 2022,点击顶部菜单栏中的 “工具”(Tools),在下拉菜单里选择 “选项”(Options)。
  2. 在弹出的 “选项” 对话框左侧列表中,展开 “文本编辑器”(Text Editor)。
  3. 展开 “C/C++”,接着选择 “高级”(Advanced)。
  4. 在右侧 “IntelliSense” 区域内,找到 “自动快速信息”(Auto Quick Info),将其值从 “True” 改为 “False” 。
  5. 点击 “确定” 保存设置。

 

快捷键: 

Q:我在codeblock里可以用ctrl+shift+C和X来注释和取消,VS2022里,有类似的吗

A:在 VS2022 里,可用 Ctrl+K、Ctrl+C 组合键来注释所选代码,用 Ctrl+K、Ctrl+U 组合键来取消注释。

 

Ctrl+L删除某行

 

###:初学我尽量写  using namespace std;  这样好处可以专注编程和代码思路,满片子绿色的std::真的傻逼,光有整齐的书写了,但过于无意义的追叙std::,相当耽误看代码理解代码

就像考试没写解一样,下次总会记得,但总写解或者总写std::太干扰理解代码了,还有就是这些关于时间戳的问题,我理解学了好久好久,收获是适应了这种C++各种std库函数,但有时候时间戳问题涉及底层编译器亦或是其他解释,就很烦,很想专注学代码看代码,反倒是他妈的搞了一堆工具,一堆环境,一堆编译器版本,系统时间,编译器版本,运行配置,编译器和工具链问题,狗屁问题,真的很难受

 

看了好几好几天的时间戳也终于才熟悉了几个gmtimemktimelocaltimetm函数

###:

###:

 

posted @ 2025-01-21 19:54  GerJCS  阅读(58)  评论(0)    收藏  举报