basis

大端序&小端序

大端字节序(Big-endian)
高位字节存储在低地址处,低位字节存储在高地址处。
一个4字节的整数0x12345678
0x12 | 0x34 | 0x56 | 0x78
(从左侧的低地址到右侧的高地址)
小端字节序(Little-endian)
低位字节存储在低地址处,高位字节存储在高地址处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main() {
int num = 1;
// 将int类型指针转换为char类型指针,取第一个字节
char* ptr = reinterpret_cast<char*>(&num);

if (*ptr == 1) {
std::cout << "Little-endian" << std::endl;
} else {
std::cout << "Big-endian" << std::endl;
}
return 0;
}

这个代码可以检测字节序
网络传输:通常使用大端字节序(Big-endian),也称为网络字节序,这是 TCP/IP 协议的规定,多字节数据在网络上传输时使用大端字节序。
因此,如果本地系统使用的是小端字节序,那么就需要在传输之前将其转换为大端字节序。一般通过使用htonl()、htons()、ntohl()和ntohs()等函数来完成

const的作用

修饰变量

对于确定不会被修改的变量,应该加上 const,这样可以保证变量的值不会被无意中修改,也可以使编译器在代码优化时更加智能。
define a 10 -> const int a = 10

修饰函数参数

1
2
3
4
void func(const int a) {
// 编译错误,不能修改 a 的值
a = 10;
}

这样写函数体内部也不能改变参数值了

修饰函数返回值

当 const 修饰函数返回值时,表示函数的返回值为只读,不能被修改。这样做可以使函数返回的值更加安全,避免被误修改。

1
2
3
4
5
6
7
8
9
10
const int func() {
int a = 10;
return a;
}

int main() {
const int b = func(); // b 的值为 10,不能被修改
b = 20; // 编译错误,b 是只读变量,不能被修改
return 0;
}

修饰指针或引用

在 C/C++ 中,const 关键字可以用来修饰指针,用于声明指针本身为只读变量或者指向只读变量的指针。

指向只读变量的指针:指针本身可以被修改(意思是指针可以指向新的变量),但是不能通过指针修改所指向的变量。

1
2
3
4
5
6
const int* p;  // 声明一个指向只读变量的指针,可以指向 int 类型的只读变量
int a = 10;
const int b = 20;
p = &a; // 合法,指针可以指向普通变量
p = &b; // 合法,指针可以指向只读变量
*p = 30; // 非法,无法通过指针修改只读变量的值

只读指针:const 关键字修饰的是指针本身,使得指针本身成为只读变量。

1
2
3
4
5
int a = 10;
int b = 20;
int* const p = &a; // 声明一个只读指针,指向 a
*p = 30; // 合法,可以通过指针修改 a 的值
p = &b; // 非法,无法修改只读指针的值

只读指针指向只读变量:const 关键字同时修饰了指针本身和指针所指向的变量,使得指针本身和所指向的变量都成为只读变量。

1
2
3
4
const int a = 10;
const int* const p = &a; // 声明一个只读指针,指向只读变量 a
*p = 20; // 非法,无法通过指针修改只读变量的值
p = nullptr; // 非法,无法修改只读指针的值

常量引用:常量引用是指引用一个只读变量的引用,因此不能通过常量引用修改变量的值。

1
2
3
const int a = 10;
const int& b = a; // 声明一个常量引用,引用常量 a
b = 20; // 非法,无法通过常量引用修改常量 a 的值

修饰成员函数

当 const 修饰成员函数时,表示该函数不会修改对象的状态(就是不会修改成员变量)

1
2
3
4
5
6
7
8
9
10
class A {
public:
int func() const {
// 编译错误,不能修改成员变量的值
m_value = 10;
return m_value;
}
private:
int m_value;
};

这里还要注意,const 的成员函数不能调用非 const 的成员函数,原因在于 const 的成员函数保证了不修改对象状态,但是如果调用了非 const 成员函数,那么这个保证可能会被破坏。

static关键字

修饰全局变量

static 修饰全局变量可以将变量的作用域限定在当前文件中,使得其他文件无法访问该变量。 同时,static 修饰的全局变量在程序启动时被初始化(可以简单理解为在执行 main 函数之前,会执行一个全局的初始化函数,在那里会执行全局变量的初始化),生命周期和程序一样长。

1
2
3
4
5
6
7
8
9
10
11
12
// a.cpp 文件
static int a = 10; // static 修饰全局变量
int main() {
a++; // 合法,可以在当前文件中访问 a
return 0;
}

// b.cpp 文件
extern int a; // 声明 a
void foo() {
a++; // 非法,会报链接错误,其他文件无法访问 a
}

修饰局部变量

static 修饰局部变量可以使得变量在函数调用结束后不会被销毁,而是一直存在于内存中,下次调用该函数时可以继续使用。
同时,由于 static 修饰的局部变量的作用域仅限于函数内部,所以其他函数无法访问该变量。

1
2
3
4
5
6
7
8
9
10
11
12
void foo() {
static int count = 0; // static 修饰局部变量
count++;
cout << count << endl;
}

int main() {
foo(); // 输出 1
foo(); // 输出 2
foo(); // 输出 3
return 0;
}

修饰函数

static 修饰函数可以将函数的作用域限定在当前文件中,使得其他文件无法访问该函数。

同时,由于 static 修饰的函数只能在当前文件中被调用,因此可以避免命名冲突和代码重复定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// a.cpp 文件
static void foo() { // static 修饰函数
cout << "Hello, world!" << endl;
}

int main() {
foo(); // 合法,可以在当前文件中调用 foo 函数
return 0;
}

// b.cpp 文件
extern void foo(); // 声明 foo
void bar() {
foo(); // 非法,会报链接错误,找不到 foo 函数,其他文件无法调用 foo 函数
}

修饰成员函数和变量

static 修饰类成员变量和函数可以使得它们在所有类对象中共享,且不需要创建对象就可以直接访问。

1
2
3
4
5
6
7
8
9
10
11
class MyClass {
public:
static int count; // static 修饰类成员变量
static void foo() { // static 修饰类成员函数
cout << count << endl;
}
};
// 访问:

MyClass::count;
MyClass::foo();

typedef、define、using

define:在预处理阶段进行符号替换,故不会有类型检查等功能
typedef:用于为现有类型创建新的名称(别名),在编译阶段处理的,有更严格的类型检查
using:引入名称 + 定义别名 + 继承成员的统一关键词

引入命名空间中的名称

1
2
3
4
5
using std::cout;
using std::string;

cout << "Hello";

创建类型别名(推荐取代 typedef)

宏定义不支持模板,因此不能用于定义模板类型别名。

typedef 可以与模板结合使用,但在 C++11 之后,推荐使用 using 关键字定义模板类型别名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using ll = long long;
using Vec = std::vector<int>;

// 使用 typedef 定义模板类型别名
template <typename T>
struct MyContainer {
typedef std::vector<T> Type;
};

// 使用 using 定义模板类型别名(C++11 及以后)
template <typename T>
struct MyContainer {
using Type = std::vector<T>;
};

继承中,让基类的成员(方法/类型)在子类作用域中可见

1
2
3
4
5
6
7
8
9
10
class Base {
public:
void func(int);
};

class Derived : public Base {
public:
using Base::func; // 让重载恢复可见
void func(double);
};

带 using 的继承声明(C++11 新增语法)

1
2
3
4
5
6
7
8
9
class A {
public:
A(int);
};

class B : public A {
public:
using A::A; // 继承 A(int) 构造
};