目录
一、基本的c++语法
1.1 数据类型
1. 基本的内置类型
7种基本的数据类型
关键字 | 内存 (Linux-64) |
---|---|
bool | 1个字节 |
char | 1个字节 |
int | 4个字节 |
float | 4个字节 |
double | 8个字节 |
void | |
wchar_t | 4个字节 |
cout << "bool: \t\t" << "所占字节数:" << sizeof(bool);
cout << "\t最大值:" << (numeric_limits<bool>::max)();
cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl;
// bool: 所占字节数:1 最大值:1 最小值:0
2. typedef 声明
使用 typedef 为一个已有的类型取一个新的名字
typedef int feet;
feet distance; //创建了一个整型变量 distance:
3. 枚举类型
如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。
// 定义一个颜色枚举,变量 c 的类型为 color。c 被赋值为 "blue"。
enum color { red, green, blue } c;
c = blue;
// 默认情况下,每个名称都会比它前面一个名称大 1, 以此类推。
enum color { red, green=5, blue }; // blue 的值为 6,red仍为0
4. 类型转换
C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
静态转换(Static Cast)
强制类型转换
静态转换不进行任何运行时类型检查,因此可能会导致运行时错误。
int i = 10;
float f = static_cast<float>(i); // 静态将int类型转换为float类型
动态转换(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 属性,不能改变对象的类型。
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类型
1.2 常量
1. 定义常量
- 使用 #define 预处理器,在预处理阶段起作用。字符替换不进行内存分配,存储于代码段。
- 使用 const 关键字,在编译、运行阶段起作用。会进行内存分配,存储于程序的数据段中。定义成 const 后的常量,程序对其中只能读不能修改。。
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
const int LENGTH = 10; // 在一个类里建立const时不能给初值
const int WIDTH = 5;
const char NEWLINE = '\n';
// 指针常量
const *p; //修饰*p,指针指向的对象不可变
* const p; //修饰p,指针不可变
2. 定义静态变量
static:用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。
void example_function() {
static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
count++;
}
1.3 指针
指针是一个变量,其值为另一个变量的地址
int *ip; // 一个整型的指针
1. c++指针操作
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址
// 输出在指针变量中存储的地址
cout << ip << endl;
// 访问指针中地址的值
cout << *ip << endl;
2. c++ 指针的算术运算
递增一个指针
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,执行:
ptr++;
ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。
指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
3. 指针和数组
#include <iostream>
using namespace std;
int main ()
{
int var[3] = {10,100,200};
int *p=var;
for (int i = 0; i < 3; i++)
{
cout << *(var++)<< endl; //编译错误,因为var是数组名(也是数组首元素地址),不是一个变量,不可修改其值
cout << *(var+i)<< endl; //编译正确,因为没有对var进行赋值,只是通过与i相加访问变量地址
cout << *(p++)<< endl; ///编译正确,p为指针变量可赋值
}
// 输出:10 10 100 100 200 200
return 0;
}
4. c++ 指向指针的指针(多级间接寻址)
指针的指针就是将指针的地址存放在另一个指针里面。
int var;
int *ptr;
int **pptr;
var = 3000;
// 获取 var 的地址
ptr = &var;
// 使用运算符 & 获取 ptr 的地址
pptr = &ptr;
- var、*ptr、**pptr 值为3000;
- &var、ptr、*pptr 值为地址0x7ffeec7a65e8。
c++ 传递指针给函数
5. c++ 从函数返回指针
C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static变量。
// 要生成和返回随机数的函数
int * getRandom( )
{
static int r[10];
// 设置种子,当srand()的参数值固定的时候,rand()获得的数也是固定的
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) << endl;
}
return 0;
}
1.4 引用
引用变量是一个别名
1. 引用和指针
三个主要的不同:
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
2. 创建引用
语法:
int i = 17;
int& r = i;
注意:
- 引用必须在声明时将其初始化,不能先声明后赋值
- 引用更接近const指针,必须在创建时进行初始化,一旦引用和某个变量关联起来,该引用就会一直指向该变量。
3. 把引用作为参数
传递变量的引用。形参是引用变量,和实参是一个变量,调用函数时,形参(引用变量)指向实参变量单元。这种通过形参引用可以改变实参的值。
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
swap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}
// 函数定义
void swap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
/* 输出:
交换前,a 的值: 100
交换前,b 的值: 200
交换后,a 的值: 200
交换后,b 的值: 100
*/
1.5 c++ 存储类
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。
下面列出 C++ 程序中可用的存储类:auto、register、static、extern、mutable、thread_local (C++11)
(从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。)
1. static 存储类
- static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
- static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
- 在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
#include <iostream>
// 函数声明
void func(void);
static int count = 2; /* 全局变量 */
int main()
{
while(count--)
{
func();
}
return 0;
}
// 函数定义
void func( void )
{
static int i = 10; // 局部静态变量
i++;
std::cout << "变量 i 为 " << i ;
std::cout << " , 变量 count 为 " << count << std::endl;
}
/*
变量 i 为 10 , 变量 count 为 1
变量 i 为 11 , 变量 count 为 0
*/
(1) static 修饰类的成员变量
- 静态成员变量是先于类的对象而存在
- 这个类的所有对象共用一个静态成员
- 如果静态成员是公有的,那么可以直接通过类名调用
- 静态成员数据在声明时候类外初始化
// 类
class Data
{
public:
Data(){}
~Data(){}
void show()
{
cout<<this->data<<" "<<number<<endl;
}
static void showData()//先于类的对象而存在
{
//这方法调用的时候不包含this指针
cout<<" "<<number<<endl;
}
private:
int data;
public:
static int number; //静态数据在声明时候类外初始化
};
int Data::number=0;//静态成员初始化
int main()
{
Data::showData();//通过类名直接调用
Data::number = 100;//通过类名直接使用
Data d;
d.show();
d.showData();//通过对象调用
cout << "Hello World!" << endl;
return 0;
}
(2).static 修饰类的成员方法
- 静态成员函数是先于类的对象而存在
- 可用类名直接调用(公有)
- 在静态成员函数中没有this指针,所以不能使用非静态成员
2. extern 存储类
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。extern 是用来在另一个文件中声明一个全局变量或函数。
// 文件1 :main.cpp
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
// 文件2:support.cpp
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
/*
第二个文件中的 extern 关键字用于声明已经在第一个文件 main.cpp 中定义的 count。
输出:Count is 5
*/
3. thread_local 存储类
-
使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。
-
thread_local 说明符可以与 static 或 extern 合并。
-
可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。
thread_local int x; // 命名空间下的全局变量
class A
{
static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string A::s; // A::s 是需要定义的
void foo()
{
thread_local std::vector<int> v; // 本地变量
}
1.6 c++ 运算符
算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算
1. 逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑与运算符。如果两个操作数都 true,则条件为 true |
|| | 逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 |
! | 逻辑非运算符。用来逆转操作数的逻辑状态 |
2. 位运算符
运算符 | 描述 |
---|---|
& | 按位与,0&0=0; 0&1=0; 1&0=0; 1&1=1 |
| | 按位或, 0|0=0; 0|1=1; 1|0=1; 1|1=1 |
^ | 异或运算符 |
~ | 取反运算符,按二进制位进行"取反"运算。~1=-2 |
<< | 二进制左移运算符。左边的二进制位丢弃,右边补0 |
>> | 二进制右移运算符。正数左补0,负数左补1,右边丢弃。 |
1.7 c++数组
1. 声明初始化数组
double arr[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
// 省略数组的大小,则数组的大小为初始化时元素的个数
double arr[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
2. 传递数组给函数
(1) 形参为指针
void myFunction(int *arr)
{
}
(2) 形参为固定大小的数组
void myFunction(int arr[10])
{
}
(3) 形参为未定义大小的数组
void myFunction(int arr[])
{
}
3. 从函数返回数组
C++ 不允许返回一个完整的数组作为函数的参数。可以返回一个指向数组的指针。
- C++ 不支持在函数外返回局部变量的地址,可以定义局部变量为 static 变量。
int* myFunction()
{
static int Arr[3] = {1, 2, 3};
return Arr;
}
1.8 c++字符串
C++ 标准库提供了 string类 类型
1. string类的输入输出
(1) cin
接收一个字符串,但是遇到“空格”, “TAB”, "回车"结束。
(2) getline()
可以接收空格并输出
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
getline(cin,s);
cout << s;
return 0;
}
2. 字符数组的输入输出
(1) cin.get()
可以用来接收字符和字符数组,定义的数组的个数是10,则实际上只能接收9个字符,还要加上’\0’。
#include<iostream>
#include<string>
using namespace std;
int main()
{
char ch, arr[10];
ch = cin.get(); //或者是cin.get(ch);
cout << ch;
cin.get(arr, 10);
cout << arr;
return 0;
}
(2) cin.getline()
可以接收空格,并且输出。这个函数有三个参数,分别是字符串的位置,接受个数,结束字符。
cin.getline(m, 5, ‘a’):输入jkalj时,输出jk。
char a[20];
cin.getline(a, 20);
cout << a;
函数 | 字符数组 | string | 是否包含空格 |
---|---|---|---|
cin | √ | √ | × |
cin.get() | √ | × | √ |
cin.getline() | √ | × | √ |
getline() | √ | √ | √ |
1.9 日期和时间
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。
有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。
类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。
(1)time_t time(time_t * time);
该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 -1。
(2)clock_t clock(void);
该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 -1。
(3)size_t strftime();
该函数可用于格式化日期和时间为指定的格式。
(4)char * asctime ( const struct tm * time );
该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。
1. 获取当前的日期和时间
#include <iostream>
#include <ctime>
using namespace std;
int main( )
{
// 基于当前系统的当前日期/时间
time_t now = time(0);
// 把 now 转换为字符串形式
char* dt = ctime(&now);
cout << "本地日期和时间:" << dt << endl;
// 把 now 转换为 tm 结构
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "UTC 日期和时间:"<< dt << endl;
}
/*
本地日期和时间:Sat Jan 8 20:07:41 2011
UTC 日期和时间:Sun Jan 9 03:07:41 2011
*/
2. 使用结构 tm 格式化时间
tm 结构以 C 结构的形式保存日期和时间。
#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 到目前时间:1503564157
年: 2017
月: 8
日: 24
时间: 16:42:37
*/
}
自学记录,部分来源:菜鸟教程
二、c++ 算法学习
2.1 C++ 按位操作(与、或、异或)
1. 异或运算
异或运算:参加运算的两个数据,按二进制位进行 “异或” 运算。即 位异 结果为1, 否则为0
int main(){
// 3^5 即 0000 0011 ^ 0000 0101 = 0000 0111 因此,3|5的值得6
int a = 3, b = 5;
cout<< a ^ b <<endl;
return 0;
}
-
性质
- 任何数和0做异或运算,结果仍然是原来的数,即 a ⊕ 0=a
- 任何数和其自身做异或运算,结果是0,即 a ⊕ a = 0
- 异或运算满足交换律和结合律,即 a ⊕ b ⊕ a = b ⊕ a ⊕ a = b ⊕ ( a ⊕ a ) = b ⊕ 0 = b
-
应用
- 使特定位翻转:
找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。 - 保留原值
和 0 做 异或 运算
- 使特定位翻转:
2. 与运算
按二进制位进行 “与” 运算。
- 性质
运算规则:0 & 0 = 0; 0 &1 = 0; 1 & 0 = 0; 1 & 1 = 1;
即:两位同时为“1”,结果才为“1”,否则为0 - 应用
- 清零
将数字与0相‘与’ - 取一个数的指定位
指定位置为1,其余为0。 - 判断奇偶
根据末位是0还是1来决定,为0就是偶数,为1就是奇数。
因此可以用if (a & 1 == 0)代替if (a % 2 == 0)来判断a是不是偶数。
- 清零
3. 或运算
按二进制位进行 “或” 运算。
- 性质
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为1,其值为1。 - 应用
- 将数据的指定位置为1。