4-16笔试

一、

char str[] = "abcd";
cout<<sizeof(str)<<endl;
//答案:5。因为有一个'\0'
 
wchar_t wstr[] = L"abcd";
cout<<sizeof(wstr)<<endl;
//答案:10。因为在wchar_t中,'\0'也是两个字节。
 
char *p = "abcd";
cout<<sizeof(p)<<endl;
//答案:4。因为p是指针
 
void *a = (char *)malloc(100);
cout<<sizeof(a)<<endl;
//答案:4。因为a是指针

二、

#define MAX 3+2
cout<<MAX*MAX<<endl;
//答案:11。3+2*3+2=11

三、在C++中调用C编译器编译过的函数时,为什么要使用extern C

答:

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字include声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。
例如,假设某个函数的原型为: void foo( int x, int y );,该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。 _foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float
同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以.来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
extern C是连接申明(linkage declaration),被extern C修饰的变量和函数是按照C语言方式编译和连接的。

  • (1)未加extern “C”声明时的连接方式。
    假设在C++中,模块A的头文件如下:
// 模块A头文件 moduleA.h
int foo( int x, int y );

在模块B中引用该函数:

// 模块B实现文件 moduleB.cpp
include "moduleA.h"
foo(2,3);

在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

  • (2)加extern “C”声明后的编译和连接方式。
    加extern “C”声明后,模块A的头文件变为:
// 模块A头文件 moduleA.h
extern "C" int foo( int x, int y );

在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:
(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo

如果在模块A中函数声明了foo为extern C类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。所以,可以用一句话概括extern C这个声明的真实目的:实现C++与C及其它语言的混合编程。

extern C通常的使用技巧和惯用法:

  • (1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern "C"
{
#include "cExample.h"
}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern C声明,在.c文件中包含了extern C时会出现编译语法错误。
如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern C {}

  • (2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern C,但是在C语言中不能直接引用声明了extern C的该头文件,应该仅将C文件中将C++中定义的extern C函数声明为extern类型。

四、堆和栈有什么区别?全局变量、静态变量和常量分别存放在哪里?char *p = “abc”;,”abc”存放在哪里?

地址、内存、堆、栈总结

“abc”存放在文字常量区

五、SendMessage()和PostMessage()有什么区别?GetMessage()和PeekMessage()有什么区别?

http://blog.csdn.net/xt_xiaotian?viewmode=contents

1、SendMessage、PostMessage的运行机制

SendMessage可以理解为,SendMessage函数发送消息,等待消息处理完成后,SendMessage才返回。稍微深入一点,是等待窗口处理函数返回后,SendMessage就返回了。 PostMessage可以理解为,PostMessage函数发送消息,不等待消息处理完成,立刻返回。稍微深入一点,PostMessage只管发送消息,消息有没有被送到则并不关心,只要发送了消息,便立刻返回。 对于写一般Windows程序的程序员来说,能够这样理解也就足够了。

2、SendMessage、PostMessage的运行内幕

在MSDN中,SendMessage解释如为:The SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message.

翻译成中文为:SendMessage函数将指定的消息发到窗口。它调用特定窗口的窗口处理函数,并且不会立即返回,直到窗口处理函数处理了这个消息。

再看看PostMessage的解释:The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.

翻译成中文为:PostMessage函数将一个消息放入与创建这个窗口的消息队列相关的线程中,并立刻返回不等待线程处理消息。

仔细看完MSDN解释,我们了解到,SendMessage的确是发送消息,然后等待处理完成返回,但发送消息的方法为直接调用消息处理函数(即WndProc函数),按照函数调用规则,肯定会等消息处理函数返回之后,SendMessage才返回。而PostMessage却没有发送消息,PostMessage是将消息放入消息队列中,然后立刻返回,至于消息何时被处理,PostMessage完全不知道,此时只有消息循环知道被PostMessage的消息何时被处理了。

GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax,UINT wRemoveMsg)

参数wRemoveMsg的作用是指定消息获取的方式,如果设为PM_NOREMOVE,那么消息将不会从消息队列中被移出,如果设为PM_REMOVE,那么消息将会从消息队列中被移出;

两个函数主要有以下两个区别:

1.GetMessage将等到有合适的消息时才返回,而PeekMessage只是撇一下消息队列。

2.GetMessage会将消息从队列中删除,而PeekMessage可以设置最后一个参数wRemoveMsg来决定是否将消息保留在队列中。

在Windows的内部,GetMessage和PeekMessage执行着相同的代码。而两者最大的不同之处则体现在没有任何消息返回到应用程序的情况下。在此种情况下,PeekMessage会返回一个空值到应用程序,GetMessage会在此时让应用程序休眠。

六、举例说明Windows中有哪些同步对象(或锁),在什么情况下使用这些同步对象?

http://www.cppblog.com/sherrylso/archive/2007/07/22/28585.html

在windows平台下,用于对多线程(包括进程)之间的同步保护机制,基本上有这么几种:

1)Critical Section对象(临界区) 2)Event对象(事件) 3)Mutex对象(互斥量) 4) Semaphore对象(信号量)。

