C++基础

C++基础

基本数据类型

C++基本数据类型包括整型、字符型、浮点型、布尔型

基本整型

char 1字节(8位)

short 至少2字节(16位,2^16=65536)

int 至少2字节(16位,[-32768,32767]) unsigned int(16位,[0,65535])

long 至少4字节(32位)

long long (C++11) 至少8字节(64位)

tips:可以通过头文件查看整型限制信息

char是C++基本整型,字符串string不是基本数据类型,字符串使用std::string类,属于C++的标准库。

char本质是一个整数,由cin/cout处理字符流

char有无符号由C++判定 char/signed char/unsigned char

wchar_t是一种宽字符类型(2字节),原本的char为1字节,此时使用wcin/wcout处理字符流

C++新增char16_t(2字节)/char32_t(4字节)

1
2
3
4
5
6
7
8
9
int a(12);
// C++初始化方法
int a = 12;
// C/C++初始化方法
int b{ 12 };
int b = {12};
int d = {}; // int d = 0;
int d{};
// C++11初始化方法
1
2
3
4
5
6
7
8
char a = 'A';
// 字符使用'',字符串使用""
char a = 'A';
int b = a;
cout << b << endl;
cout.put(a);
// cout.put()用于显示一个字符
// char本质是一个整数

write(字符串地址,显示多少个字符)

1
2
3
4
const char * a = "hello";
int len = strlen(a);
cout.write(a,5);
// hello

浮点型

float 至少4字节(32位) 3.1F/3.1f

double 至少6字节(48位) 通常为8字节64位

long double 至少6字节(48位) 通常为80/96/128位 3.1L/3.1l

tips:可以在头文件cfloat或float.h中查看限制

1
2
3
4
// C++浮点数默认cout精度为6位,可使用ostream中precision成员函数指定精度
float a = 0.123456789;
cout << a << endl;
//0.123457

转译字符

1
2
3
4
5
6
7
\n
\t
\\
\?
\'
\"
// 常用

常量符号

const 常量限定符(signed/unsigned都是限定符)

1
2
3
4
5
6
int a = 3;
const int* pt = &a;
int* const finger = &a;
// pt、*finger为变量,*pt、finger为常量
const int* const stick = &a;
// stick、*stick都是常量

#define


复合数据类型

数组

C++/C将数组名视为指针(数组第一个元素的地址 &arr[0] ) 如果&arr则返回的是整个数组的地址

数组初始化

1
2
3
4
5
6
7
8
9
10
int arr[] = {1, 2, 3, 4, 5};
int arr[10] = {1, 2, 3}; //剩余的元素会被自动初始化为 0
int arr[5] = {}; //所有元素会被初始化为 0

int arr[][3] = {{1, 2, 3}, {4, 5, 6}}; //第一维由编译器初始化确定,第二维必须明确指定
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int arr[2][3] = {{1}, {4, 5}}; //未初始化的元素会被自动初始化为 0

int arr[10] {1, 2, 3};
int arr[2][3] {{1}, {4, 5}};

在标准 C++ 里,数组的大小必须是一个常量/常量表达式

1
2
const int size = 4;
int nums[size] = {};

数组遍历

C++新特性–打印数组的方法

1
2
3
4
5
for (declaration : range) {
// 循环体
}
// declaration变量声明
// range要遍历的对象(数组、容器)
1
2
3
4
5
int arr[] = {1, 2, 3, 4, 5};
for (int num : arr){
cout << num << "";
}
cout << endl;

获取数组大小

1
2
3
int nums[] = { 1,2,3,4,5 };
int length = sizeof(nums) / sizeof(nums[0]);
// 整个数组所占的字节数/数组中单个元素所占的字节数

指针指向数组的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int arr1[] { 1,2,3 };
int* ar = arr1;
cout << *ar << endl;
//1
int arr1[] { 1,2,3 };
int* ar = arr1;
cout << (*ar+1) << endl;
//2

int arr2[2][3]{ {1,3,5}, {2 ,4, 6} };
// arr2会转换为指向arr2[0]的指针,而arr2[0]是一个包含3个int元素的一维数组
int (*ar2)[3] = arr2;
// ar2是一个指向包含3个int类型元素数组的指针 ar2[][3]
// ar2是一个指针,指向一块地址,这块地址存放3个int变量
cout << (*ar2)[0] << endl; // 1第一行第一个值
cout << *(*ar2 + 1) << endl; // 3第一行第二个值
// *ar2对指针ar2解引用指向arr2[0][0]
// *ar2+1会让指针向后移动一个int类型的位置arr2[0][1]
ar2 += 1;
// 指针ar2向后移动3个int变量指向arr2[1][0]
cout << (*ar2)[1] << endl; // 8第二行第二个值

