这里写自定义目录标题
初始化和赋值
值和标识符
值:10、“hello world”
标识符:变量x、*x、&x、const
初始化和赋值的过程
初始化,在内存空间中开辟了一段内存。赋值将值通过变量名和内存地址关联起来。
不光变量具备类型、变量值也具备类型。
例如:
int x = 3.14; //将double型的值10、赋值给int型的变量。(会有隐式的类型转换)
类型信息 存储空间大小(sizeof)
类型取值范围(numerical_limits)
#include <iostream>
#include <limits>
int main()
{
int x = 10;
std::cout << std::numeric_limits<int>::min() << std::endl;
// 这是一个模板
}
存储空间大小和对齐信息
#include <iostream>
#include <limits>
struct Test{
char a;
int b;
};
int main()
{
int x = 10;
std::cout << "Size of int is: " << sizeof(int) << std::endl;
std::cout << "Size of Test is: " << sizeof(Test) << std::endl;
std::cout << "Align info is: " << alignof(Test) << std::endl; //对齐信息
}
输出为:
Size of int is: 4
Size of Test is: 8 (这里原本数据类型是4+1为5,但是考虑到对齐信息,为8)
Align info is: 4
对齐信息的目的是为了提高运算中缓存cache到内存中读写的速度。
cache读内存是按行来读写,因此应该让一行能存储得了的数据,读写一次即可。 align info是4,表示该变量类型的存储内存地址可以被4整除。例如,一个int型的变量可能会存储在8000(能被四整除)~8003中,而不会存储在7999 ~8002中。
字面值及其类型
字符——‘a’、‘\n’、‘\x4d’ (单引号) char 型
字符串——“Hello” (双引号) char[6]型
自定义后缀类型
float x = 1.3f;
可以由用户自定义后缀
https://en.cppreference.com/w/cpp/language/user_literal
变量定义和声明
/main.cpp
int x = 10; //定义
/another_file.cpp
extern int x; //声明
extern int x = 15; //这个是定义!!!link会报错!!
变量隐式转换
#include <iostream>
int main()
{
unsigned int x = 3;
int y = -1;
std::cout << (x > y) << std::endl; //输出结果为0?
}
// c++ insights
#include <iostream>
int main()
{
unsigned int x = 3;
int y = -1;
// 将int隐式转换为unsigned int型变量
std::cout.operator<<((x > static_cast<unsigned int>(y))).operator<<(std::endl);
return 0;
}
解决方法:(c++20引入)
https://en.cppreference.com/w/cpp/utility/intcmp
#include <iostream>
#include <utility>
int main()
{
unsigned int x = 3;
int y = -1;
std::cout << std::cmp_greater(x, y) << std::endl; //输出1
}
类型别名与类型的自动推导
auto
int main()
{
int x = 3.14 + 15l; // 两次隐式类型转换,会导致精度丢失
//一个 double 和 long 类型相加是什么类型?
auto y = 3.14 + 15l; // 使用auto由编译器自动推导!!
}
1. auto推导的类型退化
去引用
引用类型的变量(左值),作为右值进行赋值运算时。使用auto会被退化为去引用类型。
#include <iostream>
#include <type_traits>
int main()
{
int x = 10;
int& y = x;
auto z = y; // 细品!当你写出这行代码的时候,你认为z的类型是啥?
// 事实上,使用auto来自动推导,会把y去引用,变成int
std::cout << std::is_same_v<decltype(z), int> << std::endl;
} //输出为1
去常量
#include <iostream>
#include <type_traits>
int main()
{
int x = 10;
const int y = x;
auto z = y; // 细品!当你写出这行代码的时候,你认为z的类型是啥?
// 事实上,使用auto来自动推导,会把y去const,变成int
std::cout << std::is_same_v<decltype(z), int> << std::endl; //输出为1
const int& y1 = x;
auto z = y1;
std::cout << std::is_same_v<decltype(z), int> << std::endl;
}
2. 防止类型退化,就要显式的添加说明符&
#include <iostream>
#include <type_traits>
int main()
{
int x = 10;
const int& y1 = x;
auto& z1 = y1; //注意下面z1的自动推导类型!!保留了const!!!
const auto z2 = y1;
const auto& z3 = y1;
std::cout << std::is_same_v<decltype(z1), const int&> << std::endl;
std::cout << std::is_same_v<decltype(z2), const int> << std::endl;
std::cout << std::is_same_v<decltype(z3), const int&> << std::endl;
}
通过观察可以发现,只需在auto后面增加一个&,即可完整推导等号右边变量的类型,而不会发生类型退化。即如果告诉编译器待推导变量的类型是一个引用类型,编译器会防止一切退化推导!!这个非常合理!!例如:
int main()
{
const int& x = 10;
const int& y = x; //没问题! 不能通过y修改x的值,两者都是const!
int& z = x; //错误!! z可以修改x的值!编译器会报错!!
}
decltype
1. 对右值和左值变量名,不会引入任何类型退化
#include <iostream>
#include <type_traits>
int main()
{
int x = 10;
const int& y = x;
auto z1 = y; // z1的类型为int
decltype(y) z2 = x; // z2的类型为const int&
std::cout << std::is_same_v<decltype(z2), const int&> << std::endl;
}
2.对左值表达式(不是左值变量名)会加一个引用
#include <iostream>
#include <type_traits>
int main()
{
int x = 19;
int* ptr = &x;
// *ptr 是一个左值表达式
std::cout << std::is_same_v<decltype(*ptr), int&> << std::endl;
// ptr是一个左值变量,不会推倒后增加一个引用!
std::cout << std::is_same_v<decltype(ptr), int*> << std::endl;
std::cout << std::is_same_v<decltype((x)), int*> << std::endl;
}
3.希望给不是引用的变量名,推导出加上引用的类型,在变量名两边加括号!
4. decltype(auto) 既可以自动推导类型,同时可以防止类型退化
int main()
{
decltype(auto) = 3.14 + 15l;
}