C++入门

时间:Aug. 11, 2021 分类:

目录:

简介

C++是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程

C++由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的,C++进一步扩充和完善了C语言,最初命名为带类的C,后来在1983年更名为C++,C++是C的一个超集,事实上,任何合法的C程序都是合法的C++程序。

静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查

C++面向对象开发有四大特征

  • 封装
  • 抽象
  • 继承
  • 多态

C++组成部分

  • 核心语言:提供了所有构件块,包括变量、数据类型和常量等
  • C++标准库:提供了大量的函数,用于操作文件、字符串等
  • 标准模板库STL:提供了大量方法,用于操作数据结构等

C++的编译遵循ANSI标准,在Mac、UNIX、Windows、Alpha上都能通过编译

C++的标准主要有

  • 2017年的C++17
  • 2014年的C++14
  • 2011年的C++11
  • 2003年的C++03
  • 1998年的C++98

程序结构

#include <iostream>
using namespace std;

// main() 是程序开始执行的地方

int main()
{
   cout << "Hello World"; // 输出 Hello World
   return 0;
}
  • #include <iostream> 定义需要的头文件
  • using namespace std; 告诉编译器使用std的命名空间
  • return 0; 终止main()函数,并调用main()函数,并调用进程返回值为0

编译和执行

$ g++ main.cpp 
$ ./a.out 
Hello, World!

语句

C++以分号为语句结束符

x = y;
y = y+1;
add(x, y);

等价于

x = y; y = y+1; add(x, y);

语句块通过大括号将一组按逻辑连接的语句,例如

{
   cout << "Hello World"; // 输出 Hello World
   return 0;
}

标识符

标识符为[A-Za-z0-9_]+,保留关键字

关键字

  • asm 在C++嵌入汇编代码
  • auto 是存储类型标识符,表明变量"自动"具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型
  • bool 基本数据类型
  • break 跳出循环
  • case 用于switch的条件判断
  • catch 与try用于异常处理
  • char 基本数据类型,0~255的int,代表256个ASCII码,需要''括起来
  • class 类声明
  • const 常量
  • const_cast 常量对象被转换成非常量对象
  • continue 跳出当前次循环
  • default 用于switch的条件默认选项
  • delete 释放程序动态申请的内存空间
  • do do-while循环,和while不同的是至少进入一次循环体
  • double 基本数据类型,以双精度形式存储一个浮点数
  • dynamic_cast 运行时进行动态类型转换,提供了两种转换方式,基类指针转换成派生类指针,指向基类左值转换为派生类引用
  • else 判断
  • enum 枚举类型
  • explicit
  • export
  • extern
  • false bool值
  • float 基本数据结构,精度小于double
  • for 循环结构
  • friend
  • goto 转到
  • if 判断
  • inline
  • int 基本数据结构,整型
  • long 基本数据结构,长整型
  • mutable
  • namespace 命名空间,用于组织类
  • new 新建对象
  • private 访问控制符,只能本类以及友元中访问
  • protected 访问控制符,
  • public 访问控制符,在任何类访问
  • register 寄存器
  • reinterpret_cast
  • return
  • short 基本数据类型,短整形
  • signed 符号数,默认
  • sizeof 获取数据类型占用的字节数
  • static
  • static_cast
  • struct 结构
  • switch 分支语句
  • template 模板,用于泛型
  • this 调用者自身的指针
  • throw 异常处理,抛出异常
  • true
  • try 异常处理机制
  • typedef 类型定义
  • typeid
  • typename
  • union 类似enum
  • unsigned 无符号数,和相反
  • using 指定使用的namespace
  • virtual
  • void 空,可作为函数返回值,可以作为参数
  • volatile 一个可以被外部进程改变的对象
  • wchar_t 宽字符类型,汉字就需要这个类型

详见C关键字

三字符组,略

注释

注释在编译的时候会被忽略,注释有两种

  • // 一般用于单行注释
  • /* ... */ 一般用于多行注释

基本数据类型

内置的基本数据类型

  • 布尔型 bool
  • 字符型 char
  • 整型 int
  • 浮点型 float
  • 双浮点型 double
  • 无类型 void
  • 宽字符型 wchar_t

宽字符型的实现是

typedef short int wchar_t;

一些基本类型可以使用一个或多个类型修饰符进行修饰

变量类型在存储值的时候需要占用内存

类型 范围
char 1个字节 -128到127或者0到255
unsigned char 1个字节 0到255
signed char 1个字节 -128到127
int 4个字节 -2147483648到2147483647
unsigned int 4个字节 0到4294967295
signed int 4个字节 -2147483648到2147483647
short int 2个字节 -32768到32767
unsigned short int 2个字节 0到65535
signed short int 2个字节 -32768到32767
long int 8个字节 -9,223,372,036,854,775,808到9,223,372,036,854,775,807
signed long int 8个字节 -9,223,372,036,854,775,808到9,223,372,036,854,775,807
unsigned long int 8个字节 0到18,446,744,073,709,551,615
float 精度型占4个字节(32位)内存空间 +/-3.4e+/-38 (~7个数字)
double 双精度型占8个字节(64位)内存空间 +/-1.7e+/-308 (~15个数字)
long double 长双精度型16个字节(128位)内存空间 可提供18-19位有效数字
wchar_t 2或4个字节 1个宽字符