本文的着眼点在于:总结出这些同步保护机制的一些明显的行为特征。

以下我们所讨论的这些行为特征,是对并发的进/线程之间的同步保护机制的一般描述,本文用windows平台作为一个典型的例子。 基于这一些行为特征,对本文提及的这四种同步对象做一个分类。 另外,在这里,我们把这四种同步对象,统统称为“锁”,以便于接下来的讨论。

第一、保护与同步。

在这里要强调的是:保护与同步是两个不同的概念。而我们经常会混合这两个概念。保护是指在多线程的环境下对共享资源的保护。这样的共享资源大多数情况下是一段内存块,它会被很多线程试图访问和修改。而同步更多的强调的是线程之间的协作,协同工作是需要同步支持的。 基于这一性质,我们可以看出:Critical Section对象其本质更多的强调的是保护,而Event对象、Mutex对象与Semaphore对象更多的强调的是同步。不过,这样的区别,只是概念上的区别,其本身不会对程序本身产生影响。

第二、锁的等待超时

在开发并发的多进/线程程序时,为了避免死锁之类的问题,引入了“等超时“的概念,即当一个线程需要获得一个锁来执行某些代码的时候,它可以在所等待的锁上设置超时值。如果在确定的时间(超时值)内无法获得该锁,它可以选择放弃执行该段代码的权利,这样可以在一定程度上避免出现死锁的问题。这就是锁的等待超时的基本含义。基于这一行为特征,我们来对上面四种同步对象做一个划分:Critical Section对象是无法设置等待超时的,而其他三个对象则可以设置等待超时。从这一点来讲,在使用Critical Section对象时,由于在等待进入关键代码段时无法设置等待超时,很容易造成死锁。

第三、线程锁与进程锁

这里所说的线程锁指的是该锁只在一个进程的所有线程中可见,而进程锁指的是该锁可以被不同的进程所访问,可用于进程间的同步与互斥。当然进程锁仍然可以被用于同一个进程的不同线程之间的同步与互斥。进程锁的概念是大于线程锁的。基于这一特点划分的话,Critical Section对象是线程锁,而其他三个对象是进程锁。这一点从本质上来分析,Critical Section对象是用户态模式下面实现线程同步的方法,而其他三个对象均是内核对象。内核对象机制的适应性远远优于用户方式机制。实际上,内核对象机制的唯一不足之处在于它的速度比较慢,这是因为当调用内核机制对象时,必须从用户方式转到内核方式。这样的转换需要付出很大的代价,是一件很费时的操作。在X86平台上,这样往返一次需要占用1000个CPU周期(这并不包括执行内核方式的代码)。当然需要注意的是:使用Critical Section对象并不意味着线程不会陷入核心态执行。当一个线程试图进入另一个线程拥有的关键代码段时,该线程就会进入等待状态。这意味着:该线程必须从用户态转为核心态。(为了提高这一方面的性能,Microsoft将循环锁的概念纳入到了Critical Section对象中,该线程可以有选择地不进入核心态等待.具体请参阅MSDN)

第四、锁的递归特质

所谓递归锁指的是当一个线程拥有一个同步锁时,而递归地想再次取得该锁.如果这次获得操作不会阻塞当前线程的执行,则称该锁为递归锁.递归锁主要是在”保护”的概念上提出的,而”保护”概念下的锁包括Critical Section对象和Mutext 对象.这两种锁在Windows平台上都是递归锁。需要注意的是:调用线程获得几次递归锁必须释放几次递归锁。

第五、读写锁

读写锁允许高效的并发的访问多线程环境下的共享资源。对于一种共享资源,多个线程可以获得读锁,共享地读该共享资源。而在同一时刻,只允许一个线程拥有写锁改变该共享资源.这就是读写锁的概念。很遗憾的是在Windows平台上没有这样的读写锁,你需要自己去实现。

  支持等超时 进程锁 递归锁 读写锁
Critical Section对象 N/A
Event对象 N/A N/A
Mutex对象 N/A
Semaphore对象 N/A N/A

七、Windows中进程和线程是什么关系?线程中能创建窗口吗?

http://www.blogjava.net/chalmers/archive/2011/03/31/347358.html

进程是系统分配资源的单位,每一个进程对应与一个活动的程序,当进程激活时,操作系统就将系统的资源包括内存、I/O和CPU等分配给它,使它执行。线程是CPU分配时间的单位,每一个线程对应于它在进程中的一个函数,也就是内存中的代码段,多个线程执行时CPU会根据它们的优先级分配时间,使它们完成自己的功能。 一般来说,进程中至少一个线程,一个主线程和其他线程组成一个进程。多个线程的目的在于分享CPU的时间片,从而完成并行任务。

1.调度

线程师调度和分派的基本单位,进程是资源拥有的基本单位。

2.并发性

进程之间可以并发执行,在一个进程中的多个线程之间也可以并发执行。

3.拥有资源

