C++ 系列 -- 基础知识

数据类型

七种基本的 C++ 数据类型

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

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

  • signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型
  • unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍
  • short:表示变量的范围比 int 更小。short int 可以缩写为 short
  • long:表示变量的范围比 int 更大。long int 可以缩写为 long
  • long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符

各数据类型的内存和范围

下表显示了各种数据类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值

类型 范围
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 到 65,535
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 个字节 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字)
double 8 个字节 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字)
long double 16 个字节 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字
wchar_t 2 或 4 个字节 1 个宽字符

枚举类型

例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 "blue"

cpp 复制代码
enum color { red, green, blue } c;
c = blue;

类型判断

  1. typeid 运算符:可以用于获取一个对象的类型信息,返回一个 type_info 对象(类型的首字母)。例如:

    cpp 复制代码
    int i = 42;
    float j = 42.5f;
    std::cout << typeid(i).name() << std::endl;  // 输出:i
    std::cout << typeid(j).name() << std::endl;  // 输出:f
  2. std::is_same 类型特征:可以用于检查两种类型是否相同。例如:

    cpp 复制代码
    std::cout << std::is_same<int, float>::value << std::endl;  // 输出:0
    std::cout << std::is_same<int, int>::value << std::endl;    // 输出:1
  3. std::is_integral 类型特征:可以用于检查一个类型是否为整型。例如:

    cpp 复制代码
    std::cout << std::is_integral<int>::value << std::endl;    // 输出:1
    std::cout << std::is_integral<float>::value << std::endl;  // 输出:0
  4. std::is_floating_point 类型特征:可以用于检查一个类型是否为浮点型。例如:

    cpp 复制代码
    std::cout << std::is_floating_point<int>::value << std::endl;    // 输出:0
    std::cout << std::is_floating_point<float>::value << std::endl;  // 输出:1
  5. std::is_pointer 类型特征:可以用于检查一个类型是否为指针类型。例如:

    cpp 复制代码
    std::cout << std::is_pointer<int*>::value << std::endl;    // 输出:1
    std::cout << std::is_pointer<float>::value << std::endl;  // 输出:0
  6. std::is_array 类型特征:可以用于检查一个类型是否为数组类型。例如:

    cpp 复制代码
    std::cout << std::is_array<int[]>::value << std::endl;    // 输出:1
    std::cout << std::is_array<float>::value << std::endl;    // 输出:0
  7. std::is_function 类型特征:可以用于检查一个类型是否为函数类型。例如:

    cpp 复制代码
    std::cout << std::is_function<int(int)>::value << std::endl;  // 输出:1
    std::cout << std::is_function<float>::value << std::endl;     // 输出:0

这些类型判断方式可以帮助我们在编写泛型代码时更加灵活和安全

类型转换

隐式类型转换

简单粗暴,但是存在问题。例如将一个 float 类型的值 42.5 转换为 int 类型,由于 int 类型不支持小数部分,因此在进行转换时,小数部分会被截断,只保留整数部分

cpp 复制代码
float f = 42.5f;
int i = int(f);
int i = (int)f;
int i = f;

显式类型转换

C 风格的类型转换:使用括号将需要转换的类型括起来,并在前面添加需要转换的类型。例如:

cpp 复制代码
int i = 42;
float f = (float)i;  // C 风格的类型转换

C++ 风格的类型转换:使用 static_castdynamic_castconst_castreinterpret_cast 进行类型转换。例如:

静态转换static_cast是将一种数据类型的值强制转换为另一种数据类型的值

cpp 复制代码
int i = 42;
float f = static_cast<float>(i);

动态转换dynamic_cast通常用于将一个基类 指针或引用转换为派生类指针或引用

cpp 复制代码
class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转换为派生类指针

常量转换const_cast用于将 const 类型 的对象转换为非 const 类型的对象

cpp 复制代码
const int i = 10;
int& r = const_cast<int&>(i); // 常量转换,将const int转换为int