typedef可以为已有的类型取一个新的名字

语法为

typedef type newname;

示例

// 为int取一个feet的名字
typedef int feet;
// 声明创建一个整型变量
feet distance;

枚举类型是C++的一种派生数据类型,变量只能有集中可能的值,创建枚举的语法

enum 枚举名{ 
     标识符[=整型常数], 
     标识符[=整型常数], 
... 
     标识符[=整型常数]
} 枚举变量;

示例定义一个颜色的枚举,变量c的类型为color,c的赋值为blue

enum color { red, green, blue } c;
c = blue;

默认的情况下,第一个名称的值为0,第二个名称的值为1,以此类推,也可以赋值

enum color { red, green=5, blue };

这里red为0,因为设置了green为5,所以blue在其基础加一为6

变量类型

变量为程序可操作的存储区的名称,每个变量需要有个指定的类型,类型决定了变量的存储大小和布局,范围值内的数据都可以存储在内存中,运算符可以应用到变量上

bool类型存储的值就是true和false

char通常是一个字符(8位),是一个整数类型

int对于机器而言就是最大的整数了

float为单精度浮点值,格式为1位符号,8位指数,23位小数

double为双精度浮点值,格式为1位符号,11位指数,52位小数

void为类型缺失

wchar_t为宽字符类型

定义变量

int why_age = 20

分别是数据类型,变量名和值

变量声明可以一次声明多个

int    i, j, k;
char   c, ch;
float  f, salary;
double d;

如果需要在声明的时候进行初始化

extern int d = 3, f = 5;   // 声明 
int d = 3, f = 5;          
byte z = 22;                
char x = 'x';

不进行初始化定义的,带有静态存储持续时间的变量会隐式的初始化为NULL,所有字节值为0,其他所有变量都初始化都是未定义的

变量的声明是告诉编译器保证变量以给定的类型和名称存在,编译器在不知道变量细节的时候进行进一步编译,例如使用很多文件且只在其中一个文件中定义变量的时候,可以在任意一个地方声明变量(可以多次声明),但是只能在一个文件、函数和代码块中被定义一次

#include <iostream>
using namespace std;

// 变量声明
extern int a, b;
extern int c;
extern float f;

int main ()
{
  // 变量定义
  int a, b;
  int c;
  float f;

  // 实际初始化
  a = 10;
  b = 20;
  c = a + b;

  cout << c << endl ;

  f = 70.0/3.0;
  cout << f << endl ;

  return 0;
}

函数也可以被声明

// 函数声明
int func();

int main()
{
    // 函数调用
    int i = func();
}

// 函数定义
int func()
{
    return 0;
}

变量的作用域分为三种

  • 局部变量,在函数或一个代码块内部声明的变量
  • 形式参数,在函数参数的定义中声明的变量
  • 全局变量,在所有函数外部声明的变量

局部变量示例

#include <iostream>
using namespace std;

int main ()
{
  // 局部变量声明
  int a, b;
  int c;

  // 实际初始化
  a = 10;
  b = 20;
  c = a + b;

  cout << c;

  return 0;
}

全局变量可以在整个程序使用,

#include <iostream>
using namespace std;

// 全局变量声明
int g;

int main ()
{
  // 局部变量声明
  int a, b;

  // 实际初始化
  a = 10;
  b = 20;
  g = a + b;

  cout << g;

  return 0;
}

局部变量和全局变量的名称可以相同,但是在函数内,局部变量会覆盖全局变量的值

#include <iostream>
using namespace std;

// 全局变量声明
int g = 20;

int main ()
{
  // 局部变量声明
  int g = 10;

  cout << g;

  return 0;
}

在定义全局变量的时候,系统会自动初始化下列的值

数据类型 初始化默认值
int 0
char '\0'
float 0
double 0
pointer NULL

常量

常量在程序运行的时候不会进行改变,可以是任何基本数据类型

整数常量

可以是十进制、八进制或十六进制

通过前缀表示

  • 0X和0x代表16进制
  • 0表示八进制
  • 不带前缀默认表示为十进制

也可以通过后缀表示

  • U和L组合,U表示无符号整数,L表示长整数,可以是大写也可以是小写

示例

212         // 合法的
215u        // 合法的
0xFeeL      // 合法的
078         // 非法的:8 不是八进制的数字
032UU       // 非法的:不能重复后缀

85         // 十进制
0213       // 八进制 
0x4b       // 十六进制 
30         // 整数 
30u        // 无符号整数 
30l        // 长整数 
30ul       // 无符号长整数

浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成,可以使用小数形式和指数形式来表示浮点常量

  • 小数形式,必须包含整数部分和小数部分
  • 指数形式,必须包含小数点、指数,或同时包含两者。带符号的指数是用e或E引入的

示例

3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指数
210f          // 非法的:没有小数或指数
.e55          // 非法的:缺少整数或分数

字符常量

字符常量是括在单引号中

以L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在wchar_t类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中。

字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。

转义序列码

转义序列 含义
\\ \ 字符
\' ' 字符
\" " 字符
\? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh... 一个或多个数字的十六进制数

示例使用转义字符

#include <iostream>
using namespace std;

int main()
{
   cout << "Hello\tWorld\n\n";
   return 0;
}

