1. inline函数:
特点:在编译过程中,该函数在函数调用点,就将该函数的代码全部展开,会进行类型的检查。
(1) inline函数是一种更安全的宏:
宏在预处理阶段进行替换,只是单纯的字符串的替换,并不会进行类型检查,宏没有办法调试,inline函数在编译阶段在函数调用点将函数的代码展开,但会进行类型检查,因此称为更安全的替换
(2) inline函数和普通函数的区别:
inline函数没有标准函数栈帧的开辟和回退;
inline函数不产生符号,只能用在本文件当中
(3) inline函数和static函数的区别:
1.作用域:inline函数和static函数都是在本文件中可用;
2.栈帧开辟回退:inline函数没有栈帧的回退,但static函数有栈帧的回退;
3.符号的产生:inline函数编译后不产生符号,static函数编译时产生符合(local)
(4) 普通函数和static函数区别:
static函数只是本文件可见-》即普通函数和static函数产生的符号不同,普通函数产生globle符号,static修饰的产生local符号,连接器只处理globle符号,不会处理local符号。
*inline函数只是在release版本起作用;inline函数在debug版本,该版本需要调试,该函数的调用也需要栈帧的开辟和回退,inline函数只是对编译器的一个建议。
====================================================================================================================================
2. 函数的重载:
C语言中--à函数产生符号->由函数名决定
C++中----à函数产生符号->由函数名称+形参类型+形参个数
C++构成函数的重载:
1.函数名相同,参数列表不同,不能仅通过返回值不同进行函数的重载(因为函数产生符号,和函数的返回值没有关系)
2.重载必须处于同一个作用域;
3.对实参的值是否有影响(const /volatile */&可以作为函数重载的前提条件)
如:void func(int *a)与void func(const int *a)就可以构成重载
Volatile关键字作用:
1. 防止编译器对汇编指令进行顺序上的优化:
防止编译器对指令顺序的调优----使用volatile
防止程序运行时CPU对指令顺序上的调优-------barrier()
2. 防止寄存器存储变量的副本值:
主要应用于多线程程序中
如:int a = 10;
void func()
{
a = 20;
}
Thread1 func();
Thread2
{int b =a;//mov eax,dword ptr[a]
mov dword ptr[b],eax
Int c = a;// mov dword ptr[c],eax!!!!
}
====================================================================================================================================
3. Const:
用const修饰的量不能再被修改
(1)在C中的const:经const修饰后不能再被修改,const修饰的量并不是一定要初始化;但若不初始化后期也不可以再被赋值----à在c中const修饰的量是一个常变量而不是常量。
在C中const修饰的常变量和普通的变量唯一区别:
常变量并定义以后,不可以再作为左值。(即const修饰的常变量也是globel的)
(2)在C++中的const:
(定义时初始化,常量->常变量,作用域是local的)
1.必须要初始化(和c不同)
2.在C++中,const修饰的量是常量(明确的初始值)
3.在C++中,const的编译规则(和c不同):所有使用常量名字的地方全部替换成常量的初始值(类似于宏,inline函数)
4.Const修饰的常量什么时候变成常变量?
当引用一个编译阶段不明确的值的时候,会变成常变量
如:int main()
{ Int b = 20;
const int a = b;//成为一个常变量
int arr[a] = {0};// a是一个常变量, 编译出错
}
5.在C++中,const修饰的量如:constint data = 20;生成的符号都是local的,即当前文件可见,不可以在其他文件使用(可以在const修饰的常量的定义处,加extern使生成的符号变成global)
====================================================================================================================================
4. 引用&:
(1) 引用不参与类型;
(2) 定义引用和定义指针相同,同样需要开辟内存;
(3) 从汇编指令来看,定义一个引用变量和指针变量以及分别给变量赋值,是一模一样的。从底层来说,指针和引用是一样的,但从语法上来说并不相同;
引用规则:
(1)必须初始化;
(2)初始化的值必须要能够取地址(若右值不能取地址,需要加const产生临时变量);
(3)一经引用不能改变;
(4)访问引用变量(由于会自动解引用),永远访问的都是它所引用的内存,引用会比指针更安全一些,因为指针可能会出现无效指针。
Q:定义引用变量是否开辟内存?开辟内存。
A:之所以有些书上说引用变量没有内存,是因为引用变量会自动解引用,访问引用变量时访问的是所引用的内存,而不会是引用变量开辟的内存。
5. Const和一级指针的结合:
const int *p;(*p不能修改,p可以修改)
int const *p;(*p不能修改,p可以修改)
int *const p;(*p可以修改,p不能改)
const int *const p; (*p可以修改,p不能改)
const int *-->int * 错误!
Int *------->const int * 正确;
如:
int a = 10;
const int *p = &a;//正确
int *q = p; //错误,
由于p既可以指向const int a = 10;也可以指向int a = 10;所以q并不知道是变量还是常量,所以错误。
Const如没有修饰*/&时,并不考虑const
如:
Void func(int a)
Void func(const int a)//不可以构成重载。
====================================================================================================================================
6. Const和引用的结合:常引用
&必须要和引用变量写在一起,常量只能用常引用:
const &引用常量:可寻址的常量和不可寻址常量(产生一个临时量)
const只有结合&时会产生临时量,和*结合不会产生临时量。
例子:
const int a =10;const int &b = a;
int &a = 10;(错误)
引用必须初始化,并且初始化不能是一个立即数,因为无法取地址
const int &a = 10;(正确)
加上const,产生了一个临时量,a中的地址是临时量的地址。
====================================================================================================================================
7. Const和二级指针的结合:(见笔记)
8. 动态内存的开辟
malloc---->free new------>delete
new->malloc:new作用一是开辟内存;二是初始化(内置类型是初始化,自定义类型 调用析构函数)
开辟100个元素的整型数组:
int *p = (int *)malloc(sizeof(int)*100)----->free(p)
int *p = new int[100]//此时不能给数组元素赋值
delete[]p;
new:::
(1)new开辟内存失败,不会返回NULL,而是抛出一个异常:throw bad_alloc();
(2)不抛出异常的版本:int *p = new(nothrow)int(10);此时是判断返回值是否为NULL;
(3)定位new placement new
char buff[1024] = {0};
char *p = new(buffer)char [100];//在buff上开辟一个100个元素的数组;
开辟内存与初始化分开进行:
Int *p = new int;
P = new(p)int(10);
(4)在堆上开辟常量
const int *p = new const int(4);
cons tint *q = new cosnt int [10]
====================================================================================================================================
9. C/C++的作用域:
(1)C的作用域:局部作用域,全局作用域;
C++的作用域:
局部作用域,类作用域,
名字空间作用域namespace(全局的名字空间作用域,局部的名字空间作用域)
例:定义一个名字空间,Myname-à定义一个新的作用域
Namespace Myname(名字不参与编译,不产生符号)
{
}可以定义多个同名的名字空间,编译时会合并;
using 指示符
usingMyname :: gdata;
usingnamespace Myname;表示将Myname中所有符号放于全局作用域
usingnamespace std;//表示std名字空间下所有符号放在全局作用域。
====================================================================================================================================
10. C/C++之间的互相调用
可以看见源代码:
(1) 在.cpp文件中调用.c文件的函数:
产生问题:在.c文件中函数产生的符号和.cpp中产生的符号不同,因为无法编译通过。
解决方法:用extern “c”解决,extern c表示里边的符号都是按照c规则生成,所以生成符号时,按照c产生函数符号
(2) 在.c文件中调用.cpp文件中函数:
产生问题:在.c文件中函数产生的符号和.cpp中产生符号不同,
解决办法:将cpp源码括在“extern “c””中,从而产生的符号和c产生的符号相同,从而.c文件中可以调用.cpp文件中函数。
Q:静态链接和动态链接的区别:静态库(.lib/.a)动态库(.dll/.so)
A:静态链接:生成可执行文件之前就进行链接;占用磁盘空间较多但是速度更快;
动态链接:程序运行时连接;
不可见源代码(已经是库函数,是可执行文件):
(1)在.cpp文件中调用.c库中的函数:
用c写的.so库,已经是可执行程序,在cpp文件中在声明处加”extern c”
(2)在.c文件中调用.cpp库中的函数:
由于此时.cpp已经是可执行程序,看不到源代码,因此不能再使用”extern c”括上源代码执行;
解决办法:使用一个中间.cpp文件,包含库,使用”extern c”,即每个临时.cpp文件在自己封装一个接口。
====================================================================================================================================
11.const */&在函数当中的应用
函数的返回值:
(1)<=4 bytes;通过寄存器带回,不产生临时量
(2)>=4 bytes <=8 bytes;通过寄存器带回,不产生临时量
(3)>8bytes;产生临时量
(4)在C++中,当返回一个对象时,不论该对象占用多大内存,都要产生临时量
*函数返回值均为值返回时:
当返回值为内置类型:产生都是寄存器的立即数;
当返回值为自定义类型,当小于等于8字节,指针指向或者引用时,编译器自动产生临时量;
当返回值为一个对象,一律产生临时量,并把临时量的实参传递进来,被调用函数通过访问ebp+8能够访问调用方的临时对象内存地址。
(当函数返回*或者&,再加以讨论)
产生临时量的三种情况:
(1)函数调用之前;(2)函数的return语句;(3)函数调用之后;
*只有const &时,才有产生临时变量的可能。
常量与常变量:
在c中:const int a = 10;常变量
在cpp中:const int a = 10;常量,编译时和c也不同,是直接替换的;
const int a = b;常变量
const int a = 10;
static int a = 10;
//两个都是本文件可见,生成的符号均为local,若加上“extern”关键字,会使“local”变为“global”
内置类型产生的临时量都是常量(在寄存器中),不可更改;
自定义类型产生的临时量都是变量(在内存中),可以更改;
编译器的类型转换(做转换的目的:提高运算的精度):
double<-----------------float
向上转化
unsigned
向上转化
long
向上转化
int <-----------------------short/char