重新解释转换reinterpret_cast将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换

cpp 复制代码
int i = 10;
float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型

数字和字符串互转

  • 字符串转 int、float
cpp 复制代码
#include <iostream>
#include <string>

int main() {
    std::string str = "42";
    int i = std::stoi(str);
    std::cout << "The integer is: " << i << std::endl;
    float f = std::stof(str);
    std::cout << "The float is: " << f << std::endl;
}

这些函数都需要包含头文件 <string>,接受一个字符串作为参数。如果字符串不包含有效的数字,则会抛出 std::invalid_argumentstd::out_of_range 异常。

需要注意的是,这些函数只能将符合特定格式的字符串转换为数字类型。例如,对于 std::stoi,字符串必须以数字开头,可以包含正负号,但不能包含其他字符。对于 std::stof,字符串必须包含小数点和数字,可以包含正负号和指数符号,但不能包含其他字符

  • int、float 转字符串
cpp 复制代码
#include <iostream>
#include <string>

int main() {
    int i = 42;
    float f = 3.14;
    std::string str1 = std::to_string(i);
    std::string str2 = std::to_string(f);
    std::cout << "The integer string is: " << str1 << std::endl;
    std::cout << "The float string is: " << str2 << std::endl;
}

常量

在 C++ 中,有两种简单的定义常量的方式:

  • 使用 #define 预处理器。
  • 使用 const 关键字。

define 预处理器

下面是使用 #define 预处理器定义常量的形式:

cpp 复制代码
#define identifier value

具体请看下面的实例:

cpp 复制代码
#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;
}

const 关键字

您可以使用 const 前缀声明指定类型的常量,如下所示:

cpp 复制代码
const type variable = value;

具体请看下面的实例:

cpp 复制代码
#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;
}

类型限定符

类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。

限定符 含义
const const 定义常量,表示该变量的值不能被修改。。
volatile 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程
restrict restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict
mutable 表示类中的成员变量可以在 const 成员函数中被修改
static 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问
register 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。

const 实例

cpp 复制代码
const int NUM = 10; // 定义常量 NUM,其值不可修改
const int* ptr = &NUM; // 定义指向常量的指针,指针所指的值不可修改
int const* ptr2 = &NUM; // 和上面一行等价

volatile 实例

cpp 复制代码
volatile int num = 20; // 定义变量 num,其值可能会在未知的时间被改变

mutable 实例

cpp 复制代码
class Example {
public:
    int get_value() const {
        return value_; // const 关键字表示该成员函数不会修改对象中的数据成员
    }
    void set_value(int value) const {
        value_ = value; // mutable 关键字允许在 const 成员函数中修改成员变量
    }
private:
    mutable int value_;
};

static 实例

cpp 复制代码
void example_function() {
    static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
    count++;
}

register 实例

cpp 复制代码
void example_function(register int num) {
    // register 关键字建议编译器将变量 num 存储在寄存器中
    // 以提高程序执行速度
    // 但是实际上是否会存储在寄存器中由编译器决定
}

变量作用域

有三个地方可以定义变量:

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

局部变量和全局变量

例子:

cpp 复制代码
#include <iostream>
using namespace std;

int g; // 全局变量声明
int main ()
{
  int a, b; // 局部变量声明
  // 实际初始化
  a = 10;
  b = 20;
  g = a + b;
  cout << g;
}

局部变量的值会覆盖全局变量的值,例子:

cpp 复制代码
#include <iostream>
using namespace std;

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

变量的作用域可以分为以下几种:

  • 局部作用域:在函数内部声明的变量具有局部作用域,它们只能在函数内部访问。局部变量在函数每次被调用时被创建,在函数执行完后被销毁
  • 全局作用域:在所有函数和代码块之外声明的变量具有全局作用域,它们可以被程序中的任何函数访问。全局变量在程序开始时被创建,在程序结束时被销毁
  • 块作用域:在代码块内部声明的变量具有块作用域,它们只能在代码块内部访问。块作用域变量在代码块每次被执行时被创建,在代码块执行完后被销毁
  • 类作用域:在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问。类作用域变量的生命周期与类的生命周期相同