sizeof() 获取的是字节数,1字节(byte)等于8位(bit),1KB等于1024字节

字符串

C风格字符串

头文件为 字符串以空字符(\0)结尾 cout识别到空字符才会停止打印

1
2
3
4
5
6
7
8
9
10
11
char a[5] = { 'y', 'o', 'u', 'n', 'g' };
char b[6] = { 'y', 'o', 'u', 'n', 'g' ,'\0' };
cout << a << endl;
cout << b << endl;
//young烫烫烫烫烫烫烫烫烫烫烫烫烫蘺oung
//young
char c[] = "young";
cout << c << endl;
//young
char d[8] = "young";
//多出的会使用\0填充 y o u n g \0 \0 \0

字符串常量拼接

1
cout << "mr_""young" << endl;

C字符串标准库

strlen() 确定字符串长度 只计算可见字符

字符串输入

1
2
3
4
5
6
7
8
9
10
11
12
char a[15];
cin >> a ;
cout << a << endl;
cout << strlen(a) << endl;
cout << sizeof(a) << endl;
//young
//5
//15
cout << a[0] << endl;
cout << sizeof(a[0]) << endl;
//y
//1

cin使用空白(空格/换行/制表符)确定字符串输入结束

1
2
3
4
5
char a[15];
cin >> a ;
cout << a << endl;
//输入mr young
//输出mr

面向行的字符串输入方法 get()/getline()

getline()会将换行符Enter转换为空字符\0 get()会读取换行符Enter

1
2
3
4
5
char a[15];
cin.getline(a, 5); // 读取四个可见字符和一个\0
cout << a << endl;
//输入mr young
//输出mr y
1
2
3
4
5
6
7
8
char a[15];
cin.get(a, 6);
cout << "第一个get:"<<a << endl;
cin.get(a, 6);
cout << "第二个get:" << a << endl;
//输入young
//第一个get:young
//第二个get:
1
2
3
4
5
6
7
8
9
char a[15];
cin.get(a , 6).get(); // 可以调用get()不带参数,读取下一个字符
cout << "第一个get:"<<a << endl;
cin.get(a , 6);
cout << "第二个get:" << a << endl;
//young
//第一个get:young
//young
//第二个get:young

一次读多行字符串

1
2
3
4
5
6
7
8
9
10
char a[15];
char b[15];
cin.getline(a, 15).getline(b, 15);
//使用get()可写成
//cin.get(a, 15).get();
//cin.get(b, 15).get();
cout << a << b << endl;
//输入ciallo
//输入 young
//ciallo young

空行问题 get()/getline()看到前一个队列中留下的换行符会把当前行认为是空行

1
2
3
4
5
6
7
8
9
char a[15];
int n = 0;
cin >> n;
cout << "n=" << n << endl;
cin.getline(a, 15);
cout << "a=" << a << endl;
//12
//n=12
//a=
1
2
3
4
5
6
7
8
9
10
char a[15];
int n = 0;
(cin >> n).get(); // 使用get()变体读取下一个字符
cout << "n=" << n << endl;
cin.getline(a, 15);
cout << "a=" << a << endl;
/12
//n=12
//young
//a=young

其他函数

strcpy(a,b) 将b赋值给a strncpy()

strcat(a,b) 将b加到a的尾部 strncat()

C++ string类

string包含在std中,可以使用也可以std::string

与char数组的区别:string可以声明为简单变量而不是数组;string可以将一个string赋值给另一个string而不必在意字符串大小;string简化了字符串拼接(不需要使用cstring函数即可快速完成对字符串的操作)

1
2
3
4
5
string a = "mryoung";
string b = " ciallo ";
b += a;
cout << b << endl;
//ciallo mryoung

a.size() string确定字符串长度,在这里a是一个string对象size()是一个类方法,而strlen(b)是一个函数b是一个变量 只计算可见字符

1
2
string a ="young";
cout << a.size() << endl;

C++字符串初始化

1
2
3
4
char a[] { "young" };
char a[] = { "young" };
string a { "young" };
string a = { "young" };

C++字符串输入

直接cin输入与C风格字符串基本无异