字符串常量

字符串字面值或常量是括在双引号""中,一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符

可以使用\做分隔符,把一个很长的字符串常量进行分行

示例

#include <iostream>
#include <string>
using namespace std;

int main() {
    string greeting = "hello, runoob";
    cout << greeting;
    cout << "\n";     // 换行符
    string greeting2 = "hello, \
                       runoob";
    cout << greeting;
    return 0;
}

定义常量

常量定义有两种方式

  • #define预处理器
  • const关键字

示例

#include <iostream>
using namespace std;

#define LENGTH 10   
#define WIDTH  5
#define NEWLINE '\n'

int main()
{

   int area;  

   area = LENGTH * WIDTH;
   cout << area;
   cout << NEWLINE;
   return 0;
}

#include <iostream>
using namespace std;

int main()
{
   const int  LENGTH = 10;
   const int  WIDTH  = 5;
   const char NEWLINE = '\n';
   int area;  

   area = LENGTH * WIDTH;
   cout << area;
   cout << NEWLINE;
   return 0;
}

修饰符类型

允许在char、int和double数据类型前放置修饰符,用于改变基本类型的含义,满足更多的需求

  • signed
  • unsigned
  • long
  • short

但是

  • signed、unsigned、long和short可应用于整型
  • signed和unsigned可应用于字符型
  • long可用于双精度型
  • signed和unsigned也可以作long或short修饰符的前缀。例如:unsigned long int

如果不写int,只写unsigned、signed、short或long也是可以的

unsigned x;
unsigned int y;

示例

#include <iostream>
using namespace std;

/* 
 * 这个程序演示了有符号整数和无符号整数之间的差别
*/
int main()
{
   short int i;           // 有符号短整数
   short unsigned int j;  // 无符号短整数

   j = 50000;

   i = j;
   cout << i << " " << j;

   return 0;
}

执行结果

-15536 50000

类型限定符有

  • const 对象在程序执行期间不能被修改改变
  • volatile 修饰符告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率
  • restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有C99增加了新的类型限定符restrict

存储类

程序中可选的存储类有

  • auto
  • register
  • static
  • extern
  • mutable
  • thread_local

在C++11有thread_local,在C++17开始,auto不再是存储类说明符,并且register将被弃用

auto存储类

用于两种情况

  • 声明变量时根据初始化表达式自动推断该变量的类型
  • 声明函数时函数返回值的占位符

在C++98中,但由于极少使用,C++11中已删除

auto f=3.14;      //double
auto s("hello");  //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型

register存储类

register存储类用于定义存储在寄存器中而不是RAM中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的'&'运算符(因为它没有内存位置)

{
   register int  miles;
}

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义register并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制

static存储类

static存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁

  • static修饰局部变量可以在函数调用之间保持局部变量的值
  • static修饰全局变量可以使变量的作用域限制在声明它的文件内
  • static修饰类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享

示例

#include <iostream>

// 函数声明 
void func(void);

static int count = 10; /* 全局变量 */