块作用域

cpp 复制代码
#include <iostream>

int main() {
    int a = 10;
    {
        int a = 20;  // 块作用域变量
        std::cout << "块变量: " << a << std::endl;
    }
    std::cout << "外部变量: " << a << std::endl;
}

// 输出结果
块变量: 20
外部变量: 10

类作用域

可以使用类名和作用域解析运算符 :: 来访问这个变量

cpp 复制代码
#include <iostream>

class MyClass {
public:
    static int class_var;  // 类作用域变量
};
int MyClass::class_var = 30; // 使用类名和作用域解析运算符 :: 来访问这个变量
int main() {
    std::cout << "类变量: " << MyClass::class_var << std::endl;
}

C++ 内存分区

  1. 全局区Global

全局区是存放全局变量静态变量 的内存区域,在程序启动 时自动分配,在程序结束 时自动释放。全局区的内存空间是连续的,由编译器自动管理。全局区的大小也是固定 的,因此只能存放较小的数据

  1. 常量区Const

常量区是存放常量数据 的内存区域,如字符串常量、全局常量等。常量区内存只读,不可修改 。常量区的内存空间是连续的,由编译器自动管理

  1. 栈区Stack

栈区是由编译器自动分配和释放的内存区域,存放函数的参数值、局部变量 等。栈区内存的分配和释放速度很快 ,因为它的内存空间是连续 的,且由编译器自动管理。栈区的大小是固定的,一般只能存放较小 的数据。当函数执行完毕后,栈区内存会自动释放,因此不需要手动释放栈区内存

  1. 堆区Heap

堆区是由程序员手动分配和释放的内存区域,存放程序运行期间动态分配的内存。堆区的内存空间是不连续 的,因此内存分配和释放的速度较慢 ,但是堆区的内存空间较大 ,可以存放较大的数据。堆区内存的分配和释放需要使用 newdeletemallocfree 等函数手动管理

  1. 代码区Code

代码区是存放程序的可执行代码的内存区域,由操作系统负责管理。代码区的内存空间是只读的,不可修改

运算符

算术运算符

下表显示了 C++ 支持的算术运算符。

假设变量 A 的值为 10,变量 B 的值为 21,则:

运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 31
- 从第一个操作数中减去第二个操作数 A - B 将得到 -11
* 把两个操作数相乘 A * B 将得到 210
/ 取整 B / A 将得到 2
% 取余 B % A 将得到 1
++ 自增运算符,整数值增加 1 A++ 将得到 11
-- 自减运算符,整数值减少 1 A-- 将得到 9

关系运算符

下表显示了 C++ 支持的关系运算符。

假设变量 A 的值为 10,变量 B 的值为 20,则:

运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 不为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 不为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 不为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。

逻辑运算符

下表显示了 C++ 支持的关系逻辑运算符。

假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。 (A && B) 为 false。
|| 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 (A || B) 为 true。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。 !(A && B) 为 true。

其他一些重要的运算符

运算符 描述
sizeof sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
,(逗号运算符) 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点运算符)和 ->(箭头运算符) 成员运算符用于引用类、结构和共用体的成员
::(双冒号运算符) 用于直接访问命名空间、类、结构体、共用体或枚举类型的成员或静态成员
Cast(强制转换运算符) 强制类型转换,把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2
&(指针运算符) 指针运算符 & 由变量获取到它的地址 。例如 &a 得到变量 a 的地址
*(指针运算符) 指针运算符 * 由地址获取到变量的值 。例如 *ptr 得到地址 ptr 指向的变量

