1,指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch; /* 一个字符型的指针 */
指针的大小是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
指针使用实例:
#include <iostream> using namespace std; int main () { int var = 20; // 实际变量的声明 int *ip; // 指针变量的声明,只声明未初始化 ip = &var; // 初始化,在指针变量中存储 var 的地址 cout << "Value of var variable: "; cout << var << endl; //原变量的值是20 // 输出在指针变量中存储的地址 cout << "Address stored in ip variable: "; cout << ip << endl; //输出的是存储的地址,是地址 // 访问指针中地址的值 cout << "Value of *ip variable: "; cout << *ip << endl; //加×以后输出的是地址区域对应的内容是值,这叫指针 return 0; }
指针可以为空:
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
#include <iostream> using namespace std; int main () { int *ptr = NULL; cout << "ptr 的值是 " << ptr ; 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] = 0x7ffe3c8e3980
Value of var[0] = 10
Address of var[1] = 0x7ffe3c8e3984
Value of var[1] = 100
Address of var[2] = 0x7ffe3c8e3988
Value of var[2] = 200
指针可以比较大小:
#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] ) //指针只要比var[2]小就继续向下移动 { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 指向上一个位置 ptr++; //指针向下一个元素移动,数组中的内存地址是有讲究的,它们按顺序排列,所以可以通过++的方式获取下一个元素 i++; } return 0; }
指针数组:
个人觉得指针数组有两种,一种是创建一个数组用以存储多个指针,如下:
#include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr[MAX]; //创建一个指针数组,长度为3 for (int i = 0; i < MAX; i++) { ptr[i] = &var[i]; // 把指针数组中的元素挨个赋值为var数组中元素的地址 } 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 MAX = 4; int main () { 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; //此处要注意,上一个例子打印出内存地址对应的值用的是:*数组名[索引号],而此处不能这样用,原因不明,但是如果用*name[i]打印出来的将会是ZHNS,首字母,大约是因为指针指向数组的首个吧,但是name[i]为何能打印出值来,就不得而知了 } 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
指针可以指向指针:
声明:
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 << "var 值为 :" << var << endl; //3000 cout << "*ptr 值为:" << *ptr << endl; //3000 cout << "**pptr 值为:" << **pptr << endl; //3000 return 0; }
指针传递给函数:
只要声明函数的时候说明是指针就可以了
#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
从函数返回指针:
首先要定义一个返回指针的函数,其次要知道,c++不支持返回纯局部变量,除非局部变量为static
#include <iostream> #include <ctime> #include <cstdlib> using namespace std; // 要生成和返回随机数的函数 int * getRandom( ) { static int r[10]; // 设置种子 srand( (unsigned)time( NULL ) ); for (int i = 0; i < 10; ++i) //数组是长度为10的int数组 { 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; }
2,引用
引用约等于浅拷贝,是原来的变量的别名,所以引用不能为空,引用不能改变,引用必须在创建的时候被初始化,函数不能返回一个局部变量的引用,static例外
比如:
int i = 17; int& r = i; //r 是一个初始化为 i 的整型引用
实例:
#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; }
函数返回一个引用实例:
#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; }
返回:
改变前的值
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
引用也可以作为参数:
声明函数的时候这样做就可以了:
void swap(int& x, int& y);
3,日期和时间
c++比c多了处理日期和时间的相关函数和结构,可引用<ctime>,有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。
序号 | 函数 & 描述 |
---|---|
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 。 |
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 。 |
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(); 该函数可用于格式化日期和时间为指定的格式。 |
实例来一波:
#include <iostream> #include <ctime> using namespace std; int main( ) { // 基于当前系统的当前日期/时间,time_t是ctime中类似于数据类型的一种结构,而time()返回的是1990年至今有多少秒 time_t now = time(0); // 把 now 转换为字符串形式,字符串dt是对时间now的一种引用 char* dt = ctime(&now); cout << "本地日期和时间:" << dt << endl; // 把 now 转换为 tm 结构 tm *gmtm = gmtime(&now); //这个看起来是指针,函数返回一个指向time的指针 dt = asctime(gmtm); //据说是格林尼治的时间 cout << "UTC 日期和时间:"<< dt << endl; }
输出格式化的当前时间:
//这个完全看不懂 #include <iostream> #include <ctime> #include <stdlib.h> #include <stdio.h> using namespace std; string Get_Current_Date(); int main( ) { // 将当前日期以 20** - ** - ** 格式输出 cout << Get_Current_Date().c_str() << endl; getchar(); return 0; } string Get_Current_Date() { time_t nowtime; nowtime = time(NULL); //获取日历时间 char tmp[64]; strftime(tmp,sizeof(tmp),"%Y-%m-%d %H:%M:%S",localtime(&nowtime)); return tmp; }
4,输入和输出
I/O库重要的头文件:
头文件 | 函数和描述 |
---|---|
<iostream> | 该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 |
<iomanip> | 该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 |
<fstream> | 该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 |
标准输出流:cout
#include <iostream> using namespace std; int main( ) { char str[] = "Hello C++"; cout << "Value of str is : " << str << endl; }
标准输入流:
#include <iostream> using namespace std; int main( ) { char name[50]; cout << "请输入您的名称: "; cin >> name; //用户的输入赋值给name,类似于python的input cout << "您的名称是: " << name << endl; }
标准错误流:cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。不大明白用法,且先看吧
#include <iostream> using namespace std; int main( ) { char str[] = "Unable to read...."; cerr << "Error message : " << str << endl; }
标准日志流:clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
#include <iostream> using namespace std; int main( ) { char str[] = "Unable to read...."; clog << "Error message : " << str << endl; }
良好的编程实践告诉我们,使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。
5,数据结构
C/C++ 数组允许定义可存储相同类型数据项的变量,但是结构是 C++ 中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。
可用struct定义结构,以下是结构实例:
struct Books { char title[50]; char author[50]; char subject[100]; int book_id; } book; //声明一个结构体类型 Books,变量为 book:
用英文点号访问结构成员,
#include <iostream> #include <cstring> using namespace std; // 声明一个结构体类型 Books struct Books { char title[50]; char author[50]; char subject[100]; int book_id; }; int main( ) { Books Book1; // 定义结构体类型 Books 的变量 Book1 Books Book2; // 定义结构体类型 Books 的变量 Book2 // Book1 详述 strcpy( Book1.title, "C++ 教程"); //strcpy字符串拷贝命令,把第二个字符串的值赋值给第一个,相当于给books结构的book1实例的第一个属性----title赋值为‘c++教程’ strcpy( Book1.author, "Runoob"); strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; // Book2 详述 strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; // 输出 Book1 信息 cout << "第一本书标题 : " << Book1.title <<endl; //输出的时候基本格式是实例加.加结构内容 cout << "第一本书作者 : " << Book1.author <<endl; cout << "第一本书类目 : " << Book1.subject <<endl; cout << "第一本书 ID : " << Book1.book_id <<endl; // 输出 Book2 信息 cout << "第二本书标题 : " << Book2.title <<endl; cout << "第二本书作者 : " << Book2.author <<endl; cout << "第二本书类目 : " << Book2.subject <<endl; cout << "第二本书 ID : " << Book2.book_id <<endl; return 0; }
结构作为函数的参数:
#include <iostream> #include <cstring> using namespace std; void printBook( struct Books book ); //声明一个函数,参数是结构 // 声明一个结构体类型 Books struct Books { char title[50]; char author[50]; char subject[100]; int book_id; }; int main( ) { Books Book1; // 定义结构体类型 Books 的变量 Book1 Books Book2; // 定义结构体类型 Books 的变量 Book2 // Book1 内容赋值 strcpy( Book1.title, "C++ 教程"); strcpy( Book1.author, "Runoob"); strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; // Book2 内容赋值 strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; // 输出 Book1 信息 printBook( Book1 ); //直接把结构传递进去就行了,不用加struct // 输出 Book2 信息 printBook( Book2 ); return 0; } void printBook( struct Books book ) //函数实现 { cout << "书标题 : " << book.title <<endl; cout << "书作者 : " << book.author <<endl; cout << "书类目 : " << book.subject <<endl; cout << "书 ID : " << book.book_id <<endl; }
指向结构的指针:
#include <iostream> #include <cstring> using namespace std; void printBook( struct Books *book ); //函数声明,声明需要一个结构的实例的指针 struct Books //预定义结构的内容 { char title[50]; char author[50]; char subject[100]; int book_id; }; int main( ) { Books Book1; // 定义结构体类型 Books 的变量 Book1 Books Book2; // 定义结构体类型 Books 的变量 Book2 // Book1 详述 strcpy( Book1.title, "C++ 教程"); strcpy( Book1.author, "Runoob"); strcpy( Book1.subject, "编程语言"); Book1.book_id = 12345; // Book2 详述 strcpy( Book2.title, "CSS 教程"); strcpy( Book2.author, "Runoob"); strcpy( Book2.subject, "前端技术"); Book2.book_id = 12346; // 通过传 Book1 的地址来输出 Book1 信息 printBook( &Book1 ); //因为需要的是指针,所以要把book1 book2 的内存地址放进去 // 通过传 Book2 的地址来输出 Book2 信息 printBook( &Book2 ); return 0; } // 该函数以结构指针作为参数 void printBook( struct Books *book ) { cout << "书标题 : " << book->title <<endl; //此处要特别注意,访问指针指向的实例中的结构内容的话,不能继续用点号了,要用->号,不用问为啥,因为是规定 cout << "书作者 : " << book->author <<endl; cout << "书类目 : " << book->subject <<endl; cout << "书 ID : " << book->book_id <<endl; }
补充typedef:
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
//(1)用typedef声明一个新类型名来代替已有的类型名。如 typedef int Status //指定标识符Status代表int类型 typedef double DATE //指定标识符DATE代表double类型 int i; Status i; //等价的 //(2)用typedef对数组类型起新名: typedef int NUM[100];//声明NUM为整数数组类型,可以包含100个元素 NUM n;//定义n为包含100个整数元素的数组,n就是数组名 //(3)对一个结构体类型声明一个新名字: typedef struct //在struct之前用了关键字typedef,表示是声明新类型名 { int month; int day; int year; } TIME; //TIME是新类型名,但不是新类型,也不是结构体变量名,这样就可以用TIME定义该结构体变量,如: TIME birthday; //不用typeof的时候: struct Books //Books是结构体类型 { char title[50]; char author[50]; char subject[100]; int book_id; } book; //book是结构变量