int main()
{
    while(count--)
    {
       func();
    }
    return 0;
}
// 函数定义
void func( void )
{
    static int i = 5; // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;

执行结果

变量 i 为 6 , 变量 count 为 9
变量 i 为 7 , 变量 count 为 8
变量 i 为 8 , 变量 count 为 7
变量 i 为 9 , 变量 count 为 6
变量 i 为 10 , 变量 count 为 5
变量 i 为 11 , 变量 count 为 4
变量 i 为 12 , 变量 count 为 3
变量 i 为 13 , 变量 count 为 2
变量 i 为 14 , 变量 count 为 1
变量 i 为 15 , 变量 count 为 0

extern存储类

提供一个全局变量的引用,全局变量对所有的程序文件都是可见的

对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置

当有多个文件定义了一个可能在其他文件中出现的全局变量或者函数,可以在其他文件中使用extern来得到已定义的变量或者函数的引用

示例

第一个文件——main.cpp

#include <iostream>

int count ;
extern void write_extern();

int main()
{
   count = 5;
   write_extern();
}

第二个文件——support.cpp,使用extern关键字用于声明已经在第一个文件中定义的count

#include <iostream>

extern int count;

void write_extern(void)
{
   std::cout << "Count is " << count << std::endl;
}

编译方式

$ g++ main.cpp support.cpp -o write

mutable存储类

仅适用于类的对象,允许对象的成员替代常量。也就是说,mutable成员可以通过const成员函数修改

thread_local存储类

thread_local修饰的变量仅可在它在其上创建的线程上访问,变量在创建线程时创建,并在销毁线程时销毁,每个线程都有其自己的变量副本

thread_local仅能应用与数据声明和定义,可以与static或extern合并,不能用于函数声明或定义

示例

thread_local int x;  // 命名空间下的全局变量
class X
{
    static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s;  // X::s 是需要定义的

void foo()
{
    thread_local std::vector<int> v;  // 本地变量
}

运算符

运算符有很多类型

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 杂项运算符

算数运算符

算数运算符表格

A变量为10,B变量为20

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
-- 自减运算符,整数值减少 1 A-- 将得到 9

关系运算符

  • ==
  • !=
  • >
  • <
  • >=
  • <=

逻辑运算符

  • &&
  • ||
  • !

位运算

位运算应用于位,并逐位执行操作

p q p&q `p q` p^q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

A=60,换算一下就是00111100,B=13,换算一下就是00001101

所以

  • A&B = 0000 1100
  • A|B = 0011 1101
  • A^B = 0011 0001
  • ~A = 1100 0011

~为取反,~A得到是-61,所以是1100 0011

  • ~1=-2
  • ~0=1

还有左移运算符<<,将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0),右移运算符>>,将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃

  • A << 2 将得到 240,即为 1111 0000
  • A >> 2 将得到 15,即为 0000 1111

赋值运算符

运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C *= A 相当于 C = C * A
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
= 按位或且赋值运算符 C = 2 等同于 C = C 2

杂项运算符

sizeof

sizeof是一个编译型运算符,用于判断变量和数据类型和字节大小,sizeof运算符可用于获取类,结构体或者其他自定义数据类型的大小

语法为sizeof (data type)

示例

#include <iostream>
using namespace std;

int main()
{
   cout << "Size of char : " << sizeof(char) << endl;
   return 0;
}

三目运算

语法为Exp1 ? Exp2 : Exp3,如果Exp1为真,计算Exp2,如果Exp1为假,计算Exp3

示例

if(y < 10){ 
   var = 30;
}else{
   var = 40;
}

按照三目运算写法

var = (y < 10) ? 30 : 40;

强制类型转换

例如int(2.2000)将返回 2

逗号运算符

逗号运算符是为了把几个表达式放在一起

#include <iostream>
using namespace std;

int main()
{
   int i, j;

   j = 10;
   i = (j++, j+100, 999+j);

   cout << i;

   return 0;
}

执行结果为1010

指针运算符

  • 取地址运算符 &
  • 间接寻址运算符 *

示例

#include <iostream>

using namespace std;

int main ()
{
   int  var;
   int  *ptr;
   int  val;

   var = 3000;

   // 获取 var 的地址
   ptr = &var;

   // 获取 ptr 的值
   val = *ptr;
   cout << "Value of var :" << var << endl;
   cout << "Value of ptr :" << ptr << endl;
   cout << "Value of val :" << val << endl;

   return 0;
}

成员运算符

  • .运算符,应用于实际对象
  • ->运算符,与一个指向对象的指针一起使用
struct Employee {
  char first_name[16];
  int  age;
} emp;

将why赋值给emp的first_name成员

strcpy(emp.first_name, "why");
// 或者p_emp为Employee对象的指针
strcpy(p_emp->first_name, "zara");

运算符优先级

循环

循环类型有几种

  • while
  • for
  • do while

while示例

#include <iostream>
using namespace std;

int main ()
{
   // 局部变量声明
   int a = 10;

   // while 循环执行
   while( a < 20 )
   {
       cout << "a 的值:" << a << endl;
       a++;
   }

   return 0;
}

for示例

#include <iostream>
using namespace std;

int main ()
{
   // for 循环执行
   for( int a = 10; a < 20; a = a + 1 )
   {
       cout << "a 的值:" << a << endl;
   }

   return 0;
}

do while示例

#include <iostream>
using namespace std;

int main ()
{
   // 局部变量声明
   int a = 10;

   // do 循环执行
   do
   {
       cout << "a 的值:" << a << endl;
       a = a + 1;
   }while( a < 20 );

   return 0;
}

循环控制有几种

  • break
  • continue
  • goto

goto示例

#include <iostream>
using namespace std;

int main ()
{
   // 局部变量声明
   int a = 10;

   // do 循环执行
   LOOP:do
   {
       if(a == 15)
       {
          // 跳过迭代
          a = a + 1;
          goto LOOP;
       }
       cout << "a 的值:" << a << endl;
       a = a + 1;
   }while( a < 20 );

   return 0;
}

判断

判断主要有两类

  • if
  • switch

示例if

if(boolean_expression)
{
   // 如果布尔表达式为真将执行的语句
}

if(boolean_expression)
{
   // 如果布尔表达式为真将执行的语句
}
else if(boolean_expression2)
{
   // 如果布尔表达式为真将执行的语句
}
else
{
   // 如果布尔表达式为假将执行的语句
}

示例代码

#include <iostream>
using namespace std;

int main ()
{
   // 局部变量声明
   int a = 100;

   // 检查布尔条件
   if( a == 10 )
   {
       // 如果 if 条件为真,则输出下面的语句
       cout << "a 的值是 10" << endl;
   }
   else if( a == 20 )
   {
       // 如果 else if 条件为真,则输出下面的语句
       cout << "a 的值是 20" << endl;
   }
   else if( a == 30 )
   {
       // 如果 else if 条件为真,则输出下面的语句
       cout << "a 的值是 30" << endl;
   }
   else
   {
       // 如果上面条件都不为真,则输出下面的语句
       cout << "没有匹配的值" << endl;
   }
   cout << "a 的准确值是 " << a << endl;

   return 0;
}

示例Switch

switch(expression){
    case constant-expression  :
       statement(s);
       break; // 可选的
    case constant-expression  :
       statement(s);
       break; // 可选的
    // 您可以有任意数量的 case 语句
    default : // 可选的
       statement(s);
}

switch会对每个switch case进行检查

  • expression必须为一个整型、枚举类型或者class有一个转换函数能将其转换为整型或者枚举类型
  • case的constant-expression必须和expression具有相同的数据类型

示例代码

#include <iostream>
using namespace std;

int main ()
{
   // 局部变量声明
   char grade = 'D';

   switch(grade)
   {
   case 'A' :
      cout << "很棒!" << endl; 
      break;
   case 'B' :
   case 'C' :
      cout << "做得好" << endl;
      break;
   case 'D' :
      cout << "您通过了" << endl;
      break;
   case 'F' :
      cout << "最好再试一下" << endl;
      break;
   default :
      cout << "无效的成绩" << endl;
   }
   cout << "您的成绩是 " << grade << endl;

   return 0;
}

函数

每个C++程序至少有一个函数,主函数main()

程序可以定义额外的函数

  • 函数声明会告诉编译器函数的名称、返回类型和参数
  • 函数定义提供函数的主体

定义函数格式

return_type function_name( parameter list )
{
   body of the function
}
  • 返回类型,return_type函数返回值的数据类型
  • 函数名称,function_name
  • 参数
  • 函数主体

示例

// 函数返回两个数中较大的那个数

int max(int num1, int num2) 
{
   // 局部变量声明
   int result;

   if (num1 > num2)
      result = num1;
   else
      result = num2;

   return result; 
}

调用函数

示例

#include <iostream>
using namespace std;

// 函数声明
int max(int num1, int num2);

int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
   int ret;

   // 调用函数来获取最大值
   ret = max(a, b);

   cout << "Max value is : " << ret << endl;

   return 0;
}