逗号运算符(,)

  • 使用逗号运算符是为了把几个表达式放在一起
  • 整个逗号表达式的值为系列中最后一个表达式的值

例子:

cpp 复制代码
#include <iostream>
using namespace std;
 
int main()
{
   int i, j;
   j = 10;
   i = (j++, j+100, 999+j);
   cout << i; // 1010
}

成员运算符(. ->)

成员运算符用于引用类、结构和共用体的(public)成员

cpp 复制代码
class Point {
public:
    int x;
    int y;
    void print() {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

int main() {
    /** 点运算符 */
    Point p;
    p.x = 5;  // 访问成员变量
    p.print();  // 访问成员函数
    
    /* 箭头运算符 */
    Point* pPtr = new Point();
    pPtr->x = 5;  // 访问成员变量
    pPtr->print();  // 访问成员函数
    delete pPtr;
}

双冒号运算符(::)

双冒号运算符用于访问命名空间、枚举类型、类的成员

以下例子中演示访问命名空间 NS 中的成员变量 x,其实 std 也是一个命名空间,其包含了 C++ 标准库中的所有标识符,例如标准输入输出流、容器、算法等等

cpp 复制代码
/* 命名空间 */
namespace NS {
    int x = 42;
}
/* 类 */
class MyClass {
public:
    static int y;
};
/* 枚举类型 */
enum Color { RED, GREEN, BLUE };

int MyClass::y = 123; // 访问类的静态成员

int main() {
    std::cout << NS::x << std::endl;  // 访问命名空间成员
    std::cout << MyClass::y << std::endl;  // 访问类的静态成员
    std::cout << Color::RED << std::endl;  // 访问枚举类型成员
}

基类也是类的一种,所以访问基类的成员也用双冒号运算符

cpp 复制代码
class Base {
public:
    int x;
    void print() {
        std::cout << "Base::print()" << x << std::endl;
    }
};

class Derived : public Base {
public:
    void print() {
        Base::print();  // 调用基类的方法
        std::cout << "Derived::print()" << Base::x << std::endl;  // 访问基类的成员
    }
};

int main() {
    Derived d;
    d.x = 42;
    d.print();
}

指针运算符(& *)

cpp 复制代码
#include <iostream>
using namespace std;
 
int main ()
{
   int  var;
   int  *ptr; // * 运算符也可以用来表示一个指针
   int  val;

   var = 3000;

   ptr = &var; // 获取变量 var 的地址,赋值给 ptr
   val = *ptr; // 获取地址 ptr 指向的变量 var 的值
   cout << "Value of var :" << var << endl;
   cout << "Value of ptr :" << ptr << endl;
   cout << "Value of val :" << val << endl;

   return 0;
}

注释

C++ 存在三种注释:

  • // 一般用于单行注释
  • /* ... */ 一般用于多行注释
  • #if 0 ... #endif 条件编译注释
cpp 复制代码
#include <iostream>
using namespace std;
 
int main() {
    // 这是单行注释
    
    /* 这是注释 */
    /* 
     * 可以多行注释
     */ 
    cout << "Hello World!";
    return 0;
}

块注释用于程序调试,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0 来屏蔽测试代码

cpp 复制代码
#if condition
  code1
#else
  code2
#endif

基本的输入和输出

I/O 库头文件

下列的头文件在 C++ 编程中很重要。

头文件 函数和描述
<iostream> 该文件定义了 cin、cout、cerrclog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。
<iomanip> 该文件通过所谓的参数化的流操纵器(比如 setwsetprecision),来声明对执行标准化 I/O 有用的服务。
<fstream> 该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。

标准输出流(cout)

cpp 复制代码
#include <iostream>
 
using namespace std;
 
int main( )
{
   char str[] = "Hello C++";
   cout << "Value of str is : " << str << endl;
}

C++ 编译器根据要输出变量的数据类型,选择合适的流插入运算符来显示值。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。

流插入运算符 << 在一个语句中可以多次使用 ,如上面实例中所示,endl 用于在行末添加一个换行符

标准输入流(cin)

cpp 复制代码
#include <iostream>
using namespace std;
 
int main( )
{
   char name[50];
   cout << "请输入您的名称: ";
   cin >> name;
   cout << "您的名称是: " << name << endl;
}

流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:

cpp 复制代码
cin >> name >> age;

这相当于下面两个语句:

cpp 复制代码
cin >> name;
cin >> age;

标准错误流(cerr)

预定义的对象 cerriostream 类的一个实例。cerr 对象附属到标准输出设备,通常也是显示屏,但是 cerr 对象是非缓冲 的,且每个流插入到 cerr 都会立即输出

cpp 复制代码
#include <iostream>
 
using namespace std;
 
int main( )
{
   char str[] = "Unable to read....";
   cerr << "Error message : " << str << endl;
}

标准日志流(clog)

预定义的对象 clogiostream 类的一个实例。clog 对象附属到标准输出设备,通常也是显示屏,但是 clog 对象是缓冲 的。这意味着每个流插入到 clog 都会先存储在缓冲 区,直到缓冲填满 或者缓冲区刷新时才会输出

cpp 复制代码
#include <iostream>
 
using namespace std;
 
int main( )
{
   char str[] = "Unable to read....";
   clog << "Error message : " << str << endl;
}

日期和时间

C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime> 头文件。

有四个与时间相关的类型:clock_t、time_t、size_ttm。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。

结构类型 tm 把日期和时间以 C 结构的形式保存,tm 结构的定义如下:

cpp 复制代码
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++ 标准库中查看一下各个函数的细节。

函数 描述
time_t time(time_t *time); 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 -1
char *ctime(const time_t *time); 该返回一个表示当地时间的字符串指针,字符串形式 day month year hours:minutes:seconds year\n\0
struct tm *localtime(const time_t *time); 该函数返回一个指向表示本地时间的 tm 结构的指针
clock_t clock(void); 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 -1
char * asctime ( const struct tm * time ); 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。
struct tm *gmtime(const time_t *time); 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示
time_t mktime(struct tm *time); 该函数返回日历时间,相当于 time 所指向结构中存储的时间
double difftime ( time_t time2, time_t time1 ); 该函数返回 time1 和 time2 之间相差的秒数
size_t strftime(); 该函数可用于格式化日期和时间为指定的格式

当前日期和时间

下面的实例获取当前系统的日期和时间,包括本地时间和协调世界时(UTC)

cpp 复制代码
#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;
}

输出结果:
本地日期和时间:Fri Sep 15 06:44:51 2023
UTC 日期和时间:Fri Sep 15 06:44:51 2023

使用结构 tm 格式化时间

C 库函数 size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr 根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。

下面是 strftime() 函数的声明

cpp 复制代码
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
  • str -- 这是指向目标数组的指针,用来复制产生的 C 字符串。
  • maxsize -- 这是被复制到 str 的最大字符数。
  • format -- 这是 C 字符串,包含了普通字符和特殊格式说明符的任何组合。这些格式说明符由函数替换为表示 tm 中所指定时间的相对应值。格式说明符是:
cpp 复制代码
#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 );
}

