声明
- 建议阅读书籍
- 不会介绍变量声明。函数定义是如何进行的或者规则等基础知识
- 只包含对书中提到的部分知识的理解
程序的执行过程(进程是如何创建的?)
- 将源代码编译成若干目标模块
- 将一组目标模块链接成装入模块
- 由装入程序将装入模块加载到内存
注意:可执行程序是根据特定的硬件平台进行编译生成的,所以不具有可移植性。这个很容易理解,可执行程序是一堆机器指令,不同的硬件平台使用的指令集可能是不同的,所以有的机器可能无法执行该可执行程序的某些指令。但是如果源代码可以在不同的平台编译成功,说明该源代码是具有可移植性的。
关于函数
-
对于普通的函数(非类成员函数)是由返回值类型、函数名、形参列表组成的,所以返回值类型和形参列表都是该函数类型的一部分。
-
对于成员函数,类名是函数类型的一部分。
-
函数声明时不必包含参数名,包含参数类型信息即可,除非声明和定义是一起的。
-
函数重载,参数类型不一样的同名函数,编译器会自动选择最合适的函数进行调用,如果重载函数之间存在二义性,会报错。
#include <iostream>
void func(int a, int b){
std::cout << a + b << std::endl;
}
void func(double a, double b){
std::cout << a + b << std::endl;
}
int main(int argc, char **argv){
func(1, 10.0);//编译不会通过,因为 1这里可以被解释为 int 类型
return 0;
}
- 当进行函数重载时应该具有相同的语义,即所有的重载函数应该实现相同的功能,只是为了适应不同的场景。
关于类型
- 所有的基本类型都直接对应硬件设施,所以类型所占的内存大小是固定的,在不同的硬件平台上可能会不同,可以通过
sizeof
查看类型所占的内存大小
关于算术运算
- 表达式中使用的类型转换称为常规算术类型转换(usual arithmetic conversion),其目的是确保表达式以运算对象中最高的精度进行计算。
关于初始化
- C++提供了很多种初始化变量的方法(很头大,知道常用的即可),通过
=
直接初始化或者使用花括号。两者的区别是使用大括号可以避免类型的隐式转化,避免丢失精度信息。 - 如果一个变量的类型可以通过它的初始值推断出来,可以使用
auto
来代替类型定义,如果不需要显示声明变量类型,建议使用auto
。 - 参数传递和函数返回值的基本语义是初始化,对这话的理解是,函数传参时,会创建一个临时变量并使用传过来的参数初始化该临时变量,函数返回也是一样的,如果返回的是局部变量,那在销毁该变量前,CPU会拷贝该变量的内容到一块临时内存中,也即初始化该内存区域。
关于常量
C++提供两种不可变的概念const
和constexpr
- const修饰的变量的值可以在运行时计算
- constexpr修饰的变量的值是在编译阶段确定的
int a = 10;
const int c = a;//OK
constexpr int b = a;//这种方式是错误的
- 当想要将函数的返回值初始化一个常量表达式时,函数必须使用
constexpr
来修饰,因为要在编译期间求值,该函数参数可以使用非常量,此时返回值也不再是常量表达式。所以使用constexpr
修饰函数可以同时达到两种效果。 - 使用
constexpr
修饰的函数应该尽可能简单,可以包含循环,但是不能修改非局部变量
int global = 0;
//下面的操作是错误的,编译会报错
constexpr int func(int a, int b){
global++;
return a + b;
}
关于数组
- 数组的大小必须是一个常量,如果前提不知道数组的大小,使用malloc来创建动态数组,这个很容易理解,静态开辟的数组空间是在栈区的,运行前就已经分配了地址空间,但是此时还不知道变量的值,那编译器就随便开辟了一块,显然是错误的。
int a = 10;
int arr[a];//这种是错误的做法
关于引用
- 引用不同于指针,是变量的别名,不需要解引用,引用创建必须进行初始化引用对象,且初始化以后不能再引用其他对象。
int a = 10;
int b = 5;
int& ra = a;
std::cout << &ra << std::endl;//0x70fe0c
ra = b;
std::cout << &ra << std::endl;//0x70fe0c
std::cout << a << std::endl;//此时发现a的值变成了 5
运行程序发现,ra的地址前后并没有发生变化,说明ra的引用没有改变,始终引用a
- 向函数中传入引用可以避免值的拷贝,如果不想修改参数,可以加const进行修饰
关于空指针
- 在C中没有nullptr,引入nullptr是为了区别整型和指针,所以不能将nullptr赋值给一个非指针变量。
- 为了确保所有的指针都指向某个对象,当指针没有指向的时候应该指向nullptr,所有的指针共享一个nullptr,表示指针没有指向
关于赋值
- 赋值和初始化是两个不同的概念,赋值对于基本类型来说是机器指令的拷贝,初始化是将内存的内容由未知边已知。
- 用户自定义的类型在赋值之后想
x = y
,应该有x == y
,所以要进行运算符重载。