// 函数返回两个数中较大的那个数
int max(int num1, int num2) 
{
   // 局部变量声明
   int result;

   if (num1 > num2)
      result = num1;
   else
      result = num2;

   return result; 
}

函数参数

函数使用参数,需要声明接收参数值变量,这些变量被称为形式参数,在函数内就像局部变量,在进入函数的时候创建,在退出函数的时候销毁

调用函数有三种传参方式

  • 传值调用,将参数实际值传入
  • 指针调用,将参数地址传入,可以在函数内部修改实际参数
  • 引用调用,将参数引用传入,可以在函数内部修改实际参数

传值调用示例

#include <iostream>
using namespace std;

// 函数声明
void swap(int x, int y);

// 函数定义
void swap(int x, int y)
{
   int temp;

   temp = x; /* 保存 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y */

   return;
}

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;
}

指针调用示例

#include <iostream>
using namespace std;

// 函数声明
void swap(int *x, int *y);

// 函数定义
void swap(int *x, int *y)
{
   int temp;
   temp = *x;    /* 保存地址 x 的值 */
   *x = *y;        /* 把 y 赋值给 x */
   *y = temp;    /* 把 x 赋值给 y */

   return;
}

int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;

   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;

   /* 调用函数来交换值
    * &a 表示指向 a 的指针,即变量 a 的地址 
    * &b 表示指向 b 的指针,即变量 b 的地址 
    */
   swap(&a, &b);

   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;

   return 0;
}

引用调用示例

#include <iostream>
using namespace std;

// 函数声明
void swap(int &x, int &y);

// 函数定义
void swap(int &x, int &y)
{
   int temp;
   temp = x; /* 保存地址 x 的值 */
   x = y;    /* 把 y 赋值给 x */
   y = temp; /* 把 x 赋值给 y  */

   return;
}

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;
}

参数可以使用默认值

#include <iostream>
using namespace std;

int sum(int a, int b=20)
{
  int result;

  result = a + b;

  return (result);
}

int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
   int result;

   // 调用函数来添加值
   result = sum(a, b);
   cout << "Total value is :" << result << endl;

   // 再次调用函数
   result = sum(a);
   cout << "Total value is :" << result << endl;

   return 0;
}

数字

定义数字

示例

#include <iostream>
using namespace std;

int main ()
{
   // 数字定义
   short  s;
   int    i;
   long   l;
   float  f;
   double d;

   // 数字赋值
   s = 10;      
   i = 1000;    
   l = 1000000; 
   f = 230.47;  
   d = 30949.374;

   // 数字输出
   cout << "short  s :" << s << endl;
   cout << "int    i :" << i << endl;
   cout << "long   l :" << l << endl;
   cout << "float  f :" << f << endl;
   cout << "double d :" << d << endl;

   return 0;
}

数学运算

需要引用数据的头文件<cmath>,有用的内置的数学函数有

  • double cos(double):该函数返回弧度角(double 型)的余弦
  • double sin(double):该函数返回弧度角(double 型)的正弦
  • double tan(double):该函数返回弧度角(double 型)的正切
  • double log(double):该函数返回参数的自然对数
  • double pow(double, double):假设第一个参数为x,第二个参数为y,则该函数返回x的y次方
  • double hypot(double, double):该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度
  • double sqrt(double):该函数返回参数的平方根
  • int abs(int):该函数返回整数的绝对值
  • double fabs(double):该函数返回任意一个浮点数的绝对值
  • double floor(double):该函数返回一个小于或等于传入参数的最大整数

示例

#include <iostream>
#include <cmath>
using namespace std;