输出结果:
格式化的日期 & 时间 : 2023-09-15 06:45:54

判断

判断语句

C++ 编程语言提供了以下类型的判断语句。点击链接查看每个语句的细节。

语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成
if...else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行
嵌套 if 语句 您可以在一个 ifelse if 语句内使用另一个 ifelse if 语句
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句

? : 运算符

我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:

cpp 复制代码
Exp1 ? Exp2 : Exp3;

其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置

? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值

循环

循环类型

C++ 编程语言提供了以下几种循环类型。点击链接查看每个类型的细节。

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件
for 循环 多次执行一个语句序列,简化管理循环变量的代码
do...while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似
嵌套循环 您可以在 while、for 或 do..while 循环内使用一个或多个循环

循环控制语句

循环控制语句更改执行的正常序列。当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁。

C++ 提供了下列的控制语句。点击链接查看每个语句的细节。

控制语句 描述
break 语句 终止 loopswitch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句
continue 语句 引起循环跳过主体的剩余部分,立即重新开始测试条件
goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句

字符串

C++ 标准库提供了 string 类类型,需要引入 #include <string>

1. 构造字符串

cpp 复制代码
string s1();  // si = ""
string s2("Hello");  // s2 = "Hello"
string s3(4, 'K');  // s3 = "KKKK"
string s4("12345", 1, 3);  //s4 = "234",即 "12345" 的从下标 1 开始,长度为 3 的子串

