C和 C ++ 的区别
1.内联函数: 在函数的调用点,把函数的代码全部展开,编译阶段(一种更安全(进行类型检查)宏)
宏: 预编译阶段(字符串替换,不进行类型检查,无法调式)
内联函数和普通函数的区别:
内联函数没有标准的函数栈帧的开辟和回退
普通函数:
函数调用的开销大于函数执行的开销->写成内联函数
内联函数本文件可见(不产生符号),一般写在头文件中
内联函数和static函数的区别:
本文件可见,作用域相同
Static有正常的函数栈帧的开辟和回退
内联函数不产生符号,静态函数产生符号
Static()产生的符号:是local的 (链接器只处理global的符号)
符号解析-〉符号分配内存虚拟地址-〉符号重定位 针对global符号
Inline只在release版本起作用
Debug版本里面,inline函数的调用也需要栈帧的开辟和回退(方便调试)
Inline只是对编译器的一个建议 编译器来决定
递归函数不可能处理成内联函数(编译阶段不知道函数展开几次,只有在运行时才知道)
2.函数重载:(1.函数名相同,参数列表不同,不能仅通过返回值不同;2.作用域相同;3.对实参的值是否有影响 const/volatile修饰指针/引用)
C:函数产生符号 根据函数名称
C++:函数产生符号 函数名称+形参类型+形参个数
3.多态
静多态:重载和模板
动多态:继承派生里的多态(虚函数)
C++允许不同作用域产生相同的符号,函数调用时先在局部找,若有,就调用局部的函数
编译器的类型转换:
横向:无条件转换,纵向:当出现纵向的两个类型,下面的转换为上面的(提高运算精度)
C和C++代码之间的互相调用
在C++中调用C的函数:
只需要extern”c”声明
在C中调用C++的函数:
Extern”c” //里面的符号都是按照C规则生成的
{
函数声明;
}
把.cpp文件里的源代码括在extern”c”中,这样.cpp里的代码就会用c编译器编译,或在.cpp文件中加#ifdef_cplusplus #endif,如果用C++编译器编译的就会按照C风格编译,如果是C编译器,也是会按照C风格编译,这样就保证了不管是C编译器还是C++编译器编译,都会按照C风格编译
当C++以库的形式提供源代码,在C中如何调用:(间接调用(重新封装))
静态链接和动态链接的区别:
静态链接:链接.lib库(在可执行文件生成之前链接,快)
动态链接:链接.dll库(程序运行以后链接,用到哪个才会链接哪个,减少可执行文件的大小)
4.Const
C:常变量,不能修改,不能做左值,不是必须初始化的,不能做数组下标
Const修饰的常变量和普通变量的唯一区别:两者编译方式完全一样
常变量定义以后,不能作为左值存在
C++:必须初始化,可以做数组的下标
Const的编译规则(跟宏,inline的编译方式比较像) 所有在使用常量的名字的地方全部替换成常量的初始值
常量-〉常变量 用一个变量去初始化一个常量(编译阶段引用不明确的值得时候),常量退化为常变量
C++中const生成的符号是local的,只允许本文件可见
在const常量定义处外加extern ->global
5.引用:
1.必须要进行初始化
2.初始化的值必须可以取地址
3.引用不可以改变
4.访问引用变量,永远访问它所引用的内存
定义引用变量是否开辟内存 -〉 开辟内存
引用底层就是用指针实现的 用到引用变量的时候会自动解引用
Constint *p;
Intconst *p;
Int*const p; //常量
Constint *const p; //常量 const直接修饰了变量p
常量被修改:
直接:做左值
间接:常量的指针或者引用泄露出去
Const与一级指针结合:
constint*->int* 错误 int*->const int* 正确
普通指针可以用普通指针指向也可以用常量指针指向
Inta=10;
Int*p=&a;
Int*const p=&a;
Constint *p=&a;
Inta=10;
Constint *p=&a;
Int*q=p;
//不可以,因为p可以存变量的地址也可以存常量的地址,一旦指向const int a,就会出问题。
#include<typeinfo>
Cout<<typeif(p).name()<<endl;
对编译器来说,const如果没有修饰指针/引用,不用考虑const
Volatile:
1. 防止编译器对汇编指令进行顺序上的优化
2. 防止寄存器存储变量的副本值 应用在多线程程序中
Barrier:防止运行时cpu对指令顺序上的调优
Const&的结合: const int &b int const &b
Int *const ptemp=(int*)0x0018ff44; int *const&p=ptemp;
Int*const &p=(int*)0x0018ff44;
*p=10;
Const引用常量包括可寻址的常量和不可寻址的常量(都需要常引用)
当引用一个不可寻址的常量时,系统会自动产生一个临时量让我们来引用
引用不参与类型
Const跟二级指针的结合:
类型必须完全匹配
Constint **q;
Int*const *q;
Int** const q;
Int a=10;
Const Int *p=&a;
Constint **q=&p; //**q<->*p *q<->P
//const int b=10; *q=&b; 即:p=&b(普通指针指向常量的地址)
//errorp有可能指向常量的地址 p可能修改常量的值 int**->cons tint**
Error: Const int*->int*; Int **->cnst int**; cons tint**->int**
Const在中间转化为一级指针
Inta=10;
Const Int *p=&a; //如果p指向常量的地址,会有修改常量的隐患
Constint **q=&p; //*q==p
Inta=10;
Int *p=&a; //如果p指向常量的地址,会有修改常量的隐患
Constint *const*q=&p; //*q==p
Inta=10;
Const Int *p=&a;
Constint *&q=p; // p==q 同上
Inta=10;
Int*p=&a;
Int*const *q=&p;//(const前面的指针/引用不起作用) ok: int *->const int*
Int a=10;
Int*p=&a;
Int** const q=&p; //ok :const没有修饰指针/引用
Inta=10;
ConstInt *p=&a;
const Int **q=&p;
//error: cons tint**->int** p如果指向常量的地址,会有风险
Inta=10;
Int*const p=&a;
Int*const*q=&p; //error: const int*->int * p的地址被泄露出去了
函数的返回值:
x<=4 eax
4<x<=8 eax edx
>8 调用之前产生临时量 函数调用时,先压实参,把临时量的地址当最后一个参数压栈,通过ebp+8返回
临时量产生:
函数调用之前;
函数的return之时;
函数调用之后;
引用:引用必须初始化 引用必须可以取地址 一经引用不能再引用其他变量
寄存器带回的值不可以取地址
要引用常量,而且常量没办法取地址,可以通过常引用来引用(可以产生临时量),引用的是临时量的地址
不能返回局部变量的指针/地址,作用域过了
6.New: 先开辟内存 再初始化
1.开辟内存
2.初始化自定义类型的变量(初始化类的对象)
New->malloc 初始化(内置类型 自定义 调用构造函数)
Delete->析构 free
Malloc参数>=0都会分配内存 空闲链表 位图
Eg:申请一个字节,其实会向内核申请好多页面, 其他的空闲内存可供下一次申请内存时使用,减少用户——〉内核的访问,节省时间
new和malloc的区别:
*存储区(free store)是C++两个动态内存区域之一,使用new和delete来予以分配和释放。在*存储区(free store)中,对象的生存周期可以比存放它的内存区的生存周期短;这也就是说,我们可以获得一片内存区而不用马上对其进行初始化;同时,在对象被销毁之后,也不用马上收回其占用的内存区。在对象被销毁而其占用的内存区还未被收回的这段时间内,我们可以通过void*型的指针访问这片区域,但是其原始对象的非静态成员以及成员函数(即使我们知道了它们的地址)都不能被访问或者操纵。
<128kbrk() 小内存 128K mmap()(大内存
7.作用域
C的作用域:
局部作用域 全局作用域
C++的作用域:
局部作用域 类作用域 名字空间作用域namespace->全局的名字空间作用域和局部的名字空间作用域
Intgdata=20; //全局作用域
Namespacemyname
{
Intgdata=10; //名字空间作用域
}
Usingmyname::gdata;
//using声明,把名字空间里的gdata暴露在全局作用域下,在使用using myname::gdata时可以不写名字空间作用域
Usingnamespace myname;
//using指示符,把名字空间myname里的内容全暴露在全局作用域下
Intmain()
{
Intgdata=30; //局部作用域
Cout<<gdata<<endl;
Return0;
}