int main ()
{
   // 数字定义
   short  s = 10;
   int    i = -1000;
   long   l = 100000;
   float  f = 230.47;
   double d = 200.374;

   // 数学运算
   cout << "sin(d) :" << sin(d) << endl;
   cout << "abs(i)  :" << abs(i) << endl;
   cout << "floor(d) :" << floor(d) << endl;
   cout << "sqrt(f) :" << sqrt(f) << endl;
   cout << "pow( d, 2) :" << pow(d, 2) << endl;

   return 0;
}

生成随机数示例

#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

int main ()
{
   int i,j;

   // 设置种子
   srand( (unsigned)time( NULL ) );

   /* 生成 10 个随机数 */
   for( i = 0; i < 10; i++ )
   {
      // 生成实际的随机数
      j= rand();
      cout <<"随机数: " << j << endl;
   }

   return 0;
}

rand()函数只返回一个伪随机数,生成随机数之前必须先调用srand()函数

数组

数组可以存储一个固定大小的相同类型元素的顺序集合

数组基础使用

声明数组

type arrayName [ arraySize ];

声明数组示例

double balance[10];

初始化数组

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

也可以声明数组在进行赋值

double balance[10];
balance[4] = 50.0;

访问数组元素

double salary = balance[9];

示例代码

#include <iostream>
using namespace std;

#include <iomanip>
using std::setw;

int main ()
{
   int n[ 10 ]; // n 是一个包含 10 个整数的数组

   // 初始化数组元素          
   for ( int i = 0; i < 10; i++ )
   {
      n[ i ] = i + 100; // 设置元素 i 为 i + 100
   }
   cout << "Element" << setw( 13 ) << "Value" << endl;

   // 输出数组中每个元素的值                     
   for ( int j = 0; j < 10; j++ )
   {
      cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl;
   }

   return 0;
}

输出结果

Element        Value
      0          100
      1          101
      2          102
      3          103
      4          104
      5          105
      6          106
      7          107
      8          108
      9          109

多维数组

声明二维数组

type arrayName [ x ][ y ];

初始化二维数组

int a[3][4] = {  
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};
// 等价于
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

访问二维数组

指向数组的指针

数组名是指向第一个元素的常量指针,示例声明数组

double runoobAarray[50];

runoobAarray就是指向&runoobAarray[0]的指针,可以通过*(runoobAarray + 4)来访问runoobAarray[4]

可以将这个指针再赋值

double *p;
double runoobAarray[50];
p = runoobAarray;

所以就可以使用*p、*(p+1)、*(p+2)等来访问数组元素

示例

#include <iostream>
using namespace std;

int main ()
{
   // 带有 5 个元素的双精度浮点型数组
   double runoobAarray[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
   double *p;

   p = runoobAarray;

   // 输出数组中每个元素的值
   cout << "使用指针的数组值 " << endl; 
   for ( int i = 0; i < 5; i++ )
   {
       cout << "*(p + " << i << ") : ";
       cout << *(p + i) << endl;
   }

   cout << "使用 runoobAarray 作为地址的数组值 " << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "*(runoobAarray + " << i << ") : ";
       cout << *(runoobAarray + i) << endl;
   }

   return 0;
}

执行结果

使用指针的数组值 
*(p + 0) : 1000
*(p + 1) : 2
*(p + 2) : 3.4
*(p + 3) : 17
*(p + 4) : 50
使用 runoobAarray 作为地址的数组值 
*(runoobAarray + 0) : 1000
*(runoobAarray + 1) : 2
*(runoobAarray + 2) : 3.4
*(runoobAarray + 3) : 17
*(runoobAarray + 4) : 50

p是一个指向double型的指针,也可以存储一个double类型的变量

传递数组给函数

传数组到一个函数,数组类型自动转化为指针类型,因而需要传的是实际地址

有三种方式

// 形参为指针
void myFunction(int *param){}
// 形参为已定义大小的数组
void myFunction(int param[10]){}
// 形参为未定义大小的数组
void myFunction(int param[]){}

示例

#include <iostream>
using namespace std;

// 函数声明
double getAverage(int arr[], int size);

// 函数定义
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;
}

int main ()
{
   // 带有 5 个元素的整型数组
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;

   // 传递一个指向数组的指针作为参数
   avg = getAverage( balance, 5 ) ;

   // 输出返回值
   cout << "平均值是:" << avg << endl; 

   return 0;
}

C++不会对形参执行边界进行检查

从函数返回数组

C++不允许返回一个完整的数组作为函数的参数,可以使用不带索引的数组名来返回一个指向数组的指针

示例

#include <iostream>
#include <cstdlib>
#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;
}

字符串

字符串有两种表示方式

  • C风格字符串
  • C++引入的string类型

C风格字符串

字符串实际是使用null字符\0终止的一维字符数组,因为存储了空字符所以字符数组比实际多了一个

char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
// 等价于
char site[] = "RUNOOB";

有很多操作以null结尾的字符串的函数

  • strcpy(s1, s2):复制字符串s2到字符串s1
  • strcat(s1, s2):连接字符串s2到字符串s1的末尾
  • strlen(s1):返回字符串s1的长度
  • strcmp(s1, s2):如果s1和s2是相同的,则返回0;如果s1s2则返回值大于0
  • strchr(s1, ch):返回一个指针,指向字符串s1中字符ch的第一次出现的位置
  • strstr(s1, s2):返回一个指针,指向字符串s1中字符串s2的第一次出现的位置

