指针&引用
指针是 C++ 从 C 中继承的重要数据类型,它提供了一种较为直接的地址操作手段,使用指针可灵活实现动态内存的分配。
声明
指针是一种数据类型,具有指针类型的变量称为 <b>指针变量</b>,它用于存放内存地址。在使用指针之前,需对指针进行声明,其一般形式为:
type *pointer_name;
其中 type
表示所指对象的数据类型,即说明所指内存单元存放的数据类型;*
表示声明一个指针类型的变量;pointer_name
表示指针名。
声明三个数据类型不同的指针变量:
int *p; //声明 int 型的指针变量 p。
float *p1; //声明 float 型的指针变量 p1。
char *p2; //声明 char 型的指针变量 p2。
值得注意的是,指针实际存放的是内存地址,不同类型的指针变量用于指向数据类型不同的变量或常量。
C++ 中提供了两个与地址相关的运算符:*
和 &
。其中 *
称为 <b>指针运算符</b>,用于获取指针所指变量的值,例如 *p
表示指针 p 所指变量的值;&
称为 <b>取地址运算符</b>,用于获取一个对象的地址,例如有变量 i,则 &i
表示 i 的存储地址。
#include<iostream>
using namespace std;
int main(){
int *p;
cout<<sizeof(p)<<endl; // 8 计算的是指针变量的大小,64 位系统是 8 字节,32 位系统是 4 字节
cout<<sizeof(*p)<<endl; // 4
char *c;
cout<<sizeof(c)<<endl; // 8
cout<<sizeof(*c)<<endl; // 1
}
赋值和使用
定义指针后我们仅得到了一个用于存储地址的指针变量,若要确定指针变量存放的内存地址,可以通过给指针赋值实现。其基本形式为:
1️⃣在定义指针时为指针赋值:
type *pointer_name=初始地址;
2️⃣在定义指针后为指针赋值:
type *pointer_name;
pointer_name=初始地址;
其中 <b>初始地址</b> 为某个对象的内存地址,一般使用 &对象名
获取。
例如将指针 p 指向变量 po1 的地址,其中变量 po1 的值为 6:
int po1=6; //定义 int 型变量 po1,赋值为 6。
int *p=&po1; //指针变量 p 指向变量 po1 的地址。
其等价于:
int po1=6; //定义 int 型变量 po1,赋值为 6。
int *p; //定义指针变量 p
p=&po1; //指针变量 p 指向变量 po1 的地址。
我们通过一个例子加深一下指针的定义和使用,新建 pointer1.cpp
,输入:
#include<iostream>
using namespace std;
int main (){
int po1=6; //定义 int 型变量 po1,赋值为 6。
int *p=&po1; //指针变量 p 指向变量 po1 的地址。
cout << "获取指针所指变量的值: "<<*p<<endl;
cout << "获取指针的内存地址: "<<&p<<endl;
return 0;
}
/**
获取指针所指变量的值: 6
获取指针的内存地址: 0x7fffffffdc10
*/
其中 *p
运用指针运算符 *
获取指针 p 所指变量的值;&p
运用取地址运算符 &
获取指针 p 的地址。
用指针修改变量所指向内存地址中的数据
#include<iostream>
using namespace std;
int main(){
int p = 6;
int *pp = &p;
*pp = 10;
cout<<p<<endl; // 10
}
引用
引用是指对已存在的变量别名,我们可以使用引用名来指向对象。
引用与指针主要有三个区别:
- 可以通过
指针名=0
描述一个空指针,但不存在空引用。
- 指针可在任何时间进行初始化操作,而引用只能在定义时进行初始化操作。
- 指针变量指向内存的一个存储单元;而引用只不过是原变量的一个别名而已。
声明引用变量的基本格式为:
type &引用名=被指对象名;
&
在这里是标识作用,而非取地址运算符。
例如定义引用 x,它是整型变量 i 的引用:
int &x=i;
我们通过 <b>初始化时间</b> 来区别一下指针和引用,新建 pointer2.cpp
文件,输入:
#include<iostream>
using namespace std;
int main ()
{
int i=3;
int j=4;
int &x=i; //定义引用 x,它是整型变量 i 的引用。
int *s; //定义指针 s。
s=&j; //指针 s 指向整型变量 j 的地址。
cout << "初始化引用 x: " << x << endl;
cout << "初始化指针 s: " << *s << endl;
return 0;
}
其中 int &x=i;
表示在 <b>定义引用时</b> 进行的初始化操作。s=&j;
表示在 <b>定义指针后</b> 进行的初始化操作。
#include<iostream>
using namespace std;
int swap(int &x,int &y){
int tmp = x;
x = y;
y = tmp;
}
int swapByPoint(int *x,int *y){
// *x 是操作内存中的值。
int tmp = *x;
*x = *y;
*y = tmp;
}
int main(){
int x = 10,y = 20;
swap(x,y);
// 20:10 交换成功
cout<<x<<":"<<y<<endl;
int *xp;
int *yp;
xp = &x;
yp = &y;
// 10:20 交换成功
swapByPoint(xp,yp);
cout<<x<<":"<<y<<endl;
}
引用作为返回值
#include <iostream>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i){
return vals[i]; // 返回第 i 个元素的引用
}
// 要调用上面定义函数的主函数
int main (){
cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ ){
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ ) {
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() {
int q;
//! return q; // 在编译时发生错误
static int x;
return x; // 安全,x 在函数作用域外依然是有效的
}
日期&时间
介绍 C/C++ 与时间相关的函数的使用,包括如何获取日历时间,如何获取本地时间,如何将得到的时间格式化输出,如何计算时间间隔,最后面的部分介绍了自定义时间格式的相关语法。
获取时间戳
time_t 是定义在 time.h 中的一个类型,表示一个日历时间,也就是从 1970 年 1 月 1 日 0 时 0 分 0 秒到此时的秒数,原型是:
typedef long time_t; /* time value */
可以看出 time_t 其实是一个长整型,由于长整型能表示的数值有限,因此它能表示的最迟时间是 2038 年 1 月 18 日 19 时 14 分 07 秒。
通过函数 time 获取时间戳
#include<iostream>
#include<ctime>
using namespace std;
int main(void){
time_t now = time(nullptr);
cout<<now<<endl; // 1667977365
return 0;
}
获取本地时间
time_t 只是一个长整型,不符合我们的使用习惯,需要转换成本地时间,就要用到 tm 结构,time.h 中结构 tm 的原型是:
struct tm {
int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61
int tm_min; // 分,范围从 0 到 59
int tm_hour; // 小时,范围从 0 到 23
int tm_mday; // 一月中的第几天,范围从 1 到 31
int tm_mon; // 月,范围从 0 到 11
int tm_year; // 自 1900 年起的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起
int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起
int tm_isdst; // 夏令时
}
// 源码注释
/* ISO C `broken-down time' structure. */
struct tm{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
# ifdef __USE_MISC
long int tm_gmtoff; /* Seconds east of UTC. */
const char *tm_zone; /* Timezone abbreviation. */
# else
long int __tm_gmtoff; /* Seconds east of UTC. */
const char *__tm_zone; /* Timezone abbreviation. */
# endif
};
#endif
使用这个结构可以很方便的看到年月日时分秒。
使用 localtime 获取当前系统时间,该函数将 time_t 时间转换为 tm 结构表示的时间,函数原型:
struct tm * localtime(const time_t *)
使用 gmtime 获取格林尼治时间,函数原型:
struct tm * gmtime (const time_t *)
使用 localtime 获取当前时间
#include<iostream>
#include<ctime>
using namespace std;
int main(){
time_t now = time(nullptr);
cout<<now<<endl; // 1667977365
tm *nowtime = localtime(&now);
cout<<1900+nowtime->tm_year<<"年-";
cout<<1+nowtime->tm_mon<<"月-";
cout<<nowtime->tm_mday<<"日";
}