数据类型
七种根本的 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”
enum color { red, green, blue } c;
c = blue;
类型判别
-
typeid
运算符:能够用于获取一个方针的类型信息,回来一个type_info
方针(类型的首字母)。例如:int i = 42; float j = 42.5f; std::cout << typeid(i).name() << std::endl; // 输出:i std::cout << typeid(j).name() << std::endl; // 输出:f
-
std::is_same
类型特征:能够用于检查两种类型是否相同。例如:std::cout << std::is_same<int, float>::value << std::endl; // 输出:0 std::cout << std::is_same<int, int>::value << std::endl; // 输出:1
-
std::is_integral
类型特征:能够用于检查一个类型是否为整型。例如:std::cout << std::is_integral<int>::value << std::endl; // 输出:1 std::cout << std::is_integral<float>::value << std::endl; // 输出:0
-
std::is_floating_point
类型特征:能够用于检查一个类型是否为浮点型。例如:std::cout << std::is_floating_point<int>::value << std::endl; // 输出:0 std::cout << std::is_floating_point<float>::value << std::endl; // 输出:1
-
std::is_pointer
类型特征:能够用于检查一个类型是否为指针类型。例如:std::cout << std::is_pointer<int*>::value << std::endl; // 输出:1 std::cout << std::is_pointer<float>::value << std::endl; // 输出:0
-
std::is_array
类型特征:能够用于检查一个类型是否为数组类型。例如:std::cout << std::is_array<int[]>::value << std::endl; // 输出:1 std::cout << std::is_array<float>::value << std::endl; // 输出:0
-
std::is_function
类型特征:能够用于检查一个类型是否为函数类型。例如: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
类型不支撑小数部分,因而在进行转化时,小数部分会被切断,只保留整数部分
float f = 42.5f;
int i = int(f);
int i = (int)f;
int i = f;
显式类型转化
C 风格的类型转化:运用括号将需求转化的类型括起来,并在前面增加需求转化的类型。例如:
int i = 42;
float f = (float)i; // C 风格的类型转化
C++ 风格的类型转化:运用static_cast
、dynamic_cast
、const_cast
或reinterpret_cast
进行类型转化。例如:
静态转化static_cast
是将一种数据类型的值强制转化为另一种数据类型的值
int i = 42;
float f = static_cast<float>(i);
动态转化dynamic_cast
一般用于将一个基类指针或引证转化为派生类指针或引证
class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转化为派生类指针
常量转化const_cast
用于将 const 类型的方针转化为非 const 类型的方针
const int i = 10;
int& r = const_cast<int&>(i); // 常量转化,将const int转化为int
从头解释转化reinterpret_cast
将一个数据类型的值从头解释为另一个数据类型的值,一般用于在不同的数据类型之间进行转化
int i = 10;
float f = reinterpret_cast<float&>(i); // 从头解释将int类型转化为float类型
数字和字符串互转
- 字符串转 int、float
#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_argument
或std::out_of_range
反常。
需求留意的是,这些函数只能将符合特定格局的字符串转化为数字类型。例如,关于std::stoi
,字符串有必要以数字最初,能够包括正负号,但不能包括其他字符。关于std::stof
,字符串有必要包括小数点和数字,能够包括正负号和指数符号,但不能包括其他字符
- int、float 转字符串
#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 预处理器界说常量的办法:
#define identifier value
详细请看下面的实例:
#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前缀声明指定类型的常量,如下所示:
const type variable = value;
详细请看下面的实例:
#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 实例
const int NUM = 10; // 界说常量 NUM,其值不行修正
const int* ptr = &NUM; // 界说指向常量的指针,指针所指的值不行修正
int const* ptr2 = &NUM; // 和上面一行等价
volatile 实例
volatile int num = 20; // 界说变量 num,其值或许会在未知的时刻被改动
mutable 实例
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 实例
void example_function() {
static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
count++;
}
register 实例
void example_function(register int num) {
// register 关键字主张编译器将变量 num 存储在寄存器中
// 以进步程序履行速度
// 可是实践上是否会存储在寄存器中由编译器决议
}
变量效果域
有三个地方能够界说变量:
- 在函数或一个代码块内部声明的变量,称为部分变量
- 在函数参数的界说中声明的变量,称为办法参数
- 在一切函数外部声明的变量,称为大局变量
部分变量和大局变量
比如:
#include <iostream>
using namespace std;
int g; // 大局变量声明
int main ()
{
int a, b; // 部分变量声明
// 实践初始化
a = 10;
b = 20;
g = a + b;
cout << g;
}
部分变量的值会掩盖大局变量的值,比如:
#include <iostream>
using namespace std;
int g; // 大局变量声明
int main ()
{
int g = 10; // 部分变量声明
cout << g;
}
变量的效果域能够分为以下几种:
- 部分效果域:在函数内部声明的变量具有部分效果域,它们只能在函数内部拜访。部分变量在函数每次被调用时被创立,在函数履行完后被毁掉
- 大局效果域:在一切函数和代码块之外声明的变量具有大局效果域,它们能够被程序中的任何函数拜访。大局变量在程序开端时被创立,在程序完毕时被毁掉
- 块效果域:在代码块内部声明的变量具有块效果域,它们只能在代码块内部拜访。块效果域变量在代码块每次被履行时被创立,在代码块履行完后被毁掉
- 类效果域:在类内部声明的变量具有类效果域,它们能够被类的一切成员函数拜访。类效果域变量的生命周期与类的生命周期相同
块效果域
#include <iostream>
int main() {
int a = 10;
{
int a = 20; // 块效果域变量
std::cout << "块变量: " << a << std::endl;
}
std::cout << "外部变量: " << a << std::endl;
}
// 输出成果
块变量: 20
外部变量: 10
类效果域
能够运用类名和效果域解析运算符::
来拜访这个变量
#include <iostream>
class MyClass {
public:
static int class_var; // 类效果域变量
};
int MyClass::class_var = 30; // 运用类名和效果域解析运算符::来拜访这个变量
int main() {
std::cout << "类变量: " << MyClass::class_var << std::endl;
}
C++ 内存分区
- 大局区(Global)
大局区是寄存大局变量和静态变量的内存区域,在程序启动时主动分配,在程序完毕时主动开释。大局区的内存空间是接连的,由编译器主动办理。大局区的巨细也是固定的,因而只能寄存较小的数据
- 常量区(Const)
常量区是寄存常量数据的内存区域,如字符串常量、大局常量等。常量区内存只读,不行修正。常量区的内存空间是接连的,由编译器主动办理
- 栈区(Stack)
栈区是由编译器主动分配和开释的内存区域,寄存函数的参数值、部分变量等。栈区内存的分配和开释速度很快,由于它的内存空间是接连的,且由编译器主动办理。栈区的巨细是固定的,一般只能寄存较小的数据。当函数履行完毕后,栈区内存会主动开释,因而不需求手动开释栈区内存
- 堆区(Heap)
堆区是由程序员手动分配和开释的内存区域,寄存程序运转期间动态分配的内存。堆区的内存空间是不接连的,因而内存分配和开释的速度较慢,可是堆区的内存空间较大,能够寄存较大的数据。堆区内存的分配和开释需求运用new
和delete
或malloc
和free
等函数手动办理
- 代码区(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 指向的变量 |
逗号运算符(,)
- 运用逗号运算符是为了把几个表达式放在一同
- 整个逗号表达式的值为系列中最终一个表达式的值
比如:
#include <iostream>
using namespace std;
int main()
{
int i, j;
j = 10;
i = (j++, j+100, 999+j);
cout << i; // 1010
}
成员运算符(. ->)
成员运算符用于引证类、结构和共用体的(public)成员
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++ 规范库中的一切标识符,例如规范输入输出流、容器、算法等等
/* 命名空间 */
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; // 拜访枚举类型成员
}
基类也是类的一种,所以拜访基类的成员也用双冒号运算符
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();
}
指针运算符(& *)
#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
条件编译注释
#include <iostream>
using namespace std;
int main() {
// 这是单行注释
/* 这是注释 */
/*
* 能够多行注释
*/
cout << "Hello World!";
return 0;
}
块注释用于程序调试,测验时运用 #if 1来履行测验代码,发布后运用 #if 0来屏蔽测验代码
#if condition
code1
#else
code2
#endif
根本的输入和输出
I/O 库头文件
下列的头文件在 C++ 编程中很重要。
头文件 | 函数和描绘 |
---|---|
<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;
}
C++ 编译器依据要输出变量的数据类型,选择合适的流刺进运算符来显现值。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。
流刺进运算符 << 在一个句子中能够屡次运用,如上面实例中所示,endl用于内行末增加一个换行符
规范输入流(cin)
#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
是iostream
类的一个实例。cerr 方针隶属到规范输出设备,一般也是显现屏,可是cerr
方针是非缓冲的,且每个流刺进到 cerr 都会当即输出
#include <iostream>
using namespace std;
int main( )
{
char str[] = "Unable to read....";
cerr << "Error message : " << str << endl;
}
规范日志流(clog)
预界说的方针clog
是iostream
类的一个实例。clog 方针隶属到规范输出设备,一般也是显现屏,可是clog
方针是缓冲的。这意味着每个流刺进到 clog 都会先存储在缓冲区,直到缓冲填满或许缓冲区改写时才会输出
#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_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++ 规范库中检查一下各个函数的细节。
函数 | 描绘 |
---|---|
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)
#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() 函数的声明
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
- str— 这是指向方针数组的指针,用来仿制产生的 C 字符串。
- maxsize— 这是被仿制到 str 的最大字符数。
- format— 这是 C 字符串,包括了普通字符和特殊格局说明符的任何组合。这些格局说明符由函数替换为标明 tm 中所指定时刻的相对应值。格局说明符是:
#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 句子 | 您能够在一个if或else if句子内运用另一个if或else if句子 |
switch 句子 | 一个switch句子答应测验一个变量等于多个值时的情况 |
嵌套 switch 句子 | 您能够在一个switch句子内运用另一个switch 句子 |
? : 运算符
咱们现已在前面的章节中讲解了条件运算符 ? : ,能够用来代替if…else句子。它的一般办法如下:
Exp1 ? Exp2 : Exp3;
其间,Exp1、Exp2 和 Exp3 是表达式。请留意,冒号的运用和方位
? 表达式的值是由 Exp1 决议的。假如 Exp1 为真,则核算 Exp2 的值,成果即为整个 ? 表达式的值。假如 Exp1 为假,则核算 Exp3 的值,成果即为整个 ? 表达式的值
循环
循环类型
C++ 编程言语供给了以下几种循环类型。点击链接检查每个类型的细节。
循环类型 | 描绘 |
---|---|
while 循环 | 当给定条件为真时,重复句子或句子组。它会在履行循环主体之前测验条件 |
for 循环 | 屡次履行一个句子序列,简化办理循环变量的代码 |
do…while 循环 | 除了它是在循环主体完毕测验条件外,其他与 while 句子相似 |
嵌套循环 | 您能够在 while、for 或 do..while 循环内运用一个或多个循环 |
循环操控句子
循环操控句子更改履行的正常序列。当履行脱离一个规模时,一切在该规模中创立的主动方针都会被毁掉。
C++ 供给了下列的操控句子。点击链接检查每个句子的细节。
操控句子 | 描绘 |
---|---|
break 句子 | 停止loop或switch句子,程序流将继续履行紧接着 loop 或 switch 的下一条句子 |
continue 句子 | 引起循环跳过主体的剩下部分,当即从头开端测验条件 |
goto 句子 | 将操控转移到被符号的句子。可是不主张在程序中运用 goto 句子 |
字符串
C++ 规范库供给了string类类型,需求引进 #include <string>
1. 构造字符串
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 类不接纳一个整型参数或一个字符型参数的构造函数。下面的两种写法是过错的
string s1('K'); // 不接纳一个字符型参数
string s2(123); // 不接纳一个整型参数
2. 求字符串长度
string s1 = "hello world";
cout << s1.length() << endl; // 11
cout << s1.size() << endl; // 11
3. 字符串拼接
除了能够运用+
和+=
运算符对 string 方针履行字符串的衔接操作外,string 类还有 append
成员函数,能够用来向字符串后面增加内容。append 成员函数回来方针本身的引证,会改动原字符串。例如:
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…
指针
什么是内存地址
每一个变量都有一个内存方位,每一个内存方位都界说了可运用连字号(&)运算符拜访的地址,它标明这个变量在内存中的地址
#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
什么是指针
指针是一个变量,其值为另一个变量的地址,即,内存方位的直接地址。就像其他变量或常量相同,您有必要在运用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般办法为:
type *var-name;
在这里,type是指针的基类型,它有必要是一个有用的 C++ 数据类型,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 << "变量 var 的值是:";
cout << var << endl;
// 输出在指针变量中存储的地址
cout << "指针 ip 指向的地址是:";
cout << ip << endl;
// 拜访指针中地址的值
cout << "指针 ip 指向的地址存的变量的值是:";
cout << *ip << endl;
return 0;
}
运转成果:
变量 var 的值是:20
指针 ip 指向的地址是:0xbfc601ac
指针 ip 指向的地址存的变量的值是:20
空指针
赋为 NULL 值的指针被称为空指针
#include <iostream>
using namespace std;
int main ()
{
int *ptr = NULL;
cout << "ptr 的值是 " << ptr ;
return 0;
}
运转成果:
ptr 的值是 0
内存地址 0 有特别重要的意义,它标明该指针不指向一个可拜访的内存方位。但依照惯例,假如指针包括空值(零值),则假定它不指向任何东西。
如需检查一个空指针,您能够运用 if 句子,如下所示:
if(ptr) /* 假如 ptr 非空,则完结 */
if(!ptr) /* 假如 ptr 为空,则完结 */
因而,假如一切未运用的指针都被赋予空值,同时避免运用空指针,就能够避免误用一个未初始化的指针。很多时分,未初始化的变量存有一些废物值,导致程序难以调试。
指针自增自减比较
指针自增自减比较一般用于数组
指针递加
#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;
}
运转成果:
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};
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
指针数组
在这里,把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 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;
}
运转成果:
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 类型指针的指针:
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;
}
运转成果:
var 值为 :3000
*ptr 值为:3000
**pptr 值为:3000
多级直接寻址的运用场景
- 动态内存分配:运用多级指针能够完成动态内存分配,即分配一个指针数组,用于存储多个指针变量的地址,然后再分配每个指针变量所指向的内存空间。
- 多维数组:在多维数组中,能够运用多级指针来标明二维、三维等多维数组,以便在程序中动态地分配和拜访多维数组。
- 数据结构:在数据结构中,能够运用多级指针来标明链表、树等数据结构,以便在程序中动态地增加、删去和修正节点。
- 函数指针:在函数指针中,能够运用多级指针来标明指向函数指针的指针,以便在程序中动态地办理函数指针
传递指针给函数
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 : 1695175690
能承受指针作为参数的函数,也能承受数组作为参数,如下所示:
#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
从函数回来指针
C++ 只支撑在函数外回来 static 类型的部分变量的地址(由于假如不是 static 类型的变量,函数履行完毕后该地址指向的部分变量就被毁掉了,回来出来的这个指针就没有意义了)
现在,让咱们来看下面的函数,它会生成 10 个随机数,并运用标明指针的数组名(即第一个数组元素的地址)来回来它们,详细如下:
#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;
}
运转成果:
624723190
1468735695
807113585
976495677
613357504
*(p + 0) : 624723190
*(p + 1) : 1468735695
*(p + 2) : 807113585
*(p + 3) : 976495677
*(p + 4) : 613357504
引证
引证的实质是一个已存在变量的别号
创立引证
int i = 17;
int& r = i; // 成功创立 i 的引证变量 r
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;
}
运转成果:
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;
// 函数声明
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++ 函数能够回来一个引证,办法与回来一个指针相似。
当函数回来一个引证时,则回来一个指向回来值的隐式指针。这样,函数就能够放在赋值句子的左面。例如,请看下面这个简略的程序:
#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;
}
运转成果:
改动前的值
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 在函数效果域外依然是有用的
}
引证和指针的区别
五个主要的不同:
- 初始化:引证有必要在界说时进行初始化,指针能够在任何时分进行初始化
- 空值:引证不能为空,有必要引证某个现已存在的变量,指针能够为空
- 重界说:引证不能被从头界说,即不能让引证引证另一个变量,指针能够被从头界说
- 运算符:引证没有自己的运算符,它运用被引证变量的运算符,指针有自己的运算符,如
*
和->
- 内存办理:引证不需求进行内存办理,它不会导致内存泄漏和野指针等问题,指针需求进行内存办理,需求手动分配和开释内存
引证和指针的运用场景
- 函数参数传递:在函数参数传递中,假如需求修正参数的值,则能够运用指针或引证类型来传递参数。假如参数能够为空,则能够运用指针类型。假如参数不能为空,则能够运用引证类型。例如:
// 运用指针传递参数
void func(int* p) {
if(p){
*p = 10;
}
}
// 运用引证传递参数
void func(int& r) {
r = 10;
}
- 动态内存分配:在动态内存分配中,需求运用指针类型来存储分配的内存地址,以便后续拜访内存中的数据。例如:
// 动态分配内存,回来指针
int* p = new int[10];
// 拜访内存中的数据
p[0] = 1;
- 数据结构:在数据结构中,需求运用指针类型来标明数据结构中的节点,以便在程序中动态地增加、删去和修正节点。例如:
// 界说链表节点
struct Node {
int val;
Node* next;
};
// 动态分配节点,回来指针
Node* p = new Node;
// 修正节点的值和指针
p->val = 1;
p->next = nullptr;
- 运算符重载:在运算符重载中,需求运用引证类型来完成运算符的重载,以便更加直观和安全地拜访数据。例如:
// 界说向量类
class Vector {
public:
// 重载下标运算符
int& operator[](int i) {
return data[i];
}
private:
int data[10];
};
// 运用引证拜访向量元素
Vector v;
v[0] = 1;
数组
数组声明
有必要声明数组元素类型、数组变量名、数组巨细
type arrayName [ arraySize ];
如:
double balance[10];
数组初始化
选用花括号 {}
来包括数组
- 数组巨细有必要大于等于元素个数
- 数组巨细不填则默许是元素个数
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
数组元素拜访
double salary = balance[9];
多维数组
初始化
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
拜访
int val = a[2][3];
指向数组的指针
double *p;
double runoobAarray[10];
p = runoobAarray;
p = runoobAarray;
标明把第一个元素的地址存储在 p 中,接下来就能够运用 *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
传递数组给函数
三种函数办法参数的声明办法:
// 办法参数是一个指针:
void myFunction(int *param)
{
}
// 办法参数是一个已界说巨细的数组:
void myFunction(int param[10])
{
}
// 办法参数是一个未界说巨细的数组:
void myFunction(int param[])
{
}
比如:
#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
运用静态数组需求在函数内部创立一个静态数组,并将其地址回来,例如:
int* myFunction()
{
static int myArray[3] = {1, 2, 3};
return myArray;
}
让咱们来看下面的函数,它会生成 10 个随机数,并运用数组来回来它们
#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;
}
运转成果:
624723190
1468735695
807113585
976495677
613357504
*(p + 0) : 624723190
*(p + 1) : 1468735695
*(p + 2) : 807113585
*(p + 3) : 976495677
*(p + 4) : 613357504
动态分配数组
在函数内部动态分配的数组在函数履行完毕时不会主动开释,所以需求调用函数的代码负责开释回来的数组
#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
参考内容
- 菜鸟教程
- C++入门教程,C++基础教程