String类型

通过引用<string>类类型

示例

#include <iostream>
#include <string>

using namespace std;

int main ()
{
   string str1 = "runoob";
   string str2 = "google";
   string str3;
   int  len ;

   // 复制 str1 到 str3
   str3 = str1;
   cout << "str3 : " << str3 << endl;

   // 连接 str1 和 str2
   str3 = str1 + str2;
   cout << "str1 + str2 : " << str3 << endl;

   // 连接后,str3 的总长度
   len = str3.size();
   cout << "str3.size() :  " << len << endl;

   return 0;
}

指针

指针使用

每个变量都有一个内存地址,可以通过&访问内存地址

示例

#include <iostream>

using namespace std;

int main ()
{
   int  var1;
   char var2[10];

   cout << "var1 变量的地址: ";
   cout << &var1 << endl;

   cout << "var2 变量的地址: ";
   cout << &var2 << endl;

   return 0;
}

指针是一个变量,所以也需要声明,值为另一个变量的内存地址

声明形式

type *var-name;

示例声明

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;

   // 输出在指针变量中存储的地址
   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;
}

返回的值为0,但是程序是不允许访问地址为0的内存,需要如需要检查是否有空指针的问题

if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */

指针运算符

指针支持执行算术运算,有四种

  • ++

  • +
  • -

如果ptr为一个指向1000地址的整数指针,执行ptr++会指向下一位整数位数,就是往后移动4字节,如果prt为一个指向1000地址的字符,就会往后移动1字节

指针支持比较,有三种

  • ==
  • <
  • >

指针数组

声明一个数组,为整数指针组成

int *ptr[MAX];

示例

#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;
}

指向指针的指针

指向指针的指针是一种多级间接寻址的形式

声明的形式

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;
    cout << "*ptr 值为:" << *ptr << endl;
    cout << "**pptr 值为:" << **pptr << endl;

    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;
}

示例

#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;
}

从函数返回指针

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)
  {
    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;
}

引用

引用使用

引用变量是一个已存在变量的别名

引用和指针的区别

  • 不存在空引用,必须连接到一块合法的内存
  • 一个引用被初始化为一个对象,就不能被指向另一个对象,指针可以在任何时候指向另一个对象
  • 引用必须在创建的时候初始化,指针可以在任何时候被初始化

示例创建引用

int i = 17;
int&  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;

// 函数声明
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;
}

使用引用作为函数返回值

示例代码

#include <iostream>

using namespace std;

double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};

double& setValues(int i) {  
   double& ref = vals[i];    
   return ref;   // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i],最后再返回 shit。


}

// 要调用上面定义函数的主函数
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用于日期和时间的操作的结构和函数

如果需要使用需要引入<ctime>头文件

有四个与时间相关的类型

  • clock_t
  • time_t
  • size_t
  • tm

前三者可以将系统时间和日期表示为某种整数

而tm是将日期和时间以C结构保存

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; // 夏令时
};

常用函数

  • time_t time(time_t *seconds);,获取系统的当前日历时间,自1970年1月1日来经过的秒数
  • char *ctime(const time_t *time);,返回一个当前时间的字符串指针,字符串格式
  • struct tm *localtime(const time_t *time);会返回一个本地时间的tm结构指针
  • char *asctime(const struct tm *time);,返回一个指向字符串的指针,示例见上
  • struct tm *gmtime(const time_t *time);,获取UTC时间(GMT时间)
  • time_t mktime(struct tm *time);,返回1970年1月1日来的秒数
  • double difftime ( time_t time2, time_t time1 );,返回两者相差的秒数
  • size_t strftime();,格式化日期为指定格式

示例time

#include <stdio.h>
#include <time.h>

int main ()
{
  time_t seconds;

  seconds = time(NULL);
  printf("自 1970-01-01 起的小时数 = %ld\n", seconds/3600);

  return(0);
}

示例ctime

#include <stdio.h>
#include <time.h>

int main ()
{
   time_t curtime;

   time(&curtime);

   printf("当前时间 = %s", ctime(&curtime));

   return(0);
}

示例localtime和asctime

#include <stdio.h>
#include <time.h>

int main ()
{
   time_t rawtime;
   struct tm *info;
   char buffer[80];

   time(&rawtime);

   info = localtime(&rawtime);
   printf("当前的本地时间和日期:%s", asctime(info));

   return(0);
}

示例gmtime

#include <stdio.h>
#include <time.h>

#define BST (+1)
#define CCT (+8)

int main ()
{

   time_t rawtime;
   struct tm *info;

   time(&rawtime);
   /* 获取 GMT 时间 */
   info = gmtime(&rawtime );

   printf("当前的世界时钟:\n");
   printf("伦敦:%2d:%02d\n", (info->tm_hour+BST)%24, info->tm_min);
   printf("中国:%2d:%02d\n", (info->tm_hour+CCT)%24, info->tm_min);

   return(0);
}

示例mktime

#include <stdio.h>
#include <time.h>