注意:string 类不接收一个整型参数或一个字符型参数的构造函数。下面的两种写法是错误的

cpp 复制代码
string s1('K'); // 不接收一个字符型参数
string s2(123); // 不接收一个整型参数

2. 求字符串长度

cpp 复制代码
string s1 = "hello world";
cout << s1.length() << endl; // 11
cout << s1.size() << endl; // 11

3. 字符串拼接

除了可以使用++=运算符对 string 对象执行字符串的连接操作外,string 类还有 append 成员函数,可以用来向字符串后面添加内容。append 成员函数返回对象自身的引用,会改变原字符串。例如:

cpp 复制代码
string s1("123"), s2("abc");
s1.append(s2);  // s1 = "123abc"
s1.append(s2, 1, 2);  // s1 = "123abcbc"
s1.append(3, 'K');  // s1 = "123abcbcKKK"
s1.append("ABCDE", 2, 3);  // s1 = "123abcbcKKKCDE",添加 "ABCDE" 的子串(2, 3)

补充见 c.biancheng.net/view/400.ht...

指针

什么是内存地址

每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示这个变量在内存中的地址

cpp 复制代码
#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

什么是指针

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

cpp 复制代码
type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

cpp 复制代码
int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同

指针的使用

指针的使用一般包括:

  • 定义一个指针变量
  • 把变量地址赋值给指针
  • 访问指针变量中可用地址的值
cpp 复制代码
#include <iostream>
 
using namespace std;
 
int main ()
{
   int  var = 20;   // 实际变量的声明
   int  *ip;        // 指针变量的声明
 
   ip = &var;       // 在指针变量中存储 var 的地址
 
   cout << "变量 var 的值是:";
   cout << var << endl;
 
   // 输出在指针变量中存储的地址
   cout << "指针 ip 指向的地址是:";
   cout << ip << endl;
 
   // 访问指针中地址的值
   cout << "指针 ip 指向的地址存的变量的值是:";
   cout << *ip << endl;
 
   return 0;
}

运行结果:

csharp 复制代码
变量 var 的值是:20
指针 ip 指向的地址是:0xbfc601ac
指针 ip 指向的地址存的变量的值是:20

空指针

赋为 NULL 值 的指针被称为空指针

cpp 复制代码
#include <iostream>

using namespace std;

int main ()
{
   int  *ptr = NULL;
   cout << "ptr 的值是 " << ptr ;
   return 0;
}

运行结果:

ptr 的值是 0

内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

scss 复制代码
if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */

因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

指针自增自减比较

指针自增自减比较通常用于数组

指针递增

cpp 复制代码
#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   ptr = var; // 指针中的数组地址
   // ptr = &var[MAX-1]; // 指针中最后一个元素的地址
   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;
}

运行结果:

css 复制代码
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

指针比较

cpp 复制代码
#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;
}

运行结果:

css 复制代码
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

指针数组

在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

cpp 复制代码
#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;
}

运行结果:

css 复制代码
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

也可以用一个指向字符的指针数组来存储一个字符串列表,如下:

cpp 复制代码
#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;
   }
   return 0;
}

运行结果:

css 复制代码
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 类型指针的指针:

cpp 复制代码
int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:

cpp 复制代码
#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;
}

运行结果:

csharp 复制代码
var 值为 :3000
*ptr 值为:3000
**pptr 值为:3000

多级间接寻址的应用场景

  1. 动态内存分配:使用多级指针可以实现动态内存分配,即分配一个指针数组,用于存储多个指针变量的地址,然后再分配每个指针变量所指向的内存空间。
  2. 多维数组:在多维数组中,可以使用多级指针来表示二维、三维等多维数组,以便在程序中动态地分配和访问多维数组。
  3. 数据结构:在数据结构中,可以使用多级指针来表示链表、树等数据结构,以便在程序中动态地添加、删除和修改节点。
  4. 函数指针:在函数指针中,可以使用多级指针来表示指向函数指针的指针,以便在程序中动态地管理函数指针

传递指针给函数

C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

cpp 复制代码
#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;
}

当上面的代码被编译和执行时,它会产生下列结果:

javascript 复制代码
Number of seconds : 1695175690

能接受指针作为参数的函数,也能接受数组作为参数,如下所示:

cpp 复制代码
#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;
}

当上面的代码被编译和执行时,它会产生下列结果:

csharp 复制代码
Average value is: 214.4

从函数返回指针

C++ 只支持在函数外返回 static 类型的局部变量的地址(因为如果不是 static 类型的变量,函数执行结束后该地址指向的局部变量就被销毁了,返回出来的这个指针就没有意义了)

现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:

cpp 复制代码
#include <iostream>
#include <ctime>
#include <cstdlib>
 
using namespace std;
 
// 要生成和返回随机数的函数
int * getRandom( )
{
  static int  r[5];
 
  srand( (unsigned)time( NULL ) ); // 设置种子
  for (int i = 0; i < 5; ++i)
  {
    r[i] = rand();
    cout << r[i] << endl;
  }
 
  return r;
}
 
// 要调用上面定义函数的主函数
int main ()
{
   int *p; // 一个指向整数的指针
 
   p = getRandom();
   for ( int i = 0; i < 5; i++ )
   {
       cout << "*(p + " << i << ") : ";
       cout << *(p + i) << endl;
   }
 
   return 0;
}

运行结果:

markdown 复制代码
624723190
1468735695
807113585
976495677
613357504
*(p + 0) : 624723190
*(p + 1) : 1468735695
*(p + 2) : 807113585
*(p + 3) : 976495677
*(p + 4) : 613357504

数组

数组声明

必须声明数组元素类型、数组变量名、数组大小

cpp 复制代码
type arrayName [ arraySize ];

如:
double balance[10];

数组初始化

采用花括号 {} 来包含数组

  • 数组大小必须大于等于元素个数
  • 数组大小不填则默认是元素个数
cpp 复制代码
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};

数组元素访问

cpp 复制代码
double salary = balance[9];

多维数组

初始化

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

访问

cpp 复制代码
int val = a[2][3];

指向数组的指针

cpp 复制代码
double *p;
double runoobAarray[10];

p = runoobAarray;

p = runoobAarray; 表示把第一个元素的地址 存储在 p 中,接下来就可以使用 *p*(p+1)*(p+2) 等来访问数组元素

cpp 复制代码
#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;
}

运行结果:

markdown 复制代码
使用指针的数组值 
*(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

传递数组给函数

三种函数形式参数的声明方式:

arduino 复制代码
// 形式参数是一个指针:
void myFunction(int *param)
{

}
// 形式参数是一个已定义大小的数组:
void myFunction(int param[10])
{

}
// 形式参数是一个未定义大小的数组:
void myFunction(int param[])
{

}

例子:

cpp 复制代码
#include <iostream>
using namespace std;

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

int main ()
{
   int balance[5] = {1000, 2, 3, 17, 50}; // 带有 5 个元素的整型数组
   double avg;
   avg = getAverage( balance, 5 ); // 传递一个指向数组的指针作为参数
   cout << "平均值是:" << 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;
}

从函数返回数组

由于在函数内部定义的数组属于局部变量,函数执行完后这个局部数组的内存会被释放,返回出来的指针指向的数组已经不存在了,所以不能用局部数组。只能选择用静态数组动态分配数组

静态数组

静态数组就是前面加个 static

使用静态数组需要在函数内部创建一个静态数组,并将其地址返回,例如:

cpp 复制代码
int* myFunction()
{
   static int myArray[3] = {1, 2, 3};
   return myArray;
}

让我们来看下面的函数,它会生成 10 个随机数,并使用数组来返回它们

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <ctime>
 
using namespace std;
 
// 要生成和返回随机数的函数
int * getRandom( )
{
  static int  r[5];
 
  // 设置种子
  srand( (unsigned)time( NULL ) );
  for (int i = 0; i < 5; ++i)
  {
    r[i] = rand();
    cout << r[i] << endl;
  }
 
  return r;
}
 
// 要调用上面定义函数的主函数
int main ()
{
   // 一个指向整数的指针
   int *p;
 
   p = getRandom();
   for ( int i = 0; i < 5; i++ )
   {
       cout << "*(p + " << i << ") : ";
       cout << *(p + i) << endl;
   }
 
   return 0;
}

运行结果:

markdown 复制代码
624723190
1468735695
807113585
976495677
613357504
*(p + 0) : 624723190
*(p + 1) : 1468735695
*(p + 2) : 807113585
*(p + 3) : 976495677
*(p + 4) : 613357504

动态分配数组

在函数内部动态分配的数组在函数执行结束时不会自动释放 ,所以需要调用函数的代码负责释放返回的数组

cpp 复制代码
#include <iostream>
using namespace std;

int* createArray(int size) {
    int* arr = new int[size];
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    return arr;
}

int main() {
    int* myArray = createArray(5); // 调用返回数组的函数,得到一个指向数组的指针
    for (int i = 0; i < 5; i++) {
        cout << myArray[i] << " ";
    }
    cout << endl;
    delete[] myArray; // 释放内存
    return 0;
}

运行结果:

1 2 3 4 5 

引用

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

引用 VS 指针

三个主要的不同:

  • 不存在空引用;不需要判断空指针
  • 不能修改指向,一旦引用被初始化为一个对象,就不能被指向到另一个对象;指针可以在任何时候指向到另一个对象
  • 引用必须在创建时被初始化;指针可以在任何时间被初始化

创建引用

cpp 复制代码
int i = 17;

int& r = i; // 成功创建 i 的引用变量 r

r 可以读作是"一个初始化为 i 的整型引用"

cpp 复制代码
#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;
}

运行结果:

yaml 复制代码
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7

把引用作为参数

cpp 复制代码
#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;
}

把引用作为返回值

通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。

当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:

cpp 复制代码
#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]
 
 
}
 
// 要调用上面定义函数的主函数
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;
}

运行结果:

css 复制代码
改变前的值
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

当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。

cpp 复制代码
int& func() {
   int q;
   //! return q; // 在编译时发生错误
   static int x;
   return x;     // 安全,x 在函数作用域外依然是有效的
}

参考内容

相关推荐
‘’林花谢了春红‘’3 小时前
C++ list (链表)容器
c++·链表·list
机器视觉知识推荐、就业指导5 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Yang.996 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王6 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_7 小时前
C++自己写类 和 运算符重载函数
c++
六月的翅膀7 小时前
C++:实例访问静态成员函数和类访问静态成员函数有什么区别
开发语言·c++
liujjjiyun7 小时前
小R的随机播放顺序
数据结构·c++·算法
¥ 多多¥7 小时前
c++中mystring运算符重载
开发语言·c++·算法
天若有情6738 小时前
c++框架设计展示---提高开发效率!
java·c++·算法