C语言 百文网手机站

C++中时间与时间戳的转换

时间:2022-04-21 08:39:40 C语言 我要投稿

C++中时间与时间戳的转换

  C语言把括号、赋值、强制类型转换等都作为运算符处理。从而使C语言的运算类型极其丰富,表达式类型多样化。下面是小编分享的C++中时间与时间戳的转换,一起来看一下吧。

C++中时间与时间戳的转换

  C++ 中时间与时间戳的转换实例

  // 设置时间显示格式:

  NSString *timeStr = @"2011-01-26 17:40:50";

  [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; // ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制。

  //设置时区,这个对于时间的处理有时很重要。

  //例如你在国内发布信息,用户在国外的另一个时区,你想让用户看到正确的发布时间就得注意时区设置,时间的换算。

  //例如你发布的时间为2010-01-26 17:40:50,那么在英国爱尔兰那边用户看到的时间应该是多少呢?

  //他们与我们有7个小时的时差,所以他们那还没到这个时间呢...那就是把未来的事做了。

  NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];

  [formatter setTimeZone:timeZone];

  NSDate *date = [formatter dateFromString:timeStr]; //------------将字符串按formatter转成nsdate

  NSLog(@"date = %@", date);

  NSDate *datenow = [NSDate date];//现在时间,你可以输出来看下是什么格式

  NSLog(@"datenow = %@", datenow);

  NSString *nowtimeStr = [formatter stringFromDate:datenow];//----------将nsdate按formatter格式转成nsstring,nsstring会显示与当前的时间吻合的串

  NSLog(@"nowtimeStr = %@", nowtimeStr);

  // 时间转时间戳的方法:

  NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];

  NSLog(@"timeSp:%@",timeSp); //时间戳的值

  // 时间戳转时间的方法

  NSDate *confromTimesp = [NSDate dateWithTimeIntervalSince1970:1296035591];

  NSLog(@"1296035591 = %@",confromTimesp);

  NSString *confromTimespStr = [formatter stringFromDate:confromTimesp];

  NSLog(@"confromTimespStr = %@",confromTimespStr);

  // 时间戳转时间的方法:

  NSDateFormatter* formatter1 = [[NSDateFormatter alloc] init];

  [formatter1 setDateStyle:NSDateFormatterMediumStyle];

  [formatter1 setTimeStyle:NSDateFormatterShortStyle];

  [formatter1 setDateFormat:@"yyyyMMddHHMMss"];

  NSDate *date1 = [formatter1 dateFromString:@"1283376197"];

  NSLog(@"date1:%@",date1);

  当前时间是:14:41:57

  C/C++内存管理

  内存分配方式

  简介

  在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

  栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

  堆:就是那些由 new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个 。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

  自由存储区:就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

  全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

  常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。

  常见的内存错误及其对策

  发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。

  常见的内存错误及其对策如下:

  内存分配未成功,却使用了它。编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。

  内存分配虽然成功,但是尚未初始化就引用它。犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

  内存分配成功并且已经初始化,但操作越过了内存的边界。例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。

  忘记了释放内存,造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/同理)。

  释放了内存却继续使用它。

  有三种情况:

  (1). 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

  (2). 函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

  (3). 使用free或释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

  那么如何避免产生野指针呢?这里列出了5条规则,平常写程序时多注意一下,养成良好的习惯。

  规则1:用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

  规则2:不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

  规则3:避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

  规则4:动态内存的申请与释放必须配对,防止内存泄漏。

  规则5:用free或释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

  C/C++函数调用的方式

  栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

  在参数传递中,有两个重要的问题必须要明确说明:

  1. 当参数个数多于一个时,按照什么顺序把参数压入堆栈;

  2. 函数调用后,由谁来把堆栈恢复原状。

  在高级语言中,就是通过函数的调用方式来说明这两个问题的。常见的调用方式有:

  stdcall

  cdecl

  fastcall

  thiscall

  thiscall

  naked call

  下面就分别介绍这几种调用方式:

  1.stdcall

  stdcall调用方式又被称为Pascal调用方式。在Microsoft C++系列的C/C++编译器中,使用PASCAL宏,WINAPI宏和CALLBACK宏来指定函数的调用方式为stdcall。

  stdcall调用方式的函数声明为:

  int stdcall function(int a, int b);

  stdcall的调用方式意味着:

  (1) 参数从右向左一次压入堆栈

  (2) 由被调用函数自己来恢复堆栈

  (3) 函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸

  上面那个函数翻译成汇编语言将变成:

  push b 先压入第二个参数

  push a 再压入第一个参数

  call function 调用函数

  在编译时,此函数的名字被翻译为function@8

  2.cdecl

  cdecl调用方式又称为C调用方式,是C语言缺省的调用方式,它的语法为:

  int function(int a, int b) // 不加修饰符就是C调用方式

  int cdecl function(int a, int b) // 明确指定用C调用方式

  cdecl的调用方式决定了:

  (1) 参数从右向左依次压入堆栈

  (2) 由调用者恢复堆栈

  (3) 函数名自动加前导下划线

  由于是由调用者来恢复堆栈,因此C调用方式允许函数的参数个数是不固定的,这是C语言的一大特色。

  此方式的函数被翻译为:

  push b // 先压入第二个参数

  push a // 在压入第一个参数

  call funtion // 调用函数

  add esp, 8 // 清理堆栈

  在编译时,此方式的函数被翻译成:function

  3.fastcall

  fastcall 按照名字上理解就可以知道,它是一种快速调用方式。此方式的函数的第一个和第二个DWORD参数通过ecx和edx传递,

  后面的参数从右向左的顺序压入栈。

  被调用函数清理堆栈。

  函数名修个规则同stdcall

  其声明语法为:

  int fastcall function(int a, int b);

  4.thiscall

  thiscall 调用方式是唯一一种不能显示指定的修饰符。它是c++类成员函数缺省的调用方式。由于成员函数调用还有一个this指针,因此必须用这种特殊的调用方式。

  thiscall调用方式意味着:

  参数从右向左压入栈。

  如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压入栈后被压入栈。

  参数个数不定的,由调用者清理堆栈,否则由函数自己清理堆栈。

  可以看到,对于参数个数固定的情况,它类似于stdcall,不定时则类似于cdecl。

  5.naked call

  是一种比较少见的调用方式,一般高级程序设计语言中不常见。

  函数的声明调用方式和实际调用方式必须一致,必然编译器会产生混乱。

  函数名字修改规则:

  1.C编译时函数名修饰约定规则:

  stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为function@8。

  cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为function。

  fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@8。

  它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。

  2.C++编译时函数名修饰约定规则:

  stdcall调用约定:

  (1)以“?”标识函数名的开始,后跟函数名;

  (2)函数名后面以“@@YG”标识参数表的开始,后跟参数表;

  (3)参数表以代号表示:

  X--void ,

  D--char,

  E--unsigned char,

  F--short,

  H--int,

  I--unsigned int,

  J--long,

  K--unsigned long,

  M--float,

  N--double,

  N--bool,

  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代

  表一次重复;

  (4)参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;

  (5)参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。

  其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如

  int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”

  void Test2() -----“?Test2@@YGXXZ”

  cdecl调用约定:

  规则同上面的stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。

  fastcall调用约定:

  规则同上面的stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。

  VC++对函数的省缺声明是"cedcl",将只能被C/C++调用。

  关于C/C++ 表达式求值顺序

  i = ++i + 1; // The behavior is unspecified

  在介绍概念之前,我们先解释一下它的结果。这个表达式( expression )包含3个子表达式( subexpression ):

  e1 = ++i

  e2 = e1 + 1

  i = e2

  这三个子表达式都没有顺序点( sequence point ),而 ++ i 和 i = e3 都是有副作用( side effect )的表达式。由于没有顺序点,语言不保证这两个副作用的顺序。

  更加可怕的是,如果i 是一个内建类型,并在下一个顺序点之前被改写超过一次,那么结果是未定义(undefined)的!比如本例中如果有:

  int i = 0x1000fffe;

  i = ++i + 1; // The result is undefined!!

  你也许会认为他的结果是加1 或者加2,其实更糟糕 —— 结果可能是 0x1001ffff 。他的高字节接受了一个副作用的内容,而低字节则接受了另一个副作用的内容! 如果i 是指针,那么将很容易造成程序崩溃。

  为什么要这么做呢?因为对于编译器提供商来说,未确定的顺序对优化有相当重要的作用。比如,一个常见的优化策略是“减少寄存器占用和临时对象”。编译器可以重新组织表达式的求值,以便尽量不使用额外的寄存器以及临时变量。 更加严格的说,即使是编译器提供商也无法完全彻底序列化指令(比如无法严格规定读和写的顺序),因为CPU本身有权利修改指令顺序,以便达到更高的速度。

  下面的术语以 ISO C99 和 C++03为准。译名为参考并附带原术语对照,如有解释不当或者错误望指正。

  表达式有两种功能。每个表达式都产生一个值( value ),同时可能包含副作用( side effect ),比如:他可能修改某些值。

  规则的核心在于 顺序点( sequence point ) [ C99 6.5 Expressions 条款2 ] [ C++03 5 Expressions 概述 条款4 ]。 这是一个结算点,语言要求这一侧的求值和副作用(除了临时对象的销毁以外)全部完成,才能进入下面的部分。 C/C++中大部分表达式都没有顺序点,只有下面五种表达式有:

  1 函数。函数调用之前有一个求值顺序点。

  2 && || 和 ?: 这三个包含逻辑的表达式。其左侧逻辑完成后有一个求值顺序点。

  3 逗号表达式。逗号左侧有一个求值顺序点。

  注意,他们都只有一个求值顺序点,2和3的右侧运算结束后并没有求值顺序点。

  在两个顺序点之间,子表达式求值和副作用的顺序是不确定的.。假如代码的结果与求值和副作用发生顺序相关,我们称这样的代码有不确定的行为(unspecified behavior)。 而且,假如期间对一个内建类型执行一次以上的写操作,则是未定义行为(undefined behavior)——我们知道,未定义行为带来最好的后果是让你的程序立即崩掉。

  n = n++; // 两个副作用,对于内建对象产生是未定义行为

  几乎所有表达式,求值顺序都不确定。比如,下面的加法, f1 f2 f3的调用顺序是任意的:

  n = f1() + f2() + f3(); // f1 f2 f3 调用顺序任意

  而函数也只在实际调用前有一个求值顺序点。所以,常见于早期 C 语言教材的这类题目,是错题:

  printf("%d",--a+b,--b+a); // --a + b 和 --b + a 这两个子表达式,求值顺序不确定

  天啊,甚至可能出现未定义行为?那么坚决不写与实现相关的代码是最好的对策。即使是不确定行为(比如函数调用时) 只要没有顺序点编译器怎么做方便就怎么做。 有些人认为函数调用参数求值与入栈顺序相关,这是一种误导。这个东西要解释,无异于事后诸葛亮:

  void f( int i1, int i2, int i3, int i4 ){

  cout<< i1 << ' ' << i2 << ' ' << i3 << ' ' << i4 << endl;}

  int main(){

  int i = 0;

  f( i++, i++, i++, i++ );}

  这个有四个表达式求值,同时每个表达式都有负作用。这八个操作顺序是任意的,那么结果如何?未定义。

  请用 VC7.1 Debug和 Release 分别测试这同一份代码,结果是不同的:

  0 0 0 0 [release]

  3 2 1 0 [debug]

  事实上,鉴于前面的讨论,如果换一些其他初始值,这里甚至会出现错位而得到千奇百怪的诡异结果。

  再看看C/C++标准中的其他经典例子:

  [C99] 6.5.2.2 Function call

  条款12 EXAMPLE 在下面的函数调用中:

  (*pf[f1()]) ( f2(), f3() + f4() )

  函数 f1 f2 f3 和f4 可能以任何顺序被调用。 但是,所有副作用都必须在那个 pf[ f1() ] 返回的函数指针产生的调用前完成。

  [C++03] 5 Expressions 概论4

  i = v[i++]; // the behavior is unspecified

  i = 7, i++, i++; // i becomes 9 ( 译注: 赋值表达式比逗号表达式优先级高 )

  i = ++i + 1; // the behavior is unspecified

  i = i + 1; // the value of i is incremented

  More Effective C++ 告诫我们, 千万不要重载 &&, || 和, 操作符[ MEC ,条款7 ]。为什么?

  以逗号操作符为例,每个逗号左侧有一个求值顺序点。假如ar是一个普通的对象,下面的做法是无歧义的:

  ar[ i ], ++i ;

  但是,如果ar[ i ] 返回一个 class A 对象或引用,而它重载了 operator, 那么结果不妙了。那么,上面的语句实际上是一个函数调用:

  ar[ i ].operator, ( ++i );

  C/C++ 中,函数只在调用前有一个求值顺序点。所以 ar[i] 和 ++i 的求值、以及 ++i 副作用的顺序是任意的。这会引起混乱。

  更可怕的是,重载 && 和 || 。 大家已经习惯了其速死算法: 如果左侧求值已经决定了最终结果,则右侧不会被求值。而且大家很依赖这个行为,比如是C风格字符串拷贝常常这样写:

  while( p && *p )

  *pd++ = *p++;

  假如p 为 0, 那么 *p 的行为是未定义的,可能令程序崩溃。 而 && 的求值顺序避免了这一点。 但是,如果我们重载 && 就等于下面的做法:

  exp1 .operator && ( exp2 )

  现在不仅仅是求值混乱了。无论exp1是什么结果,exp2 必然会被求值。

  C与C++的变量声明

  如何理解C和C++的复杂类型声明,曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) );这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++ 声明,我们将从天天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++ 声明的“右左法则”,需要强调一下的是,复杂的C/C++ 声明并不是好的编程风格;我这里仅仅是教你如何去理解这些声明。注重:为了保证能够在同一行上显示代码和相关注释,本文最好在至少1024x768分辨率的显示器上阅读。

  让我们从一个非常简单的例子开始,如下:

  int n;

  这个应该被理解为www.diannao114.cn “declare n as an int”(n是一个int型的变量)。接下去来看一下指针变量,如下:

  int *p;

  这个应该被理解为“declare p as an int *”(p是一个int *型的变量),或者说p是一个指向一个int型变量的指针。我想在这里展开讨论一下:我觉得在声明一个指针(或引用)类型的变量时,最好将*(或 &)写在紧靠变量之前,而不是紧跟基本类型之后。这样可以避免一些理解上的误区,比如:

  int* p;//不推荐

  再来看一个指针的指针的例子:

  char **argv;

  理论上,对于指针的级数没有限制,你可以定义一个浮点类型变量的指针的指针的指针的指针,再来看如下的声明:

  int RollNum[30][4];

  int (*p)[4]=RollNum;

  int *q[5];

  这里,p被声明为一个指向一个4元素(int类型)数组的指针,而q被声明为一个包含5个元素(int类型的指针)的数组。另外,我们还可以在同一个声明中混合实用*和&,如下:

  int **p1;// p1 is a pointer to a pointer to an int.

  int *&p2;// p2 is a reference to a pointer to an int.

  int &*p3;// ERROR: Pointer to a reference is illegal.

  int &&p4;// ERROR: Reference to a reference is illegal.

  注:p1是一个int类型的指针的指针;p2是一个int类型的指针的引用;p3是一个int类型引用的指针(不合法!);p4是一个int类型引用的引用(不合法!)。

  const 修饰符

  当你想阻止一个变量被改变,可能会用到const要害字。在你给一个变量加上const修饰符的同时,通常需要对它进行初始化,因为以后的任何时候你将没有机会再去改变它。例如:

  const int n=5;

  int const m=10;

  上述两个变量n和m其实是同一种类型的变量,都是const int(整形恒量)。因为C 标准规定,const要害字放在类型或变量名之前等价的。我个人更喜欢第一种声明方式,因为它更突出了const修饰符的作用。当const与指针一起使用时,轻易让人感到迷惑。例如,我们来看一下下面的p和q的声明:

  const int *p;

  int const *q;

  他们当中哪一个代表const int类型的指针(const直接修饰int),哪一个代表int类型的const指针(const直接修饰指针)?实际上,p和q都被声明为const int类型的指针。而int类型的const指针应该这样声明:

  int * const r= &n;// n has been declared as an int

  这里,p和q都是指向const int类型的指针,也就是说,你在以后的程序里不能改变*p的值。而r是一个const指针,它在声明的时候被初始化指向变量n(即r=&n;)之后,r的值将不再答应被改变(但*r的值可以改变)。

  组合上述两种const修饰的情况,我们来声明一个指向 const int类型的const指针,如下:

  const int * const p=&n// n has been declared as const int

  下面给出的一些关于const的声明,将帮助你彻底理清const的用法。不过请注重,下面的一些声明是不能被编译通过的,因为他们需要在声明的同时进行初始化。为了简洁起见,我忽略了初始化部分;因为加入初始化代码的话,下面每个声明都将增加两行代码。

  char **p1;// pointer to pointer to char

  const char **p2;// pointer to pointer to const char

  char * const * p3;// pointer to const pointer to char

  const char * const * p4;// pointer to const pointer to const char

  char ** const p5;// const pointer to pointer to char

  const char ** const p6;// const pointer to pointer to const char

  char * const * const p7;// const pointer to const pointer to char

  const char * const * const p8;// const pointer to const pointer to const char

  注: p1是指向char类型的指针的指针;p2是指向const char类型的指针的指针;p3是指向char类型的const指针;p4是指向const char类型的const指针;p5是指向char类型的指针的const指针;p6是指向const char类型的指针的const指针;p7是指向char类型const指针的const指针;p8是指向const char类型的const指针的const指针。

【C++中时间与时间戳的转换】相关文章:

php时间戳与日期如何转换09-11

解析php时间戳与日期的转换08-14

C++的类型转换介绍10-01

php格式化时间戳的方法技巧09-04

PHP时间戳使用实例代码「详细版」08-13

如何解析php日期转时间戳08-13

关于PHP的日期与时间09-07

c++ 中--declspec 的用法详解10-06

C++中内联函数的应用09-22