1
2
3
4
5
string b;
cin >> b;
cout << b << endl;
//输入mr young
//输出mr

读取一行 getline(cin,a)这个并非istream的类方法,istream没有处理string的类方法

1
2
3
4
5
string a;
getline(cin,a);
cout << a << endl;
//输入mr young
//输出mr young

原始字符串

通过 “( 和 )” 来界定原始字符串

1
cout << R"(\0 \ \t)" << endl;

结构

声明、定义、使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct inflatable{
int num;
double num2;
char num3[];
};
// 结构体可以匿名(不写标识符inflatable)

inflatable a;
struct inflatable b;
// C++声明结构时可以省略关键字struct

a.num = 1;
cout << a.num << endl;
// 1
inflatable c = {
2,
1.5,
"young"
};
cout << c.num3 << endl;
// young
// inflatable c {2,1.5,"young"}; C++中等号可省略
// inflatable c {}; {}为空则结构中的成员均被置为0

结构通常在mian外部声明,在main内部声明只能被main使用而其他函数无法使用(例如main2无法使用在main内部声明的结构) 外部定义的结构的变量是共享的变量,内部则为私有。

1
2
3
4
5
6
7
8
#include <string>
struct inflatable{
std::string name;
int num;
double num2;
char num3[];
};
// 结构成员可以为string

结构数组

结构数组与基本类型数组完全相同

1
inflatable a[10];

结构位字段

位字段:以位为单位来指定一个成员所占用的内存空间

1
2
unsigned int flag1 : 4;	// 4bit
bool flag2 : 1; // 1bit

共用体

union用于节省内存空间

共用体的所有成员共享同一块内存空间,而结构体的每个成员都有独立的内存空间

枚举

枚举有严格的限制


逻辑运算

与 &&

或 ||

非 !

异或 ^

同或 !(a^b)

例题:https://leetcode.cn/problems/xor-operation-in-an-array


内存模型和命名空间

new(运算符) 动态分配内存(自由存储/堆) delete(运算符) 释放

static(关键字) 静态存储

#include

头文件 <head.h>会在标准库中查找 "head.h"会先在当前工作目录或代码目录查找

#ifndef (if not defined) #endif 避免重复定义头文件

1
2
3
4
5
6
7
#define CODE_H_
// CODE_H_已经定义
#ifndef CODE_H_
#define CODE_H_
// CODE_H_不会再定义
// 此处还可以写其他程序结构
#endif

自动变量存储在栈中

动态分配内存

new/delete

1
2
3
4
5
int* ptr = new int(10);
delete ptr;

int* arr = new int[5];
delete[] arr;

智能指针


函数

函数基础

定义、函数原型、调用

函数分为无返回值函数(void)、有返回值函数

函数返回值类型不能是数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void f1();	// 函数原型
int f2(int); // 有参数的函数原型可以不提供变量名
int main() {
int a(2);
int b;
f1(); //函数调用
b = f2(a);
cout << b << endl;
}
void f1() { // 函数定义
cout << "f1 "<< endl;
}
int f2(int n) {
return n + 1;
}

函数使用指针处理数组

1
2
3
4
5
6
7
void f3(int arr[],char a);
void f3(int* arr,char a);
// 只有在此时int* arr才等价于int arr[] ,其他情况下int* arr等价于arr[0]

void f4(int arr[],int size); // OK
void f4(int arr[size]); // NO
// 函数数组大小应当通过另一个参数指出

Tips:直接传递数组名时,函数访问的是原本数组;而将数组封装在类或结构体中按值传递时,函数访问的是副本

1
2
void show_arr(const int arr[],int n);
// 可以在传递数组时使用const保护原始数组(原始数组可以不是const)

数组区间函数

1
2
3
4
5
6
7
8
9
10
11
12
13
int sum_arr(const int* begin, int* end);
// begin指向arr[0],end-1指向arr[SIZE]

int arr[SIZES];
sum_arr(arr,arr+SIZE);

int sum_arr(const int* begin, int* end){
const int* pt;
int total = 0;
for(pt = begin;pt != end;pt++)
total += *pt; // *pt指向元素的值
return total;
}

函数指针

函数指针,恐怖如斯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int f1(int);	// 返回值为int
int(*a)(int);
a = f2;

void f2(); // 无返回值
void (*a); // void (*a)();
a = f2;

void f2(int); // 无返回值
void (*a)(int);
a = f2;

