C/C++学习
第二章:变量
0.前导
成功创建一个变量,需要对他进行定义:确定数据类型,变量名,初始值 对象:通常定义在内存中有一块指定数据类型的数据块即为对象 学习过程:先知道变量的定义——了解数据类型——了解初始化
1.定义与声明:
变量一共有三种状态:带数据类型且赋值(定义),带数据类型不赋值(声明),不带数据类型且赋值(使用)
int i =10 (定义)
int i (声明)
extern int i (外部声明)通常放在头文件中
i++ (使用)
关系:声明可以多次,定义只能一次。对声明的变量进行初始化就是定义,在函数中的变量必须初始化。
在主文件中使用外部变量全过程: 创建源文件,定义变量 创建头文件,声明外部变量 返回源文件导入头文件 在主文件中导入头文件,使用变量 运行时一定要同时编译主文件与源文件
联合编译:同时编译多个文件(只能有一个main文件) 方法一: 同时编译少量文件时可以使用终端运行命令进行联合编译
g++ ./main.cpp ./source.cpp -o test -I ./include
#编译文件的路径 #-o 生成可执行文件的名称 #-I 头文件搜索路径
方法二: 编译大量文件时使用cmake: 4. 多文件编译哔哩哔哩bilibili
2.作用域:
每一个变量根据它被定义的区域确定自己的作用域。即可以使用该变量的区域,超出作用域无法使用该变量。
在函数中定义的变量只能在函数中使用;在for循环中定义的变量只能在循环中使用,但在循环中的其他循环中可以使用该变量,甚至在内层嵌套中可以定义与外层嵌套相同的变量名,并且优先使用内层嵌套的变量
3.数据类型:
基本类型:
int,float,char,······
复合类型:
复合类型由数据类型与类型修饰符组成。类型修饰符有:指针*和引用&
定义:
数据类型 类型修饰符变量名 //类型修饰符是跟随变量名的
引用:给对象起别名,对别名进行运算,赋值等操作都是对该对象进行的操作。
定义:
数据类型 &别名 = 对象 //数据类型必须与对象一致
引用相当于初始化,初始化只能进行一次,也就是说引用后的别名不能再被重新定义为引用另一个对象。并且别名所指的对象的地址就是它的地址
指针:定义一个变量,该变量有自己的内存地址,并且存放着其他变量的内存地址,可以改变其他变量的值。 指针存储的是一个对象
定义:
数据类型 *变量名 = &变量名
例子:
int *p = &a //初始化一个指针变量p,初始值为(&a)即变量a的内存地址
p = &b //赋值,将变量b的内存地址赋值给指针变量p
*p = a //对p解引用(访问指针指向的内存位置上的值),此时*p相当于b的别名,对变量b赋值为变量a
指向指针的指针的···:有多少个在解引用时要该访问最原始的那个对象就要用多少个
引用指针:
定义:
数据类型 *&别名 = 指针变量名
例子:
int *p = 0
int *&r = p //结合前面的引用与指针定义可以看懂
auto类型:
当使用表达式为变量初始化时,会出现无法知道表达式的数据类型的情况,也就导致无法确定变量的数据类型,因此引入auto数据类型,编译器根据表达式即初始值自动分析变量的数据类型。
double val1 ,val2;
auto p = val1 + val2;
auto p = val , *k = &val //要求同一语句定义的变量基本数据类型一致
decltype类型:
当定义变量时想要表达式的数据类型,但不想要初始化表达式的值的情况,可以使用decltype数据类型:
decltype (f()) sum = x ;
自定义类型:
结构体: 联合体: 枚举: 类: 模板:
类型别名:
给类型起别名,使用别名就相当于使用该类型: 使用方法:
typedef int bieming;
typedef bieming biebieming;
biebieming p = 10;
bieming p = 10;
using bieming = int
注意点:
typedef int *bieming; //bieming为整形指针
int i = 10;
bieming p = &i; //bieiming 是指针类型
const bieming p = &i; //bieming包含int*一起给const修饰
4.初始化与赋值:
初始化是在变量创建时赋予一个初始值,赋值是把对象的当前值擦除用新值代替。 初始化类型分为: 拷贝初始化:使用等号(=)将变量或者字面值或者常量赋给新建变量 直接初始化:使用括号()将变量或者字面值或常量赋给新建变量
常量:
字面值常量:固定的具体的数值。有整型字面值常量、浮点型字面值常量、字符型字面值常量,字符串型字面值常量
const限定符:(变量值常量)
前提:const限定符与数据类型结合,形成相应的常量类型。const具有左结合性,也就是const是与在它左边的数据类型进行结合的,如果左边没有则结合右边第一个数据类型。就是const是在程序运行时确定的,也就是在部分情况下是可以改变的。 const与基本数据类型: 如果const用于修饰基本数据类型(如int、float、double等)的变量,那么这个变量的值在初始化后就不能被改变。
const int a = 10; //const常量与int结合形成整形常量
a = 20; // 错误,不能修改const变量的值
const与指针类型: const int p :const与int结合,表示指针常量,指针指向常量,不可更该常量 int * const p :const与int结合,表示常量指针,指针是常量,不可更改指针,可以更改指针的值
int x = 10;
int y = 20;
const int* ptr1 = &x; // 指针指向的内容是常量,底层const
*ptr1 = 30; // 错误,不能通过ptr1修改x的值
ptr1 = &y; // 正确,可以改变ptr1指向的地址
int* const ptr2 = &x; // 指针本身是常量 ,顶层const
*ptr2 = 30; // 正确,可以通过ptr2修改x的值
ptr2 = &y; // 错误,不能改变ptr2指向的地址
const与引用类型:如果const用于修饰引用,那么引用的对象在初始化后不能被改变。但是被引用的对象可以改变
int value = 10;
const int& ref = value;
ref = 20; // 错误,不能通过引用修改对象的值
value = 100; //被引用对象可以修改值
printf(ref) //因为引用与被引用对象共享内存地址即共享值,所以ref值被修改
成员函数:在类中,如果成员函数被声明为const,则该函数不能修改类的成员变量(非静态成员),也不能调用类中没有被声明为const的成员函数。
class MyClass {
public:
void modify() { /* 可以修改成员变量 */ }
void print() const { /* 不能修改成员变量 */ }
};
总的来说,const限定符确保了变量、指针、引用或成员函数的某种“不变性”,从而在编译时提供了一种检查机制,帮助程序员避免无意的修改,增强程序的稳定性和安全性。
constexpr变量: 声明为constexpr的变量一定为常量。在编译时就确定的,意味着这个变量必须为常量:
int * const h = nullptr;
constexpr int* j = h; //正确,赋值时是常量
int k = 10;
constexpr int y = k; //错误,赋值不是常量
将指针声明为constexpr时指针为常量指针:
constexpr int* i
int* const i
//俩者相同
初始化:
使用字面值常量初始化:
char j = {'0'};
std::cout<<j<<std::endl;
std::string h{"00"};
std::cout<<h<<std::endl;
char b('0');
std::cout<<b<<std::endl;
除了标准初始化语句,使用这三种语句同样可以初始化 默认初始化:定义变量时没有指定初值,则进行默认初始化。在函数中的变量如果没有指定值将不会初始化,成为未定义变量,在访问时会出现错误。
使用变量或变量值常量初始化: 要保证用来初始化的变量或变量值常量的数据类型与定义的变量的数据类型一致
float i = 8.23;
int k = i ; //能够成功是因为,在执行语句时,float类型的数据被自动转换成int类型再进行初始化
const int i =10;
int* k = &i; //错误,因为i是变量字符常量,k不是变量值常量
数据转换:
将不同数据类型的数据赋值给另一个数据,赋值结果取决于左边数据的数据类型。 如果是普通数据类型的变量可以自动转化,复合类型转换需要额外操作。 当unsigned类型的数据与signed类型的数据进行运算时,signed类型的数据在运算时会被转换成unsigned类型,当计算结果有符号,输出的结果就会错误。