C++_03_基础语法
2018-01-19 09:28
483 查看
C++基础语法03
C++ 指针
通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C++ 程序员,学习指针是很有必要的。正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:
#include <iostream> using namespace std; int main () { int var1; char var2[10]; cout << "var1 变量的地址: "; cout << &var1 << endl; cout << "var2 变量的地址: "; cout << &var2 << endl; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
var1 变量的地址: 0xbfebd5c0 var2 变量的地址: 0xbfebd5b6
#include <iostream> using namespace std; int main(){ // 定义变量 int age = 16; char name[10]; // 打印变量的内存地址 cout << &age << endl; //0x7fff5b7adb00 cout << &name << endl;//0x7fff5b7adb2e return 0; }
通过上面的实例,我们了解了什么是内存地址以及如何访问它。接下来让我们看看什么是指针。
什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:type *var_name;
在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var_name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是同一个符号的。但是,在这个语句中,星号是用来声明指定这个变量它是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch /* 一个字符型的指针 */
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
C++ 中使用指针
使用指针时会频繁进行以下几个操作:1.定义一个指针变量、
2.用取地址符&把变量的地址赋值给指针、
3.通过解引用符号* 访问指针变量中可用地址的值。
这些是通过使用一元运算符 * (又称解引用)来返回位于操作数所指定地址的变量的值。
下面的实例涉及到了这些操作:
#include <iostream> using namespace std; int main(){ // 定义变量 int var = 20; int *p; p = &var; // 输出指针p存放的地址 cout << p << endl;//0x7fff5bfb7b18 // 解引用,即打印指针所指向的变量的值 cout << *p << endl; return 0; }
#include <iostream> using namespace std; int main () { int var = 20; // 实际变量的声明 int *ip; // 指针变量的声明 ip = &var; // 在指针变量中存储 var 的地址 cout << "Value of var variable: "; cout << var << endl; // 输出在指针变量中存储的地址 cout << "Address stored in ip variable: "; cout << ip << endl; // 访问指针中地址的值 cout << "Value of *ip variable: "; cout << *ip << endl; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of var variable: 20 Address stored in ip variable: 0xbfc601ac Value of *ip variable: 20
C++ 指针详解
在 C++ 中,有很多指针相关的概念,这些概念都很简单,但是都很重要。下面列出了 C++ 程序员必须清楚的一些与指针相关的重要概念:概念 | 描述 |
---|---|
C++ Null 指针 | C++ 支持空指针。NULL 指针是一个定义在标准库中的值为零的常量。 |
C++ 指针的算术运算 | 可以对指针进行四种算术运算:++、--、+、- |
C++ 指针 vs 数组 | 指针和数组之间有着密切的关系。 |
C++ 指针数组 | 可以定义用来存储指针的数组。 |
C++ 指向指针的指针 | C++ 允许指向指针的指针。 |
C++ 传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。(即在函数内部实现改变外部变量) |
C++ 从函数返回指针 | C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。???Excuse Me ??? |
C++ Null 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:
#include <iostream> using namespace std; int main () { int *ptr = NULL; cout << "ptr 的值是 " << ptr ; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
ptr 的值是 0
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,您可以使用 if 语句,如下所示:
if(ptr) /* 如果 p 非空,则完成 */ if(!ptr) /* 如果 p 为空,则完成 */
因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。
#include <iostream> using namespace std; int main(){ // 定义一个空指针 int *p; p = NULL; // 输出指针p存放的地址 cout << p << endl;// \0x0 if (!p){ cout << "空指针" << endl; }else{ cout << "不是空指针" << endl; } // 解引用,即打印指针所指向的变量的值 cout << *p << endl;//Segmentation fault: 11 return 0; }
C++ 指针的算术运算
指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位(即4字节)的整数,让我们对该指针执行下列的算术运算:
ptr++
在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,(前提是该操作系统整型占4个字节),即当前位置往后移 4 个字节。这个运算会在不影响内存位置中 实际整型变量的数值的情况下,移动指针到下一个内存位置。如果 ptr指向一个地址为 1000 的字符(前提是该操作系统一个字符占1个字节),上面的运算会导致指针指向位置
1001,因为下一个字符位置是在 1001。
递增一个指针
我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
#include <iostream> using namespace std; const int len = 3; int main(){ int arr[len] = {52,6,7}; int *p; // 数组名即数组首元素地址; p = arr; for (int i = 0; i < len; ++i) { // 打印地址 cout << p << endl; // 解引用 打印指向的整型变量值 cout << *p << endl; // 指针移动 p++; } return 0; }
#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; // 指针中的数组地址 ptr = var; for (int i = 0; i < MAX; i++) { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 移动到下一个位置 ptr++; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Address of var[0] = 0xbfa088b0 Value of var[0] = 10 Address of var[1] = 0xbfa088b4 Value of var[1] = 100 Address of var[2] = 0xbfa088b8 Value of var[2] = 200
递减一个指针
同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:#include <iostream> using namespace std; const int len = 3; int main(){ int arr[len] = {52,6,7}; int *p; // 取数组最后一个元素的地址 p = &arr[len - 1]; for (int i = len - 1; i >= 0; i--) { // 打印地址 cout << p << endl; // 解引用 打印指向的整型变量值 cout << *p << endl; // 指针移动 p--; } return 0; }
#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; // 指针中最后一个元素的地址 ptr = &var[MAX-1]; for (int i = MAX; i > 0; i--) { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 移动到下一个位置 ptr--; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Address of var[3] = 0xbfdb70f8 Value of var[3] = 200 Address of var[2] = 0xbfdb70f4 Value of var[2] = 100 Address of var[1] = 0xbfdb70f0 Value of var[1] = 10
指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:
牛B,还可以这样玩!
#include <iostream> using namespace std; const int len = 3; int main(){ int arr[len] = {52,6,7}; int *p; // 取数组最后一个元素的地址 p = arr; while(p <= &arr[len - 1]){ cout << *p << endl; // 指针 加1 p++; } return 0; }
#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; // 指针中第一个元素的地址 ptr = var; int i = 0; while ( ptr <= &var[MAX - 1] ) { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 指向上一个位置 ptr++; i++; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Address of var[0] = 0xbfce42d0 Value of var[0] = 10 Address of var[1] = 0xbfce42d4 Value of var[1] = 100 Address of var[2] = 0xbfce42d8 Value of var[2] = 200
C++ 指针 vs 数组
指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。请看下面的程序:#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; // 指针中的数组地址 ptr = var; for (int i = 0; i < MAX; i++) { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 移动到下一个位置 ptr++; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Address of var[0] = 0xbfa088b0 Value of var[0] = 10 Address of var[1] = 0xbfa088b4 Value of var[1] = 100 Address of var[2] = 0xbfa088b8 Value of var[2] = 200
然而,指针和数组并不是完全互换的。例如,请看下面的程序:
#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++; // 这是不正确的,因为var是指向数组首元素地址的「常量!」 } return 0; }
把指针运算符 * 应用到 var 上是完全可以接受的,但修改 var 的值是非法的。这是因为 var 是一个指向数组开头的常量,不能作为左值。
由于一个数组名对应一个指针常量,只要不改变指针所存放的地址的值,仍然可以用指针形式的表达式。
即可以通过解引用*来改其所指向的地址或(经过一定偏移后的地址)所存放的值,但不能改变其本身所存放的地址的值!
例如,下面是一个有效的语句,把 var[2] 赋值为 500:
*(var + 2) = 500;
上面的语句是有效的,且能成功编译,因为 var 未改变。
#include <iostream> using namespace std; const int len = 3; int main(){ int arr[len] = {52,6,7}; // 将第一个元素的值设置为67; *arr = 1314; cout << arr[0] << endl;//1314 // 以下试图改变数组首元素的地址的操作 是错误的 arr++; /*error: cannot increment value of type 'int [3]' arr++; ~~~^ */ // 但是,可以对数组首元素地址偏移,以改变数组中元素的值 // 例如,将arr[1]改成67 *(arr + 1) = 67; cout << arr[1] << endl;//67 return 0; }
C++ 指针数组 (一个数组,里面的元素是指针)
在我们讲解指针数组的概念之前,先让我们来看一个实例,它用到了一个由 3 个整数组成的数组:#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; for (int i = 0; i < MAX; i++) { cout << "Value of var[" << i << "] = "; cout << var[i] << endl; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of var[0] = 10 Value of var[1] = 100 Value of var[2] = 200
可能有一种情况,我们想要让数组存储指向 int 或 char 或其他数据类型的指针。下面是一个指向整数的指针数组的声明:
int *ptr[MAX];
在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:
#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
int var[MAX] = {10, 100, 200};
int *ptr[MAX];
for (int i = 0; i < MAX; i++)
{
ptr[i] = &var[i]; // 赋值为整数的地址
}
for (int i = 0; i < MAX; i++)
{
cout << "Value of var[" << i << "] = ";
cout << *ptr[i] << endl;
}
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Value of var[0] = 10 Value of var[1] = 100 Value of var[2] = 200
#include <iostream> using namespace std; const int len = 3; int main(){ int arr[len] = {52,6,7}; // 定义一个指针数组,用来存放所有元素的地址 int *p_arr[len]; for (int i = 0; i < len; ++i) { p_arr[i] = &arr[i]; } // 通过数组指针,打印元素值 for (int i = 0; i < len; ++i) { cout << *(p_arr[i]) << endl; } return 0; }
您也可以用一个指向字符的指针数组来存储一个字符串列表,如下:
#include <iostream> using namespace std; const int MAX = 4; int main () { // 之所以要加*,变成指针数组,是因为里面的元素个个都是人才(指针),里面的人才(指针)个个都指向了一个字符串串,const必须有,否则报FBI Warning const char *names[MAX] = { "Zara Ali", "Hina Ali", "Nuha Ali", "Sara Ali", }; for (int i = 0; i < MAX; i++) { cout << "Value of names[" << i << "] = "; cout << names[i] << endl; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of names[0] = Zara Ali Value of names[1] = Hina Ali Value of names[2] = Nuha Ali Value of names[3] = Sara Ali
#include <iostream> using namespace std; const int len = 3; int main(){ //之所以要加*,变成指针数组,是因为里面的元素个个都是人才(指针),里面的人才(指针)个个都指向了一个字符串串 const char *filmArr[len] = {"未闻花名","龙与虎","k-on"} ; // 打印 for (int i = 0; i < len; ++i) { // 两者等价 cout << *(filmArr + i) << endl; cout << filmArr[i] << endl; } return 0; }
C++ 指向指针的指针(多级间接寻址)
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:
int **var;
访问值需要使用两个星号运算符进行解引用,如下面实例所示:
#include <iostream> using namespace std; int main () { int var; int *ptr; int **pptr; var = 3000; // 获取 var 的地址 ptr = &var; // 使用运算符 & 获取 ptr 的地址 pptr = &ptr; // 使用 pptr 获取值 cout << "Value of var :" << var << endl; cout << "Value available at *ptr :" << *ptr << endl; cout << "Value available at **pptr :" << **pptr << endl; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of var = 3000 Value available at *ptr = 3000 Value available at **pptr = 3000
C++ 传递指针给函数
C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:
#include <iostream> #include <ctime> using namespace std; void getSeconds(unsigned long *par); int main () { unsigned long sec; getSeconds( &sec ); // 输出实际值 cout << "Number of seconds :" << sec << endl; return 0; } void getSeconds(unsigned long *par) { // 获取当前的秒数 *par = time( NULL ); return; }
当上面的代码被编译和执行时,它会产生下列结果:
Number of seconds :1294450468
#include <iostream> #include <ctime> using namespace std; // 声明函数,参数是指针,接收地址 void getTimestamp(unsigned long *stamp); int main(){ unsigned long currentTimeStamp; // 将地址传递给指针,目的是让其在函数内部赋值 getTimestamp(¤tTimeStamp); // 1616734145 cout << currentTimeStamp << endl; return 0; } // 定义函数 void getTimestamp(unsigned long *stamp) { // 解引用,为外部变量赋值 *stamp = time(NULL); }
能接受指针作为参数的函数,自然也能接受数组作为参数,如下所示:
#include <iostream> using namespace std; // 函数声明 double getAverage(int *arr, int size); int main () { // 带有 5 个元素的整型数组 int balance[5] = {1000, 2, 3, 17, 50}; double avg; // 传递一个指向数组的指针作为参数 avg = getAverage( balance, 5 ) ; // 输出返回值 cout << "Average value is: " << avg << endl; return 0; } double getAverage(int *arr, int size) { int i, sum = 0; double avg; for (i = 0; i < size; ++i) { sum += arr[i]; } avg = double(sum) / size; return avg; }
当上面的代码被编译和执行时,它会产生下列结果:
Average value is: 214.4
#include <iostream> using namespace std; // 获取一维数组的长度 #define count_of_arr(arr) (sizeof((arr)) / sizeof(arr)[0]) // 声明函数,参数是指针,接收数组首地址 double getAverage(int *arrFirstElementAddress,int len); int main(){ int arr[] = {520,6,7}; cout << getAverage(arr,count_of_arr(arr)) << endl; // 输出结果:177.667 return 0; } // 定义函数 double getAverage(int *arrFirstElementAddress,int len) { int sum = 0; for (int i = 0; i < len; ++i) { sum += arrFirstElementAddress[i]; } return double(sum)/len; }
C++ 从函数返回指针
在上一章中,我们已经了解了 C++ 中如何从函数返回数组,类似地,C++ 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示:
int * myFunction() { . . . }
另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:
#include <iostream> #include <ctime> using namespace std; const int len = 3; // 声明函数,返回一个随机数组成的数组;返回值是一个指针,存放的是数组元素的首地址 int *getRandomArr(); int main(){ int *p = getRandomArr(); for (int i = 0; i < len; ++i) { // 解引用 cout << *(p + i) << endl; } return 0; } // 定义函数 int *getRandomArr() { // 要将数组返回出去给调用者使用,就必须加static关键字 static int arr[len]; srand(time(NULL)); for (int i = 0; i < len; ++i) { // 为数组元素赋值 arr[i] = rand(); } return arr; }
#include <iostream> #include <ctime> using namespace std; // 要生成和返回随机数的函数 int * getRandom( ) { static int r[10]; // 设置种子 srand( (unsigned)time( NULL ) ); for (int i = 0; i < 10; ++i) { r[i] = rand(); cout << r[i] << endl; } return r; } // 要调用上面定义函数的主函数 int main () { // 一个指向整数的指针 int *p; p = getRandom(); for ( int i = 0; i < 10; i++ ) { cout << "*(p + " << i << ") : "; cout << *(p + i) << endl; } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
624723190 1468735695 807113585 976495677 613357504 1377296355 1530315259 1778906708 1820354158 667126415 *(p + 0) : 624723190 *(p + 1) : 1468735695 *(p + 2) : 807113585 *(p + 3) : 976495677 *(p + 4) : 613357504 *(p + 5) : 1377296355 *(p + 6) : 1530315259 *(p + 7) : 1778906708 *(p + 8) : 1820354158 *(p + 9) : 667126415
C++ 引用
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量的别名后,就可以使用该引用名称或变量名称来指向变量。
C++ 引用 vs 指针
引用很容易与指针混淆,它们之间有三个主要的不同:不存在空引用。引用必须连接到一块合法的内存。
一旦引用被初始化为一个对象,就不能被指向到另一个对象(不可以见异思迁)。指针可以在任何时候指向到任何对象。
引用必须在创建时被初始化。指针可以在任何时间被初始化。
C++ 中创建引用
试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或 引用 来访问变量的内容。例如:
int i = 17;
我们可以为 i 声明引用变量,如下所示:
int& alias = i;
在这些声明中,& 读作引用。因此,第一个声明可以读作 "alias 是一个初始化为 i 的整型引用",下面的实例使用了 int 和 double 引用:
#include <iostream> using namespace std; int main () { // 声明简单的变量 int i; double d; // 声明引用变量 int& r = i; double& s = d; i = 5; cout << "Value of i : " << i << endl; cout << "Value of i reference : " << r << endl; d = 11.7; cout << "Value of d : " << d << endl; cout << "Value of d reference : " << s << endl; return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of i : 5 Value of i reference : 5 Value of d : 11.7 Value of d reference : 11.7
#include <iostream> using namespace std; int main(){ int girl = 67; // 创建引用(别名,外号,昵称) int &monkey = girl; // 通过引用(别名,外号,昵称)输出 cout << monkey << endl; return 0; }
引用通常用于函数参数列表和函数返回值。下面列出了 C++ 程序员必须清楚的两个与 C++ 引用相关的重要概念:
概念 | 描述 |
---|---|
把引用作为参数 | C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。 |
把引用作为返回值 | 可以从 C++ 函数中返回引用,就像返回其他数据类型一样。 |
C++ 把引用作为参数
我们已经讨论了如何使用指针来实现引用调用函数。下面的实例使用了引用来实现引用调用函数。???#include <iostream> using namespace std; // 函数声明 void swap(int& x, int& y); int main () { // 局部变量声明 int a = 100; int b = 200; cout << "交换前,a 的值:" << a << endl; cout << "交换前,b 的值:" << b << endl; /* 调用函数来交换值 */ swap(a, b); cout << "交换后,a 的值:" << a << endl; cout << "交换前,b 的值:" << b << endl; return 0; } // 函数定义 void swap(int& x, int& y) { int temp; temp = x; /* 保存地址 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return; }
当上面的代码被编译和执行时,它会产生下列结果:
交换前,a 的值: 100 交换前,b 的值: 200 交换后,a 的值: 200 交换后,b 的值: 100
#include <iostream> using namespace std; // 声明函数:通过引用来改变外部 变量的值 void swap(int &a,int &b); int main(){ int var_1 = 6; int var_2 = 7; swap(var_1,var_2); cout << var_1 << "," << var_2 << endl; return 0; } // 定义函数:通过引用来改变外部 变量的值 void swap(int &a,int &b) { int temp = a; a = b; b = temp; }
C++ 把引用作为返回值
通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。
当函数返回一个引用时,则返回一个指向返回值的隐式指针。
这样,函数就可以放在赋值语句的左边。
例如,请看下面这个简单的程序:
#include <iostream> #include <ctime> 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; }
当上面的代码被编译和执行时,它会产生下列结果:
改变前的值 vals[0] = 10.1 vals[1] = 12.6 vals[2] = 33.1 vals[3] = 24.1 vals[4] = 50 改变后的值 vals[0] = 10.1 vals[1] = 20.23 vals[2] = 33.1 vals[3] = 70.8 vals[4] = 50
当返回一个引用时,要注意被引用的对象不能超出作用域。
所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() { int q; //! return q; // 在编译时发生错误 static int x; return x; // 安全,x 在函数作用域外依然是有效的 }
#include <iostream> using namespace std; // 获取一维数组的长度 #define count_of_arr(arr) (sizeof((arr)) / sizeof(arr)[0]) // 定义全局数组 double arr[] = {520,6,7,13.14}; // 声明函数,返回值是数组的引用 double &getElementOfIndex(int index); int main(){ for (int index = 0; index < count_of_arr(arr); ++index) { // 根据函数返回的引用,进行对数组元素进行打印 // 牛逼了,还可以左值 cout << getElementOfIndex(index) << endl; } // 修改特定下标的元素,先取,再赋值 getElementOfIndex(3) = getElementOfIndex(3) * 10; cout << getElementOfIndex(3) << endl; // 131.4 return 0; } // 定义函数,返回值是数组指定索引的元素的引用(别名) double &getElementOfIndex(int index) { return arr[index]; }
C++ 日期 & 时间
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime> 头文件。
有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。
类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。
结构类型 tm 把日期和时间以 C 结构的形式保存,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; // 夏令时 中国已经取消夏令时 }
下面是 C/C++ 中关于日期和时间的重要函数。所有这些函数都是 C/C++ 标准库的组成部分,您可以在 C++ 标准库中查看一下各个函数的细节。
序号 | 函数 & 描述 |
---|---|
1 | time_t time(time_t *time); 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 .1。 |
2 | char *ctime(const time_t *time); //参数是指针 该返回一个表示当地时间的字符串指针,字符串形式 day month year hours:minutes:seconds year\n。 |
3 | struct tm *localtime(const time_t *time); 该函数返回一个指向表示本地时间的 tm 结构的指针。 |
4 | clock_t clock(void); 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。 |
5 | char * asctime ( const struct tm * time ); 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n。 |
6 | struct tm *gmtime(const time_t *time); 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 |
7 | time_t mktime(struct tm *time); 该函数返回日历时间,相当于 time 所指向结构中存储的时间。 |
8 | double difftime ( time_t time2, time_t time1 ); 该函数返回 time1 和 time2 之间相差的秒数。 |
9 | size_t strftime(); 该函数可用于格式化日期和时间为指定的格式。 |
当前日期和时间
下面的实例获取当前系统的日期和时间,包括本地时间和协调世界时(UTC)。#include <iostream> #include <ctime> using namespace std; int main(){ // 获取当前时间 long timestamp = time(0); // 打印时间戳 cout << timestamp << endl; // 把time_t类型转换成字符串,参数是取地址 char *timeStr = ctime(×tamp); // Fri June 07 15:25:15 2018 // ----------------------------- // 把long类型的时间戳 转换成 tm结构体??? // 参数是时间戳的地址,返回一个指向 time结构体 的指针 tm *pointOfTimeStruct = gmtime(×tamp); // 参数是一个指向结构体的指针,返回一个指向字符串的指针 timeStr = asctime(pointOfTimeStruct); // Fri June 07 15:25:15 2018 return 0; }
#include <iostream> #include <ctime> using namespace std; int main( ) { // 基于当前系统的当前日期/时间 time_t now = time(0); // 把 now 转换为字符串形式 char* dt = ctime(&now); cout << "本地日期和时间:" << dt << endl; // 把 now 转换为 tm 结构 tm *gmtm = gmtime(&now); dt = asctime(gmtm); cout << "UTC 日期和时间:"<< dt << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
本地日期和时间:Sat Jan 8 20:07:41 2011 UTC 日期和时间:Sun Jan 9 03:07:41 2011
使用结构 tm 格式化时间
tm 结构在 C/C++ 中处理日期和时间相关的操作时,显得尤为重要。tm 结构以 C 结构的形式保存日期和时间。大多数与时间相关的函数都使用了 tm 结构。下面的实例使用了 tm 结构和各种与日期和时间相关的函数。在练习使用结构之前,需要对 C 结构有基本的了解,并懂得如何使用箭头 -> 运算符来访问结构成员。
#include <iostream> #include <ctime> using namespace std; int main( ) { // 基于当前系统的当前日期/时间 time_t now = time(0); cout << "Number of sec since January 1,1970:" << now << endl; tm *ltm = localtime(&now); // 输出 tm 结构的各个组成部分 cout << "Year: "<< 1900 + ltm->tm_year << endl; cout << "Month: "<< 1 + ltm->tm_mon<< endl; cout << "Day: "<< ltm->tm_mday << endl; cout << "Time: "<< 1 + ltm->tm_hour << ":"; cout << 1 + ltm->tm_min << ":"; cout << 1 + ltm->tm_sec << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
Number of sec since January 1, 1970:1294548238 Year: 2011 Month: 1 Day: 8 Time: 22: 44:59
#include <iostream> #include <ctime> using namespace std; int main(){ long timestamp = time(0); // 根据时间戳获取本地的时间 tm *pointToTimeStruct = localtime(×tamp); // 输出结构体的各个成员 cout << pointToTimeStruct -> tm_year << endl; // 月份,从0算起 cout << pointToTimeStruct -> tm_mon << endl; cout << pointToTimeStruct -> tm_mday << endl; cout << pointToTimeStruct -> tm_hour << endl; cout << pointToTimeStruct -> tm_min << endl; // 有61秒 cout << pointToTimeStruct -> tm_sec << endl; return 0; }
C++ 基本的输入输出
C++ 标准库提供了一组丰富的输入/输出功能,我们将在后续的章节进行介绍。本章将讨论 C++ 编程中最基本和最常见的 I/O 操作。C++ 的 I/O 发生在流中,流是字节序列。如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做输入操作。如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做输出操作。
I/O 库头文件
下列的头文件在 C++ 编程中很重要。头文件 | 函数和描述 |
---|---|
<iostream> | 该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 |
<iomanip> | manipulate,该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 |
<fstream> | 该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 |
标准输出流(cout)
预定义的对象 cout 是 ostream 类的一个实例。cout对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的,如下所示:
#include <iostream> using namespace std; int main( ) { char str[] = "Hello C++"; cout << "Value of str is : " << str << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
Value of str is : Hello C++
#include <iostream> using namespace std; int main(){ char name[] = "aaaaaaa"; cout << name << endl; // 之所以要加*,变成指针数组,是因为里面的元素个个都是人才(指针),里面的人才(指针)个个都指向了一个字符串串 // const必须有,否则报FBI Warning const char *nameArr[] = {"bbbb","cccc","ddddd"}; for (int i = 0; i < 3; ++i) { cout << nameArr[i] << endl; } }
C++ 编译器根据要输出变量的数据类型,选择合适的流插入运算符来显示值。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。
流插入运算符 << 在一个语句中可以多次使用,如上面实例中所示,endl 用于在行末添加一个换行符。
标准输入流(cin)
预定义的对象 cin 是 istream 类的一个实例。cin对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用的,如下所示:
#include <iostream> using namespace std; int main( ) { char name[50]; cout << "请输入您的名称: "; cin >> name; cout << "您的名称是: " << name << endl; }
当上面的代码被编译和执行时,它会提示用户输入名称。当用户输入一个值,并按回车键,就会看到下列结果:
请输入您的名称: cplusplus 您的名称是: cplusplus
C++ 编译器根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中。
流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:
cin >> name >> age;
这相当于下面两个语句:
cin >> name; cin >> age;
#include <iostream> using namespace std; int main(){ char name[50]; cout << "give me your cute name:"; cin >> name; cout << "your cute name is :" << name << endl; cout << "give me your age and sex:"; int age; char sex[10]; cin >> age >> sex; cout << "your age is:" << age << ",your sex is :" << sex << endl; }
标准错误流(cerr)
预定义的对象 cerr 是 ostream 类的一个实例。cerr对象附属到标准错误设备,通常也是显示屏,但是 cerr对象是非缓冲的,且每个流插入到 cerr 都会立即输出。???Excuse Me???
cout的输出可以重定向到一个文件中,而cerr必须输出在显示器上。
cerr 也是与流插入运算符 << 结合使用的,如下所示:
#include <iostream> using namespace std; int main( ) { char str[] = "Unable to read...."; cerr << "Error message : " << str << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
Error message : Unable to read....
标准日志流(clog)
预定义的对象 clog 是 ostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog对象是缓冲的。
这意味着每个流插入到 clog 都会先存储在缓冲里,直到缓冲填满或者缓冲区刷新时才会输出。
clog 也是与流插入运算符 << 结合使用的,如下所示:
#include <iostream> using namespace std; int main( ) { char str[] = "Unable to read...."; clog << "Error message : " << str << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
Error message : Unable to read....
通过这些小实例,我们无法区分 cout、cerr 和 clog 的差异,但在编写和执行大型程序时,它们之间的差异就变得非常明显。所以良好的编程实践告诉我们,使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。
C++ 数据结构
C/C++ 数组允许定义可存储相同类型数据项的变量,但是结构是 C++中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。
结构用于表示一条记录,假设您想要记录看过的每一部动漫,您可能需要记录下列属性:
Anime ID// 动漫ID
Title // 动漫名称
Content // 观看心得
Img_Url // 图片地址
定义结构
为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:struct [structure tag] { member definition; member definition; ... member definition; } [one or more structure variables];
structure tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。
在结构定义的末尾,最后一个分号之前,您可以指定一个或多个结构变量,这是可选的。
下面是声明 Anime 结构的方式:
struct Anime { int anime_id; char title[67]; char content[255]; char img_url[67];
}anime;
访问结构成员
为了访问结构的成员,我们使用成员访问运算符(.)。成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个半角句号。您可以使用 struct 关键字来定义结构类型的变量。下面的实例演示了结构的用法:#include <iostream> #include <cstring> // io代表输入输出,manip是manipulator(操纵器)的缩写 #include <iomanip> using namespace std; // 仅仅定义一个结构体 struct Anime { int anime_id; char title[67]; char actress[67]; char content[255]; char img_url[67]; }; int main(){ // 声明2个结构体变量(两个神作) struct Anime shenzuo_1; struct Anime shenzuo_2; // 给神作1赋值 shenzuo_1.anime_id = 267; strcpy(shenzuo_1.title,"未闻花名"); strcpy(shenzuo_1.actress,"面码"); strcpy(shenzuo_1.content,"我们仍未知道那天所看到的花的名字"); strcpy(shenzuo_1.img_url,"https://www.baidu.com/anohana.jpg"); // 给神作2赋值 shenzuo_2.anime_id = 268; strcpy(shenzuo_2.title,"龙与虎"); strcpy(shenzuo_2.actress,"逢坂大河"); strcpy(shenzuo_2.content,"这个是龙与虎的介绍"); strcpy(shenzuo_2.img_url,"https://www.baidu.com/tiger.jpg"); // 输出 神作2的 结构体成员 cout << shenzuo_1.anime_id; // 1个空格 ,未闻花名四个字,每个字占3格 cout << setw(1 + 4*3) << shenzuo_1.title; // 1个空格 ,面码两个字,每个字占3格 cout << setw(1 + 2*3) << shenzuo_1.actress; cout << setw(1 + 16*3) << shenzuo_1.content; cout << endl; // 输出 神作1的 结构体成员 cout << shenzuo_2.anime_id; cout << setw(1 + 3*3) << shenzuo_2.title; cout << setw(1 + 4*3) << shenzuo_2.actress; cout << setw(1 + 9*3) << shenzuo_2.content; cout << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
构作为函数参数
您可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。您可以使用上面实例中的方式来访问结构变量:
#include <iostream> #include <cstring> // io代表输入输出,manip是manipulator(操纵器)的缩写 #include <iomanip> using namespace std; // 仅仅定义一个结构体 struct Anime { int anime_id; char title[67]; char actress[67]; char content[255]; char img_url[67]; }; // 声明一个函数 void printAnime(struct Anime shenzuo); int main(){ // 声明2个结构体变量(两个神作) struct Anime shenzuo_1; struct Anime shenzuo_2; // 给神作1赋值 shenzuo_1.anime_id = 267; strcpy(shenzuo_1.title,"未闻花名"); strcpy(shenzuo_1.actress,"面码"); strcpy(shenzuo_1.content,"我们仍未知道那天所看到的花的名字"); strcpy(shenzuo_1.img_url,"https://www.baidu.com/anohana.jpg"); // 给神作2赋值 shenzuo_2.anime_id = 268; strcpy(shenzuo_2.title,"龙与虎"); strcpy(shenzuo_2.actress,"逢坂大河"); strcpy(shenzuo_2.content,"这个是龙与虎的介绍"); strcpy(shenzuo_2.img_url,"https://www.baidu.com/tiger.jpg"); // 调用函数,打印结构体成员信息 printAnime(shenzuo_1); printAnime(shenzuo_2); } // 定义一个函数 void printAnime(struct Anime shenzuo) { // 输出 神作2的 结构体成员 cout << shenzuo.anime_id; // 1个空格 ,未闻花名四个字,每个字占3格 cout << setw(1 + strlen(shenzuo.title)) << shenzuo.title; // 1个空格 ,面码两个字,每个字占3格 cout << setw(1 + strlen(shenzuo.actress)) << shenzuo.actress; cout << setw(1 + strlen(shenzuo.content)) << shenzuo.content; cout << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
指向结构的指针
您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:struct Books *struct_pointer;
现在,您可以在上述定义的指针变量中存储结构变量的地址。
为了取得结构变量的地址,请把 & 运算符放在结构名称的前面,如下所示:
struct_pointer = &Book1;
为了使用指向该结构的指针访问结构的成员,您必须使用指针 -> 运算符,如下所示:
struct_pointer->title;
让我们使用结构指针来重写上面的实例,这将有助于您理解结构指针的概念:
struct_pointer->title;
让我们使用结构指针来重写上面的实例,这将有助于您理解结构指针的概念
#include <iostream> #include <cstring> // io代表输入输出,manip是manipulator(操纵器)的缩写 #include <iomanip> using namespace std; // 仅仅定义一个结构体 struct Anime { int anime_id; char title[67]; char actress[67]; char content[255]; char img_url[67]; }; // 声明一个函数(参数是指针,指向一个结构体变量) void printAnime(struct Anime *point_of_shenzuo); int main(){ // 声明2个结构体变量(两个神作) struct Anime shenzuo_1; struct Anime shenzuo_2; // 给神作1赋值 shenzuo_1.anime_id = 267; strcpy(shenzuo_1.title,"未闻花名"); strcpy(shenzuo_1.actress,"面码"); strcpy(shenzuo_1.content,"我们仍未知道那天所看到的花的名字"); strcpy(shenzuo_1.img_url,"https://www.baidu.com/anohana.jpg"); // 给神作2赋值 shenzuo_2.anime_id = 268; strcpy(shenzuo_2.title,"龙与虎"); strcpy(shenzuo_2.actress,"逢坂大河"); strcpy(shenzuo_2.content,"这个是龙与虎的介绍"); strcpy(shenzuo_2.img_url,"https://www.baidu.com/tiger.jpg"); // 调用函数,打印结构体成员信息 printAnime(&shenzuo_1); printAnime(&shenzuo_2); } // 定义一个函数(参数是指针,指向一个结构体变量) void printAnime(struct Anime *point_of_shenzuo) { // 输出 神作的 结构体成员 cout << point_of_shenzuo->anime_id; // 1个空格 ,未闻花名四个字,每个字占3格 cout << setw(1 + strlen(point_of_shenzuo->title)) << point_of_shenzuo->title; // 1个空格 ,面码两个字,每个字占3格 cout << setw(1 + strlen(point_of_shenzuo->actress)) << point_of_shenzuo->actress; cout << setw(1 + strlen(point_of_shenzuo->content)) << point_of_shenzuo->content; cout << endl; }
当上面的代码被编译和执行时,它会产生下列结果:
typedef 关键字
下面是一种更简单的定义结构的方式,您可以为创建的类型取一个"别名"。例如:typedef struct { char title[50]; char author[50]; char subject[100]; int book_id; }Books;
现在,您可以直接使用 Books 来定义 Books 类型的变量,而不需要使用
struct 关键字(可以少写一个单词)。下面是实例:
Books Book1, Book2;
您可以使用 typedef 关键字来定义非结构类型,如下所示:
typedef long int *pint32; pint32 x, y, z;
x, y 和 z 都是指向长整型 long int 的指针。
重新修改如下:
#include <iostream> #include <cstring> // io代表输入输出,manip是manipulator(操纵器)的缩写 #include <iomanip> using namespace std; // 仅仅定义一个结构体 typedef struct { int anime_id; char title[67]; char actress[67]; char content[255]; char img_url[67]; }Anime; // 声明一个函数(参数是指针,指向一个结构体变量) void printAnime(Anime shenzuo); int main(){ // 声明2个结构体变量(两个神作) Anime shenzuo_1; Anime shenzuo_2; // 给神作1赋值 shenzuo_1.anime_id = 267; strcpy(shenzuo_1.title,"未闻花名"); strcpy(shenzuo_1.actress,"面码"); strcpy(shenzuo_1.content,"我们仍未知道那天所看到的花的名字"); strcpy(shenzuo_1.img_url,"https://www.baidu.com/anohana.jpg"); // 给神作2赋值 shenzuo_2.anime_id = 268; strcpy(shenzuo_2.title,"龙与虎"); strcpy(shenzuo_2.actress,"逢坂大河"); strcpy(shenzuo_2.content,"这个是龙与虎的介绍"); strcpy(shenzuo_2.img_url,"https://www.baidu.com/tiger.jpg"); // 调用函数,打印结构体成员信息 printAnime(shenzuo_1); printAnime(shenzuo_2); } // 定义一个函数(参数是指针,指向一个结构体变量) void printAnime(Anime shenzuo) { // 输出 神作的 结构体成员 cout << shenzuo.anime_id; // 1个空格 ,未闻花名四个字,每个字占3格 cout << setw(1 + strlen(shenzuo.title)) << shenzuo.title; // 1个空格 ,面码两个字,每个字占3格 cout << setw(1 + strlen(shenzuo.actress)) << shenzuo.actress; cout << setw(1 + strlen(shenzuo.content)) << shenzuo.content; cout << endl; }运行效果如下:
相关文章推荐
- C++学习笔记(一) 基础语法 —参考阿发你好
- C++Simplify02-基础语法
- Java千百问_03基础语法(018)_注释是什么
- 学习笔记_JavaSE_04_Java基础语法03
- C/C++_lesson1_C++语法基础
- c++复习基础要点03 虚析构函数、虚函数表
- 一个基础但是隐晦的c++语法问题
- C++语法基础--句柄类
- 【C++基础03】C++对C的加强
- Java千百问_03基础语法(017)_static有什么用
- C++语法基础--优先队列--priority_queue
- 《专题:C++语法基础》篇二:C++中的数据类型
- 01_03 JSP基础语法之page指令2(数据库的连接操作)
- C++语法基础--标准库类型--vector
- Java千百问_03基础语法(012)_transient关键字有什么用
- 【C++基础 03】do...while(0)妙用
- 第五天03 JAVA基础语法(认识对象--使用标准类)(学习笔记)
- C++语法基础--动态绑定,派生类,虚函数,覆盖虚函数机制
- C++语法基础--恢复继承成员的访问级别,默认继承保护级别,友元关系与继承,继承与静态成员
- Java千百问_03基础语法(015)_System.exit(0)有什么用