void f2(int,double); // 无返回值
void (*a)(int,double);
a = f2;

标准库函数

strlen() 确定C风格字符串长度

函数探幽

内联函数

用内存换速度 内敛函数使用内敛代码替换函数调用从而节省了函数调用花费的时间但是会占用较多内存

内敛函数为C++新增,原始实现为C的宏#define 但是宏无法按值传递,如果宏执行了类似函数的功能应当将其替换为内联函数

内联函数不能递归

1
2
inline  double a(double x){return x*x;};
// 函数声明、定义前加上关键字inline

引用变量

必须在声明引用变量时初始化,引用更类似于const指针(也可以说是const指针的伪装表示)

1
2
3
4
5
6
7
8
9
int a = 1;
int & b; // ERROR
b = a; // ERROR

int* pt1; // OK
pt1 = &a; // OK

int * const pt2; // ERROR
pt2 = &a; // ERROR

引用不是原来变量的副本

1
2
3
4
5
6
7
8
9
int a = 1;
int & b = a;
cout << &(b)<<'\n'<<&(a) << endl;
//00000091E796F874
//00000091E796F874

b++;
cout << a << endl;
//2

C++允许按引用传递参数,C只能按值传递参数(函数使用值的拷贝),C++按引用传递可以访问调用函数中的变量

1
void f(int & a,int & b);

函数重载

允许使用多个同名函数 允许重载的关键是函数的参数列表

函数重载不区分const和非const变量

函数模板

用来创建通用函数 类似蓝图或公式

用于生成可以处理相同逻辑不同数据类型的函数

关键字template、typename,函数名可以随意(AnyType) 模板不创建函数而是告诉编译器如何定义函数

1
2
3
4
5
6
7
8
9
10
11
template <typename AnyType>
void a(AnyType a,AnyType b){
// code
}

template <class AnyType>
void a(AnyType a,AnyType b){
// code
}
// typename关键字出现之前使用的是class

并非所以的模板参数都必须是泛型(AnyType)

1
2
3
4
template <typename AnyType>
void a(AnyType a,int b){
// code
}

重载的模板

1
2
3
4
5
6
7
8
template <class T>
void a(T a,T b){
// code
}
template <class T>
void a(T a,int b,int c){
// code
}

具体化包括 显式实例化、隐式实例化、显式具体化 相同之处在于,它们表示的是使用具体类型的函数定义而不是通用描述

显示具体化

非模板 > 显示具体化 > 模板化

1
2
3
4
template<> void f<int>(int,int);
template<> void f(int,int);
// 显示具体化 <int>可选
// 不能使用f()模板生成函数定义

显式实例化

1
2
template void f<int>(int,int);
// 使用f()模板生成int类型的函数定义

模板函数

模板函数是函数模板经过实例化后得到的具体函数

模板函数中的数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class T1,class T2>
void ft(T1 x,T2 y){
...
?type? xpy = x + y;
...
}
// 不知道xpy的类型

template <class T1,class T2>
void ft(T1 x,T2 y){
...
decltype(x+y) xpy = x+y;
...
}
// C++11关键字decltype
/*
int x; // 假设只有一个x
decltype(x) y; // 定义y并将y的类型变为x的类型
decltype(x+y) xpy; // 定义xpy并将xpy的类型变为x+y的类型
xpy = x + y;
至此?type? xpy = x+y;写为了decltype(x+y) xpy = x+y;
*/

模板函数中的返回值类型

使用后置返回类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class T1,class T2>
?type? gt(T1 x,T2 y){
...
retuen x + y;
...
}
// 不知道返回值类型
// 由于声明后才可以用decltype(x+y)因此decltype本身无法解决这个问题

template <class T1,class T2>
auto gt(T1 x,T2 y) -> decltype(x+y){
...
retuen x + y;
...
}
// C++新增后置返回类型
/*
double h(int x,float y);
auto h(int ,double y) -> double;
// -> double 成为后置返回类型
// auto是一个占位符
*/

C++ 标准库

std::

加上using namespace std;可以直接使用 cout 和 endl,无需写 std::

1
cout << "Hello, World!" << endl;

std 指的是标准命名空间(Standard Namespace)。使用 using 声明可以将 std 命名空间中的某个特定标识符引入到当前作用域

ostream(类)

成员函数(方法) put()/write()