进程是拥有资源的一个独立单元,线程自己不拥有系统资源(也有一点比不可少的资源)但它可以访问其隶属进程的资源。

4.系统开销

创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/O设备等,OS所付出的开销显著大于在创建或撤消线程时的开销;进程切换的开销也远大于线程切换的开销。

实际上线程运行而进程不运行。两个进程彼此获得专用数据或内存的唯一途径就是通过协议来共享内存块。这是一种协作策略。上述解释便是原因所在。上述也就是我们在任务管理器的进程选项卡中所能看到的基本信息列。 线程中可以创建窗口

八、进程间通信的方式有哪些?

http://blog.csdn.net/shiqz/article/details/5862936

1 文件映射

文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。

Win32 API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。

应用程序有三种方法来使多个进程共享一个文件映射对象。 (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。
(2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。
(3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。

文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。

2 共享内存

Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。

3 匿名管道

管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的一管道的两端点既可读也可写。

匿名管道(Anonymous Pipe)是 在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。

匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。

4 命名管道

命名管道(Named Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。

命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。

5 邮件槽

邮件槽(Mailslots)提 供进程间单向通信能力,任何进程都能建立邮件槽成为邮件槽服务器。其它进程,称为邮件槽客户,可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消 息一直放在邮件槽中,直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户,因此可建立多个邮件槽实现进程间的双向通信。

通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节,非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。

邮件槽与命名管道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的,一旦网络发生错误则无法保证消息正确地接收,而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一种选择。

6 剪贴板

剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息,为Windows应用程序之间进行数据共享提供了一个中介,Windows已建立的剪切(复制)-粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时,应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式。

剪贴板是一个非常松散的交换媒介,可以支持任何数据格式,每一格式由一无符号整数标识,对标准(预定义)剪贴板格式,该值是Win32 API定义的常量;对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用,不能在网络上使用。

7 动态数据交换

动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输,也可以当出现新数据时,通过发送更新值在应用程序间动态交换数据。

DDE和剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自己定义的数据格式。但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答-如从菜单中选择Paste命令。尽管DDE也可以由用户启动,但它继续发挥作用一般不必用户进一步干预。DDE有三种数据交换方式: (1) 冷链:数据交换是一次性数据传输,与剪贴板相同。
(2) 温链:当数据交换时服务器通知客户,然后客户必须请求新的数据。
(3) 热链:当数据交换时服务器自动给客户发送数据。

DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC,它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。

8 对象连接与嵌入

应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档),OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服务。例如,OLE支持的字处理器可以嵌套电子表格,当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时,该表格已在原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展,而如果使用DDE,用户要显式地启动电子表格编辑器。

同DDE技术相同,大多数基于Windows的应用程序都支持OLE技术。

9 动态连接库

Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享,这就又给进程间通信开辟了一条新的途径,当然访问时要注意同步问题。

虽然可以通过DLL进行进程间数据共享,但从数据安全的角度考虑,我们并不提倡这种方法,使用带有访问权限控制的共享内存的方法更好一些。

10 远程过程调用

Win32 API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数,这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。

由于Win32 API提供的RPC服从OSF-DCE(Open Software Foundation Distributed Computing Environment)标准。所以通过Win32 API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。

11 NetBios函数

Win32 API提供NetBios函数用于处理低级网络控制,这主要是为IBM NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序,其它应用程序最好不要使用NetBios函数来进行进程间通信。

12 Sockets

Windows Sockets规范是以U.C.Berkeley大学BSD UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley Socket原有的库函数以外,还扩展了一组针对Windows的函数,使程序员可以充分利用Windows的消息机制进行编程。

现在通过Sockets实现进程通信的网络应用越来越多,这主要的原因是Sockets的跨平台性要比其它IPC机制好得多,另外WinSock 2.0不仅支持TCP/IP协议,而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作,这使得在单机的进程间进行简单数据传递不太方便,这时使用下面将介绍的WM_COPYDATA消息将更合适些。

13 WM_COPYDATA消息

WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时,发送方只需使用调用SendMessage函数,参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY DATA消息,这样收发双方就实现了数据共享。

WM_COPYDATA是一种非常简单的方法,它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高,并且它只能用于Windows平台的单机环境下。

在UNIX系统中,进程间通信的基本机制有:管道和命名管道、信号量、消息、共享内存区、套接字

九、虚函数与普通函数有什么区别?

1.声明的时,虚函数要使用关键字virtual,定义时不用 如:virtual int Func()

2.虚函数能实现多态

3.虚函数实现动态联编,开销大

十、如何显式地加载动态链接库

两种方法对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样的。显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。

在VC中两种方式的具体方法:

一、动态库的隐示调用:

在 VC 工程中直接链接静态输入库XXX.lib,然后即可像调用其它源文件中的函数一样调用DLL中的函数了。

二、动态库的显式调用:

显式调用动态库步骤:

1、创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数相吻 合。

2、通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回DLL 的实例句柄。

3、通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地址,把结果赋给自定义函数的指针类型。

4、使用函数指针来调用 DLL 函数。

5、最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数。