Microsoftios
下面哪些调用转换支持可变长度参数:c++
A. cdecl B. stdcall C. pascal D. fastcall数组
几种函数调用方式:多线程
__cdecl 是C Declaration的缩写,表示C语言默认的函数调用方法:全部参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至彻底不一样的参数都不会产生编译阶段的错误。并发
_stdcall 是StandardCall的缩写,是C++的标准调用方式:全部参数从右到左依次入栈,若是是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,称为自动清栈。函数在编译的时候就必须肯定参数个数,而且调用者必须严格的控制参数的生成,不能多,不能少,不然返回后会出错。函数
PASCAL 是Pascal语言的函数调用方式,也能够在C/C++中使用,参数压栈顺序与前二者相反。返回时的清栈方式与_stdcall相同。this
_fastcall是编译器指定的快速调用方式。因为大多数的函数参数个数不多,使用堆栈传递比较费时。所以_fastcall一般规定将前两个(或若干个)参数由寄存器传递,其他参数仍是经过堆栈传递。不一样编译器编译的程序规定的寄存器不一样。返回方式和_stdcall至关。spa
_thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax。返回方式和_stdcall至关。操作系统
_fastcall 和 _thiscall涉及的寄存器由编译器决定,所以不能用做跨编译器的接口。因此Windows上的COM对象接口都定义为_stdcall调用方式。线程
C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也同样,可是默认的调用方式能够在IDE环境中设置。
带有可变参数的函数必须且只能使用_cdecl方式,例以下面的函数:
int printf(char * fmtStr, ...);
int scanf(char * fmtStr, ...);
如下代码的输出结果:
class A
{
public:
virtual void f()
{
cout<<"A:f()"<<endl;
}
void f() const
{
cout<<"A:f()const"<<endl;
}
};
class B:public A
{
public:
void f()
{
cout<<"B:f()"<<endl;
}
void f() const
{
cout<<"B:f()const"<<endl;
}
};
void ga(const A *a)
{
a->f();
}
int _tmain(intargc, _TCHAR* argv[])
{
A *a=new B();
a->f();
ga(a);
}
答案:B::f()A::f()const
第一个,b->f()为动态绑定,输出B::f没问题
第二个,因为函数ga的参数有const,因此调用成员函数也是调用const版本,可是const版本的不是虚函数,不存在动态绑定,因此输出A::f const。
const 修饰函数和没有const是不一样的重载,对于非const对象调用非const函数,固然也能够调用const函数(优先前者);然而对于const对象则只能调用const函数;其次,A中的f ()函数标注为 virtual,那么子类 B 中跟它原模原样声明即便没有标注为 virtual的函数依然是 virtual 函数。(c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。)
const是函数类型的一部分。虚函数重载的时候这个const也必须一致。若是基类的虚函数带有const,而在子类的实现中没有带const,则至关于在子类中从新定义了一个新的函数。
定义虚函数的限制:
(1)非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但能够将析构函数定义为虚函数。构造函数不能是虚函数,内联函数也不能是虚函数。构造函数不能是虚函数缘由在于:首先构造函数构造一个对象时必须知道对象的实际类型,其次,虚函数的执行依赖于虚函数表,而虚函数表在构造函数中进行初始化工做。
(2)只须要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不须要使用关键字“virtual”。
(3)当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。
(4)若是声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名而且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现这种同名函数。
子类中能够不重写父类中的虚函数:
class A
{
public:
virtual void f()
{
cout<<"A:f()"<<endl;
}
};
class B:public A
{
public:
void f2()
{
cout<<"B:f()"<<endl;
}
};
int main(){
A *a=new B();
a->f();
}
输出结果:A:f()
但若是基类中定义的是纯虚函数,而在子类中没有实习,那么在这个子类中,该函数仍然是纯虚函数,且该子类也不能被实例化。
线程与进程
程序与进程的关系:程序是计算机指令的集合,它以文件的形式存储在磁盘上,而进程一般被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。一个程序能够对应多个进程,同时,在一个进程中也能够同时访问多个程序。
进程是资源申请、调度和独立运行的单位,所以它使用系统中的运行资源。程序不能申请系统资源,不能被系统调度,也不能做为独立运行的单位,所以它不占用系统的运行资源。
进程与线程的关系:
1)进程历来不执行任何东西,它只是线程的容器。若要使进程完成某项操做,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。也就是说,进程其实是线程的执行环境。
2)单个进程可能包含若干个线程,这些线程都“同时”(时间片)执行进程地址空间中的代码。每一个进程至少拥有一个线程。当建立一个进程时,操做系统会自动建立这个进程的第一个线程,称为主线程,也就是执行主函数的线程。此后主线程能够建立其余线程。
3)进程和线程的主要差异在于它们是不一样的操做系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,子进程和父进程有不一样的代码和数据空间。而线程只是一个进程中的不一样执行路径。线程有本身的堆栈和局部变量,但线程之间没有单独的地址空间,多个线程则共享数据空间,一个线程死掉就等于整个进程死掉,因此多进程的程序要比多线程的程序健壮。
4)在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行而且又要共享某些变量的并发操做,只能用线程,不能用进程。
如下代码的输出结果
int x=10;
x= x++;
printf("%d /n", x);
不一样编译器编译出的结果不一样。可能为10(DevC++),也可能为11(VC6.0).
如下const用法正确的是:
A. const int a; // a 是常数
B. int const a; // a 是常数
C. int const *a; // a 指向常数的指针
D. const int *a; // a 是常指针
E. int const *a; // a 是常指针
答案:A B C
注:D 和 E都是指向常量的指针,而 int * const a; 才是常量指针。约束指针仍是约束常量主要看*的位置。
代码验证:
const int a=10;
int const b=11;
cout<<a<<" "<<b<<endl;
输出:10 11
为何下面的例子在使用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?
const int n = 5;
int a[n];
答案与分析:
1)这个问题讨论的是“常量”与“只读变量”的区别。常量,例如5, "abc",等,确定是只读的,由于常量是被编译器放在内存中的只读区域,固然也就不可以去修改它。而“只读变量”则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不容许被修改。C语言关键字const就是用来限定一个变量不容许被改变的修饰符(Qualifier)。上述代码中变量n被修饰为只读变量,惋惜再怎么修饰也不是常量。而ANSI C规定数组定义时长度必须是“常量”,“只读变量”也是不能够的,“常量”不等于“不可变的变量”。
2)可是在标准C++中,这样定义的是一个常量,这种写法是对的。实际上,根据编译过程及内存分配来看,这种用法原本就应该是合理的,只是ANSI C对数组的规定限制了它(实际上用GCC或VS2005编译以上代码,确实没有错误产生,也没有给出警告)。
3)那么,在ANSI C中用什么来定义常量呢?答案是enum类型和#define宏,这两个均可以用来定义常量。
例:下面的代码编译器会报一个错误,请问,哪个语句是错误的呢?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string; //1式
const pStr p2 = string; //2式
p1++;
p2++;
答案与分析:
问题出在p2++上。
1)const使用的基本形式: const type m;
限定m不可变。
2)替换基本形式中的m为1式中的*p1,替换后const char *p1;
限定*p1不可变,固然p1是可变的,所以问题中p1++是对的。
3)替换基本形式中的type为2式中的pStr,替换后const pStr m;
限定m不可变,题中的pStr就是一种新类型,所以问题中p2不可变,p2++是错误的。
下面分别用const限定不可变的内容是什么?
1)const在前面
const int nValue; //nValue是const
const char *pContent; //*pContent是const, pContent可变
const char* const pContent; //pContent和*pContent都是const
2)const在后面,与上面的声明对等
int const nValue; //nValue是const
char const * pContent; //*pContent是const, pContent可变
char* const pContent; //pContent是const,*pContent可变
char const* const pContent; //pContent和*pContent都是const
答案与分析:
const和指针一块儿使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,经常会由于这样而很差判断做者的意图,下面讲一下个人判断原则:
const只修饰其后的变量,至于const放在类型前仍是类型后并无区别。如:const int a和int const a都是修饰a为const。*不是一种类型,若是*pType以前是某类型,那么pType是指向该类型的指针
一个简单的判断方法:指针运算符*,是从右到左,那么如:char const * pContent,能够理解为char const (* pContent),即* pContent为const,而pContent则是可变的。
int const * p1,p2;
p2是const;(*p1)是一总体,所以(*p1)是const,但p1是可变的。int * p1,p2只表明p1是指向整型的指针,要表示p1、p2都是指针是需写成int * p1,* p2。因此不管是* const p1,p2仍是const * p1,p2,里面的*都是属于p1的。
int const * const p1,p2;
p2是const,是前一个const修饰的,*p1也被前一个const修饰,而p1被后一个const修饰。
int * const p1,p2;
p1是const,(* const p1)是总体,因此const不修饰p2。
const在*的左边,则指针指向的变量的值不可变;在*的右边,则指针的指向不可变。简记为“左定值,右定向”。
1)指针指向的变量的值不能变,指向可变
int x = 1;
int y = 2;
const int* px = &x;
int const* px = &x; //这两句表达式同样效果
px = &y; //正确,容许改变指向
*px = 3; //错误,不容许改变指针指向的变量的值
2)指针指向的变量的值能够改变,指向不可变
int x = 1;
int y = 2;
int* const px = &x;
px = &y; //错误,不容许改变指针指向
*px = 3; //正确,容许改变指针指向的变量的值
3)指针指向的变量的值不可变,指向不可变
int x = 1;
int y = 2;
const int* const px = &x;
int const* const px = &x;
px = &y; //错误,不容许改变指针指向
*px = 3; //错误,不容许改变指针指向的变量的值
附:
在c中,对于const定义的指针,不赋初值编译不报错,
即int* const px;等不会报错。
可是,在C++中
int* const px;和const int* const px;会报错,const int* px;不报错。
必须初始化指针的指向int* const px = &x;const int* const px=&x;
强烈建议在初始化时说明指针的指向,防止出现野指针
有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡至少要多少只小白鼠才能在24小时鉴别出哪瓶水有毒。
给1000个瓶分别标上以下标签(10位长度):
0000000001 (第1瓶)
0000000010 (第2瓶)
0000000011 (第3瓶)
......
1111101000 (第1000瓶)
从编号最后1位是1的全部的瓶子里面取出1滴混在一块儿(好比从第一瓶,第三瓶,…里分别取出一滴混在一块儿)并标上记号为1。以此类推,从编号第一位是1的全部的瓶子里面取出1滴混在一块儿并标上记号为10。如今获得有10个编号的混合液,小白鼠排排站,分别标上10,9,…1号,并分别给它们灌上对应号码的混合液。24小时过去了,过来验尸:
从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后获得一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。
3*4的格子有几个矩形:
M*N网格中有横竖各M+1、N+1条直线,其中,任意各取两条均可以组成一个长方形。
C(4,2)*C(5,2)=6*10=60;
A(N,N)=N!
A(N,M)=N*(N-1)*…*(N-M+1)
C(N,M)=A(N,M)/A(M,M)
一条线把平面分红两块,两条线把平面分红四块,若是任意两条线不平行,且没有3条线交在同一点,问100条线将平面分红多少块。
答案:5051
1条直线最多将平面分红2个部分;2条直线最多将平面分红4个部分;3条直线最多将平面分红7个部分;如今添上第4条直线.它与前面的3条直线最多有3个交点,这3个交点将第4条直线分红4段,其中每一段将原来所在平面部分一分为二,因此4条直线最多将平面分红7+4=11个部分.
彻底相似地,5条直线最多将平面分红11+5=16个部分;6条直线最多将平面分红16+6=22个部分;7条直线最多将平面分红22+7=29个部分;8条直线最多将平面分红29+8=37个部分.
通常地,n条直线最多将平面分红2+2+3....+N=(N*N+N+2)/2
有N个球,其中只有一个是重量较轻的,用天平只称三次就能找到较轻的球,如下的N值哪一个是可能的?
A 12
B 16
C 20
D 24
E 28
3 个一次能够测出来,3*3 = 9个之内 2 次,3*3*3 = 27个之内,3次!
下列代码的输出是什么?
#include <iostream>
using namespace std;
class A{
public:
long a;
};
class B : public A
{
public:
long b;
};
void seta(A* data, int idx)
{
data[idx].a = 2; // 形参为A的指针。数组步长按A的大小来取。
}
int main(int argc, char *argv[])
{
B data[4];
for(int i=0; i<4; ++i){
data[i].a = 1;
data[i].b = 1;
seta(data, i);
}
cout<<sizeof(A)<<endl; // 4
cout<<sizeof(B)<<endl; // 8
for(int i=0; i<4; ++i){
std::cout << data[i].a << data[i].b;
}
}
输出为22221111
附:
struct Test{
short int a;
short int b;
};
int main(int argc, char *argv[])
{
int test = 0x12345678;
printf("%x %x", (*(struct Test*)&test).a, (*(struct Test*)&test).b); // 5678 1234
}
//Intel是小端处理器