v 题型:前端
数据结构(链表,数,图)、C++编程基础、数组、递归、矩阵、内存管理、时间复杂度、TCP/IP、操做系统、计算机网络、UML、OOA&OOP、本身的项目程序员
v swap的几种写法:算法
void swap(int* a, int* b)编程 |
||
{ int temp;数组 temp = *a;安全 *a = *b;网络 *b = temp; }数据结构 |
{ *a = *a + *b;app *b = *a - *b;异步 *a = *a - *b; } |
{ *a = *a ^ *b; *b = *b ^ *a; *a = *a ^ *b; } |
v 如何在C++中调用C程序:
C++和C是两种彻底不一样的编译连接处理方式,若是直接在C++里面调用C函数,会找不到函数体,报连接错误。要解决这个问题,就要在 C++文件里面显示声明一下哪些函数是C写的,要用C的方式来处理。
1.引用头文件前须要加上 extern “C”,若是引用多个,那么就以下所示
extern “C”
{
#include “ s.h”
#include “t.h”
#include “g.h”
#include “j.h”
};
而后在调用这些函数以前,须要将函数也所有声明一遍。
2.C++调用C函数的方法,将用到的函数所有从新声明一遍
extern “C”
{
extern void A_app(int);
extern void B_app(int);
extern void C_app(int);
extern void D_app(int);
}
C++程序中调用被c编译器编译后的函数,为何要加extern "C"?
C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不一样。假设某个C 函数的声明以下:
void foo(int x, int y);
该函数被C 编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字用来支持函数重载和类型安全链接。因为编译后的名字不一样,C++程序不能直接调用C 函数。C++提供了一个C 链接交换指定符号extern“C”来解决这个问题。例如:
extern “C”
{
void foo(int x, int y);
// 其它函数
}
或者写成
extern “C”
{
#include “myheader.h”
// 其它C 头文件
}
这就告诉C++编译译器,函数 foo 是个C 链接,应该到库中找名字_foo 而不是找_foo_int_int。C++编译器开发商已经对C 标准库的头文件做了extern“C”处理,因此咱们能够用#include直接引用这些头文件。
v 操做符优先级
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
单目 |
||
-- |
自减运算符 |
--变量名/变量名-- |
单目 |
||
* |
取值运算符 |
*指针变量 |
单目 |
||
& |
取地址运算符 |
&变量名 |
单目 |
||
! |
逻辑非运算符 |
!表达式 |
单目 |
||
~ |
按位取反运算符 |
~表达式 |
单目 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目 |
* |
乘 |
表达式*表达式 |
双目 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目 |
- |
减 |
表达式-表达式 |
双目 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目 |
>> |
右移 |
变量>>表达式 |
双目 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目 |
>= |
大于等于 |
表达式>=表达式 |
双目 |
||
< |
小于 |
表达式<表达式 |
双目 |
||
<= |
小于等于 |
表达式<=表达式 |
双目 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目 |
!= |
不等于 |
表达式!= 表达式 |
双目 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
v ::在C++中是什么意思
::是运算符中等级最高的,它分为三种:
1)global scope(全局做用域符),用法(::name)
2)class scope(类做用域符),用法(class::name)
3)namespace scope(命名空间做用域符),用法(namespace::name)
他们都是左关联(left-associativity)
他们的做用都是为了更明确的调用你想要的变量,如在程序中的某一处你想调用全局变量a,那么就写成::a,若是想调用class A中的成员变量a,那么就写成A::a,另一个若是想调用namespace std中的cout成员,你就写成std::cout(至关于using namespace std;cout)意思是在这里我想用cout对象是命名空间std中的cout(即就是标准库里边的cout)
v i++
!x++; !与++优先级相同,先计算!x;而后计算x++; (原题:P35)
v 指针+1的问题
指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1。至于真实的地址加了多少,要看原来指针指向的数据类型是什么。
p指向的是一个字符,p+1就是移动一个字符大小,一个字符就是一个字节,因此p +1 表明的地址就比 p 表明的地址大1。
p指向的是一个整型,p+1就是移动一个整型大小,即移动4个字节,因此p+1表明的地址比p表明的地址大4.
v x++与++x
#include <stdio.h> void main() { int arr[] = { 6, 7, 8, 9, 10 }; int *ptr = arr; *(ptr++) += 123;//equal:*ptr=*ptr+123;ptr++; printf("%d,%d\n",*ptr,*(++ptr));//prinft计算参数时从右向左入栈,先执行++ptr; } RESULT:8,8 X++先执行运算最后++;++X则先++而后执行其余运算; |
v 隐式类型转换发生在下列这些典型的状况下:
v 判断一个数X是不是2N次方,不可用循环语句:
!(X&(X-1))==0
v Const
在C程序中,const的用法主要是定义常量、修饰函数参数、修饰函数返回值,在C++中,它还能够修饰函数的定义体,定义类中某个成员函数为恒态函数,即不改变类中的数据成员
被Const修饰的东西能够受到强制保护,预防意外的变更,能提升程序的健壮性
Const与#define相比有什么不一样?
Const常量有数据类型,而宏常量没有数据类型,编译器能够对前者进行类型安全检查,而对后者只能进行字符替换,没有类型安全检查,而且在字符替换中可能产生意料不到的错误
调试工具能对const常量进行调试,可是没法对define常量调试
No. |
做用 |
说明 |
参考代码 |
1 |
能够定义const常量 |
|
const int Max = 100; |
2 |
便于进行类型检查 |
const常量有数据类型,而宏常量没有数据类型。编译器能够对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,而且在字符替换时可能会产生意料不到的错误 |
void f(const int i) { .........} |
3 |
能够保护被修饰的东西 |
防止意外的修改,加强程序的健壮性。 |
void f(const int i) { i=10;//error! } |
4 |
能够很方便地进行参数的调整和修改 |
同宏定义同样,能够作到不变则已,一变都变 |
|
5 |
为函数重载提供了一个参考 |
|
class A |
6 |
能够节省空间,避免没必要要的内存分配 |
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define同样给出的是当即数,因此,const定义的常量在程序运行过程当中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝 |
#define PI 3.14159 //常量宏 |
7 |
提升了效率 |
编译器一般不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操做,使得它的效率也很高 |
|
const int *A; 或 int const *A; //const修饰指向的对象,A可变,A指向的对象不可变
int *const A; //const修饰指针A, A不可变,A指向的对象可变
const int *const A; //指针A和A指向的对象都不可变
v sizeof与Strlen()
sizeof 单位:字节
sizeof(...)是运算符,而不是一个函数。一个简单的例子:
int a;
cout<<sizeof a<<endl;
在头文件中typedef为unsigned int,其值在编译时即计算好了,参数能够是数组、指针、类型、对象、函数等。
它的功能是:得到保证能容纳实现所创建的最大对象的字节大小。
因为在编译时计算,所以sizeof不能用来返回动态分配的内存空间的大小。
实际上,用sizeof来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。
具体而言,当参数分别以下时,sizeof返回的值表示的含义以下:数组——编译时分配的数组空间大小;
指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为4);类型——该类型所占的空间大小;
对象——对象的实际占用空间大小;
函数——函数的返回类型所占的空间大小。函数的返回类型不能是void。
********************************************************************
strlen
strlen(...)是函数,要在运行时才能计算。
参数必须是字符型指针(char*), 且必须是以'\0'结尾的。当数组名做为参数传入时,实际上数组就退化成指针了。
int ac[10];
cout<<sizeof(ac)<<endl;
cout<<strlen(ac)<<endl; (ac至关于一个指针,可是strlen只能接受char*类型,因此编译时出错)
它的功能是:返回字符串的长度。该字符串多是本身定义的,也多是内存中随机的,该函数实际完成的功能是从表明该字符串的第一个地址开始遍历,直到遇到结束符'\0'。返回的长度大小不包括'\0'。
v 结构体长度对齐
在默认状况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的数据元素为对其单位,也就是说,结构体的长度必定是最长的数据元素的整数倍;若是结构体内存在长度大于处理器位数的元素,那么就以处理器的位数为对齐单位。可是结构体内类型相同的连续元素将在连续的空间内,和数组同样。
CPU的优化规则大体是这样:对于n字节的元素(n=2,4,8,…),他的首地址能被n整除,才能得到最好的性能。(我的感受是若是不是这样,那么一个元素颇有可能会跨页存储,存在两个不一样的页面上,致使访问速度慢)
v 对其是一种以空间换时间的方法,在访问内存时,若是地址按4字节对齐,则访问效率会高不少,这种现象的缘由在于访问内存的硬件电路。通常状况下,地址总线老是按照对齐后的地址来访问的。例如你想获得0x00000001开始的4字节内容,系统需先以0x00000000读4个字节,从中取3字节,而后再用0X00000004最为开始地址,得到下一个四字节,再从中获得第一个字节,两次组合出你想要的内容。可是若是地址一开始就是对齐到0x00000000,则系统只需一次内存读写便可。
v 对其的自定义设置
#pragma pack(1)
struct aStruct…{…};
#pragma pack()
v 字节对齐是在编译时决定的,不会再发生变化,在运行时不会再发生变化
v 静态变量存放在全局数据区,而sizeof计算栈中分配的大小,是不会计算static变量的。
v 数据类型的长度:
short -------------- 2
int,long,float,指针---4
double -------------- 8
v sizeof vs strlen
sizeof是算符,strlen是函数
sizeof能够用类型作参数,strlen只能用char*作参数,且必须是以“\0”结尾的。
数组作sizeof的参数不退化,传递给strlen就退化为了指针
sizeof在编译时计算,strlen在运算时计算
对函数使用sizeof,在编译阶段会被函数返回值类型取代。
v sizeof不是函数,也不是一元运算符,他是个相似宏定义的特殊关键字,sizeof()括号内的内容是不被编译的,只是替换,因此a=8;sizeof(a=6);以后a的值仍然为8。
v unsigned影响的仅仅是最高位bit的意义(正/负),而不会影响数据长度。
v 空类所占空间为1,单一继承的空类空间也为1,多重继承的空类空间仍是1,可是虚继承涉及到虚表(虚指针),因此空间大小为4
v 内联函数和宏定义
内联函数和普通函数相比能够加快程序运行的速度,由于不须要中断调用,在编译的时候内联函数能够直接被镶嵌到目标代码中,而宏只是一个简单的替换;内联函数要作参数类型检查,这是inline和宏相比的优点;
inline通常只用于以下状况:
—————————————————————————————————————————————————————————————————————————————————————————
v 指针问题:包括常量指针、数组指针、函数指针、this指针、指针传值、指向指针的指针等问题
v 指针和引用的区别
非空区别:在任何状况下都不能使用指向空值的引用;
合法性区别:在使用引用以前不须要测试它的合法性;
可修改区别:指针能够被从新赋值以指向另外一个不一样的对象,可是引用则老是指向初始化时被指定的对象,之后不能改变,可是指定的内容能够改变;
应用区别:使用指针:存在不指向任何对象的可能;须要 在不一样的时刻指向不一样的对象;使用引用:老是指向一个对象而且一旦指向一个对象后就不会改变指向。
v 引用的初始化:
声明一个引用时,必须同时初始化,如同const常量必须声明的同时初始化。
v char c[] vs char *c
char c[]分配的是局部数组,而char *c分配的是全局数组
v 函数指针
函数指针 |
void (*f)() |
函数返回指针 |
void* f() |
const指针 |
cons int * |
指向const的指针 |
int * const |
指向const的const指针 |
cons int* const |
v malloc和free是C/C++的标准库函数,new/delete是C/C++的运算符。他们均可以用于动态申请内存和释放内存。对于非内部数据类型的对象而言,光用malloc/free没法知足动态对象的要求。对象在建立的同时要自动执行构造函数,对象在消亡以前要自动执行析构函数。因为malloc/free是库函数而不是运算符,不在编译器控制权限范围内,不能把执行构造函数和析构函数的任务强加于malloc/free
v 数组指针和指针数组:
数组指针 |
指针数组 |
int (*a)[] |
int *a[] |
—————————————————————————————————————————————————————————————————————————————————————————
v STL模版与容器
_________________
v STL的优势:
v STL中的一些基本概念
v 容器向量:标准模板库是一个基于模版的容器类库,包括链表、列表、队列和堆栈;标准模板库还包括许多经常使用的算法,包括排序和查找;可重用;容器是包括其余对象的对象;标准模版库类有两种类型:顺序和关联;不一样OS间可移植;
v vector的操做:
标准库vector类型使用须要的头文件:#include <vector>。vector 是一个类模板。不是一种数据类型,vector<int>是一种数据类型。Vector的存储空间是连续的,list不是连续存储的。
初学C++的程序员可能会认为vector的下标操做能够添加元素,其实否则:
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
上述程序试图在ivec中插入10个新元素,元素值依次为0到9的整数。可是,这里ivec是空的vector对象,并且下标只能用于获取已存在的元素。
这个循环的正确写法应该是:
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new element with value ix
警告:必须是已存在的元素才能用下标操做符进行索引。经过下标操做进行赋值时,不会添加任何元素,仅能对确知已存在的元素进行下标操做 。
v 泛型编程
何谓泛型编程:STL表明用一致的方式编程是可能的;泛型编程是一种基于发现高效算法的最抽象表示的编程方法;STL是一个泛型编程的例子,C++是咱们能够实现使人信服的例子的语言。
v 模版
一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针,正如指针是一个变量的地址同样。(经测试:函数名和对函数名取地址获得的是同一个值,均可以做为该函数的指针)。有些地方必须使用函数指针才能完成给定的任务,特别是异步操做的回调和其余须要匿名回调的结构。另外,想线程的执行和时间的处理,若是缺乏了函数的支持也是很难完成的。
—————————————————————————————————————————————————————————————————————————————————————————————
v 面向对象(Object-Oriented)
编程是在计算机中反应世界
面向对象的优势:良好的可复用性、易维护、良好的可扩充性
面向对象技术的基本概念是:对象、类和继承
C++中的空类默认产生的成员函数:
默认构造函数、析构函数、拷贝构造函数、赋值函数
C++中struct也能够有constructor/destructor及成员函数,它和class的区别是:class的默认访问控制是private,struct中默认访问控制是public。
在一个类中,初始化列表的初始化变量顺序是根据成员变量的声明顺序来执行的。
MFC库中,为何CObject的析构函数是虚函数?
保证在任何状况下,不会出现因为析构函数未被调用而致使内存泄漏
析构函数能够是内联函数,但内联函数不能为virtual
多态:容许将子类类型的指针赋值给父类类型的指针,多态在Object Pascal和C++中都是经过虚函数(Virtual Function)实现的。
实现代码重用 |
封装:隐藏实现细节,使得代码模块化
继承:扩展已存在的代码模块(类)
多态:同一操做做用于不一样对象上,产生不一样的解释和执行结果—实现接口重用
友元函数:定义在类外部的普通函数,但他须要在类体内说明,为了与该类的成员函数区别,说明时须要在前面加上friend来区别;他虽然不是成员函数,但却能够访问类的私有变量。
友元还能够是类,称为友元类
继承:可使一个新类得到其父类的操做和数据结构,程序员只需在新类中增长原有类中没有的成分。
为了避免破坏类的封装性,全部操做应该经过接口进行沟通。
类中的static变量存在数据段,不在类的实例空间中。