int main () {
    int ret;
    struct tm info;
    char buffer[80];

    info.tm_year = 2021 - 1900;
    info.tm_mon = 7 - 1;
    info.tm_mday = 4;
    info.tm_hour = 0;
    info.tm_min = 0;
    info.tm_sec = 1;
    info.tm_isdst = -1;

    ret = mktime(&info);
    if( ret == -1 ) {
        printf("Error: unable to make time using mktime\n");
    } else {
        strftime(buffer, sizeof(buffer), "%c", &info );
        printf(buffer);
    }

    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 Aug 14 15:42:37 2021

UTC 日期和时间:Sat Aug 14 07:42:37 2021

使用tm格式化时间

#include <iostream>
#include <ctime>

using namespace std;

int main( )
{
   // 基于当前系统的当前日期/时间
   time_t now = time(0);

   cout << "1970 到目前经过秒数:" << now << endl;

   tm *ltm = localtime(&now);

   // 输出 tm 结构的各个组成部分
   cout << "年: "<< 1900 + ltm->tm_year << endl;
   cout << "月: "<< 1 + ltm->tm_mon<< endl;
   cout << "日: "<<  ltm->tm_mday << endl;
   cout << "时间: "<< ltm->tm_hour << ":";
   cout << ltm->tm_min << ":";
   cout << ltm->tm_sec << endl;
}

通过->来访问结构成员

1970 到目前经过秒数:1628927053
年: 2021
月: 8
日: 14
时间: 15:44:13

示例difftime

#include <stdio.h>
#include <time.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif

int main ()
{
   time_t start_t, end_t;
   double diff_t;

   printf("程序启动...\n");
   time(&start_t);

   printf("休眠 5 秒...\n");
   sleep(5);

   time(&end_t);
   diff_t = difftime(end_t, start_t);

   printf("执行时间 = %f\n", diff_t);
   printf("程序退出...\n");

   return(0);
}

日期格式化

size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)

  • str 指向目标数组的指针,用于复制产生的字符串
  • max 复制到str的最大字符数
  • format c字符串,格式化说明符

说明符如下

说明符 替换为 示例
%a 缩写的星期几名称 Sun
%A 完整的星期几名称 Sunday
%b 缩写的月份名称 Mar
%B 完整的月份名称 March
%c 日期和时间表示法 Sun Aug 19 02:56:02 2012
%d 一月中的第几天(01-31) 19
%H 24小时格式的小时(00-23) 14
%I 12小时格式的小时(01-12) 05
%j 一年中的第几天(001-366) 231
%m 十进制数表示的月份(01-12) 08
%M 分(00-59) 55
%p AM 或 PM 名称 PM
%S 秒(00-61) 02
%U 一年中的第几周,以第一个星期日作为第一周的第一天(00-53) 33
%w 十进制数表示的星期几,星期日表示为 0(0-6) 4
%W 一年中的第几周,以第一个星期一作为第一周的第一天(00-53) 34
%x 日期表示法 08/19/12
%X 时间表示法 02:50:06
%y 年份,最后两个数字(00-99) 01
%Y 年份 2012
%Z 时区的名称或缩写 CDT
%% 一个%符号 %

示例

#include <stdio.h>
#include <time.h>

int main ()
{
   time_t rawtime;
   struct tm *info;
   char buffer[80];

   time( &rawtime );

   info = localtime( &rawtime );

   strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
   printf("格式化的日期 & 时间 : |%s|\n", buffer );

   return(0);
}

基本输入输出

C++的输入输出是IO操作,发生在流中,流是字节序列

  • 如果流为设备流向内存,为输入
  • 如果流从内存流向设备,为输出

主要头文件有

  • iostream,定义了cin、cout和cerr和clog对象,对应标准的输入流,输出流,非标准操作流和缓冲标准错误流
  • iomanip,通过参数话的流操作器setw和setprecision等,来声明对执行标准化IO有用的服务
  • fstream,为用户控制的文件处理声明服务

标准输出流

cout连接到标准输出设备,通常是显示屏,与流插入运算符<<结合使用,C++编译的时候会根据输出的类型选择合适的流运算符来显示值

#include <iostream>

using namespace std;

int main( )
{
   char str[] = "Hello C++";

   cout << "Value of str is : " << str << endl;
}

标准输入流

cout连接到标准输入设备,通常是键盘,与流提取运算符>>结合使用,C++编译的时候会根据输入的类型选择合适流提取运算符来提取值,并存储到给定的变量

#include <iostream>

using namespace std;

int main( )
{
   char name[50];

   cout << "请输入您的名称: ";
   cin >> name;
   cout << "您的名称是: " << name << endl;

}

cin >> name >> age;等价于

cin >> name;
cin >> age;

标准错误流

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;
}

数据结构

结构是C++中用户自定义的可用数据类型,允许存储不同类型的数据项

结构定义

定义结构使用struct,格式为

struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
...
} object_names;

示例

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} 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( 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 );

   // 输出 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;
}

指向结构的指针

定义指针的方式

struct Books *struct_pointer;
struct_pointer = &Book1;

访问结构指针的成员需要使用->

struct_pointer->title;

示例

#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 );

   // 通过传 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可以为创建的类型取一个别名

typedef struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
}Books;

定义的时候可以不使用struct,而是直接使用Books

Books Book1, Book2;

也可以使用typedef来定义非结构类型

typedef long int *pint32;
pint32 x, y, z;

x,y和z都是指向长整型long int的指针