成员函数(方法) width() 调整字段宽度 只影响接下来被显示的一个项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int w = cout.width();//字段宽度默认为0
cout << "当前字段宽度 = " << w << endl;
int w2 = cout.width(30);//将字段宽度设置为30
cout << "当前字段宽度 = " << w2 << endl;//w2字段宽度为0是因为
//width()只影响接下来被显示的一个项目
//当前字段宽度 = 0
// 当前字段宽度 = 0

for (int i = 1; i <= 100; i*=10) {
cout.width(8);
cout << i << ':';
cout.width(8);
cout << i * i << ":\n";
}
// 1: 1:
// 10: 100:
// 100: 10000:

成员函数(方法) fill() 填充字符 cout默认空格填充,cout.fill(‘*’)可指定填充字符 只影响接下来所有项目

1
2
3
4
5
6
7
8
9
10
cout.fill('*');
for (int i = 1; i <= 100; i*=10) {
cout.width(8);
cout << i << ':';
cout.width(8);
cout << i * i << ":\n";
}
//*******1:*******1:
//******10:*****100:
//*****100:***10000:

成员函数(方法) precision() 设置浮点数精度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
float a = 0.123456789;
cout << a << endl;
cout.precision(9);
cout << a << endl;
//0.123457
//0.123456791 这里存在浮点数精度问题
double b = 0.123456789987654321;
cout.precision(18);
cout << b << endl;
//0.123456789987654317
long double c = 0.123456789987654321123456789;
cout.precision(24);
cout << c << endl;
//0.123456789987654316775867

控制符std::endl 换行、刷新输出缓冲区

控制符std::flush 刷新输出缓冲区(与endl刷新功能类似)

控制符dec/hex/oct

1
2
3
4
5
6
int a = 10;
cout << a << endl;
cout << hex << a << endl; // 十六进制
cout << oct << a << endl; // 八进制
cout << dec << a << endl; // 十进制
// 10 a 12 10

istream(类)

对象 cin

方法 getline()/get()

数学

逻辑运算符

C++运算符:https://www.runoob.com/cplusplus/cpp-operators.html

与 全为真则为真 ∧

或 存在真则为真 ∨

同或 输入相同则为真

异或 输入不同则为真

非 取反

与非 与后对结果取非

或非 或后对结果取非

蕴含 https://www.bilibili.com/video/BV1Tf421Z7g3

VS2022基础

创建空项目

生成解决方案文件、项目配置文件

解决方案文件
.sln(Visual Studio Solution):通过为环境提供对项目、项目项和解决方案项在磁盘上位置的引用,可将它们组织到解决方案中。比如生成 Debug 模式还是 Release 模式,是通用 CPU 还是专用的等设置信息也包含其中。该文件可以在开发小组的开发人员之间共享。
.suo(Solution User Operation):解决方案用户选项,记录所有将与解决方案建立关联的选项,以便在每次打开时,它都包含用户所做的自定义设置,如 VS 布局、断点信息以及项目最后编译但未关掉的文件等。.suo 文件是用户特定的文件,不能在开发人员之间共享。
.sdf(SQL Server Compact Edition Database File):工程的信息保存成的数据库文件,用于提供代码自动提示功能和跳转功能。如果没有参加大型团队项目,不涉及高深的调试过程,这个文件用处不大,可以放心删除。若之后又需要,打开工程里的 .sln 文件重新编译链接即可。

项目配置文件
.vcxproj:真正的项目配置文件,以标准 XML 格式记录工程的所有配置,如包含的文件名、定义的宏、包含的头文件地址、包含的库名称和地址、系统的种类等,还可使用过滤条件决定配置是否有效。
.vcxproj.filters:项目下文件的虚拟目录,用于在打开 Visual Studio 时组织显示项目模块的划分。
.vcxproj.user:用户配置文件,用于保存用户个人的数据,如配置 debug 的环境 PATH 等。
.props:属性表文件,用于保存一些配置,可根据需求导入到项目中使用,使用起来很灵活,比如使用开源库时,可将相关配置保存起来,每次新建项目将配置加进来,避免重复配置。

其他文件
源文件(如 .c、.cpp 等):包含项目的源代码,实现项目的具体功能逻辑。
头文件(如 .h):包含函数、类、变量等的声明,用于在多个源文件之间共享声明信息,方便代码的组织和复用。
资源文件(如 .rc):用于存储项目所需的资源,如图标、菜单、对话框等。这些资源可以在程序中通过代码进行加载和使用,以提供更好的用户界面和交互体验。