第一章 基础知识

文章讨论了C++中的函数定义、程序执行过程、可移植性概念。提到了函数重载的规则和作用,以及基本类型的内存大小和算术运算中的类型转换。还涵盖了变量初始化、常量表达式(const和constexpr)的区别,数组、引用和指针的使用,包括空指针nullptr和赋值操作的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

声明

  • 建议阅读书籍
  • 不会介绍变量声明。函数定义是如何进行的或者规则等基础知识
  • 只包含对书中提到的部分知识的理解

程序的执行过程(进程是如何创建的?)

  1. 将源代码编译成若干目标模块
  2. 将一组目标模块链接成装入模块
  3. 由装入程序将装入模块加载到内存

注意:可执行程序是根据特定的硬件平台进行编译生成的,所以不具有可移植性。这个很容易理解,可执行程序是一堆机器指令,不同的硬件平台使用的指令集可能是不同的,所以有的机器可能无法执行该可执行程序的某些指令。但是如果源代码可以在不同的平台编译成功,说明该源代码是具有可移植性的。

关于函数

  • 对于普通的函数(非类成员函数)是由返回值类型函数名形参列表组成的,所以返回值类型和形参列表都是该函数类型的一部分。

  • 对于成员函数,类名是函数类型的一部分。

  • 函数声明时不必包含参数名,包含参数类型信息即可,除非声明和定义是一起的。

  • 函数重载,参数类型不一样的同名函数,编译器会自动选择最合适的函数进行调用,如果重载函数之间存在二义性,会报错。

#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++提供两种不可变的概念constconstexpr

  • 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,所以要进行运算符重载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值