1:怎么防止头文件重复引用?为什么?
#ifndef _MY_FILE_
#def _MY_FILE_
....
....
#endif _MY_FILE_
2:指针和引用的区别?
① 非空区别,在任何情况下都不能使用指向空值的引用,一个引用必须总是指向某些对象。所以如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时应该将这个变量声明为指针,这样就可以给这个变量赋值;相反如果这个变量肯定指向一个对象,并且这个对象不允许为空,那么就应该声明为引用,引用的效率要高于指针效率;
② 合法性区别,我们在写代码的时候,对于指针通常要进行非空验证,if(p == NULL) return NULL ,但引用是不需要的;
③ 可修改性,指针是可以修改的,但是引用总是指向初始赋值的对象内容;
3:winsock建立连接的步骤?
服务器端:
1)、socket()建立套接字,
2)、绑定(bind)并监 听(listen),
3)、用accept()等待客户端连接,
4)、accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连接。
5)、该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,
6)、closesocket()关闭套接字。
客户端:
1)、socket()建立套接字,
2)、连接(connect)服务器,连接上后使用send()和recv(),在套接字上写读数据,直至数据交换完毕,
3)、closesocket()关闭套接字。
4:STL是什么?有什么作用?写出你平常使用的三种STL容器并简要说明?
STL是标准模板库,有一下几个优点:可以方便地实现搜索数据或对数据排序等一系列算法。 调试程序时更加方便和安全。跨平台使用。
常用的有:list—链表的形式存储数据;vector—可扩容的的数组;queue—FIFO队列;map—键值的映射关系,deque—双端队列,stack—LIFO,priority-queue—优先队列,
Multimap—关联式容器
类型 | 32位下sizeof大小 | 64位下sizeof大小 |
Char | 1 | 1 |
Unsigned char | 1 | 1 |
Signed char | 1 | 1 |
|
|
|
int | 4 | 4 |
Short | 2 | 2 |
long | 4 | 8 |
long int | 4 | 8 |
signed int | 4 | 4 |
unsigned int | 4 | 4 |
unsigned long int | 4 | 8 |
long long int | 8 | 8 |
unsigned long long | 8 | 8 |
|
|
|
float | 4 | 4 |
double | 8 | 8 |
long double | 8 | 16 |
5:getMemory(char* p)
#include <iostream>
void GetMemery(char* p, int num)
{
P = (char*)malloc(sizeof(char) * num);
}
int main()
{
char* str = NULL;
GetMemory(str, 100);
strcpy(str, “hello”);
return 0;
}
*p实际上是主函数中str的一个副本,p申请新的内存,只是把p所指向的内存地址变了,但是str丝毫未变。因为函数没有返回值。所以每次执行GetMemory就会申请一块内存,但是申请的内存没有有效的释放,结果造成内存泄露。
6:自己实现数据结构定义和函数。两个有序链表a,b。将他们俩合并为一个链表,合并之后链表还是有序的。
struct ListNode{
int val;
struct ListNode *next;
ListNode(int x): val(x), next(NULL){ }
};
ListNode *Merge(ListNode *pHead1, ListNode *pHead2){
if(pHead2 == NULL) return pHead1;
if(pHead1 == NULL) return pHead2;
ListNode* p1 ;
ListNode* p2;
if(pHead1->val < pHead2->val){
p1 = pHead1;
p2 = pHead2;
}
else{
p1 = pHead2;
p2 = pHead1;
ListNode* pHead = p1;
ListNode* pNode = p1;
p1 = p1->next;
while(p1 != NULL && p2 != NULL){
if(p1->val < p2->val){
pNode->next = p1;
p1 = p1->next;
pNode = pNode->next;
}
else{
pNode->next = p2;
p2 = p2->next;
pNode = pNode->next;
}
}
if(p1 == NULL) pNode->next = p2->next;
if(p2 == NULL) pNode->next = p1->next;
return pHead;
}
7:c++语言的类默认给我们提供了哪些函数?
① 构造函数
② 析构函数
③ 拷贝构造函数(以下几种情况将调用拷贝)
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
④ 赋值函数 每个类只有一个赋值函数
浅拷贝和深拷贝
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
8:C语言中static关键字用法小结
① 修饰全局变量 放在栈上,静态变量 作用域从开始到结束
② 修饰局部变量 放在堆上, 具有记忆功能
③ 修饰函数 只能本文件内调用,防止同名函数的误调用
9:const关键字的用法
① 定义const常量,具有不可变性
② 便于类型检查
③ 避免模糊数字出现,同宏定义一样
④ 保护被修饰的内存,防止被篡改
⑤ 可以节省空间,避免不必要的内存分配
⑥ 提高了效率,保存在符号表中,效率更高
10:window下的多线程同步机制有哪些
1:临界区
只能同步同一个进程的线程之间的同步,因为临界区不能跨越进程的边界工作。也是因为临界区没有name,所以不能跨进程使用。
访问临界区之前进行锁定,访问后进行解锁。
如果线程B访问线程A锁定的临界区,那么线程B会被阻塞,直到线程A释放临界区,线程B才可以运行。在线程B进行阻塞期间,不占用CPU时间.
2:互斥量
可以同步在相同进程,或不同进程间的线程进行同步。所以互斥量是有name的,可以跨进程使用。
互斥量与临界区的区别,如果一个线程锁定了临界区,而终止时没有解除临界区的锁定,那么等待临界区空闲的其他线程将无限期的阻塞下去。然而,如果锁定互斥量的线程不能在其终止前解除互斥量的锁定,那么系统将认为互斥量被“放弃”了并自动释放该互斥量,这样等待进程就可以继续执行了。
3: 事件
在任何特定时间,事件只能处在两种状态的一种:引发(设置)或者调低(重置)。设置可以认为是出于信号状态,重置事件可以认为是出于非信号状态。
事件也被描述为“线程触发器”。事件也不能跨进程。同样它没有name。
事件有自动设置事件和手动设置事件。其差别是当自动设置事件上阻塞的线程被唤醒时,该事件被自动设置为信号状态。手动设置事件不能自动重置,它必须使用编程的方式重置。
如果事件只触发一个线程,那么使用自动设置事件和使用SetEvent唤醒等待线程。 这里不需要调用ResetEvent,因为线程被唤醒的那一刻事件将被自动重置。
如果事件将触发两个或者多个线程,那么使用手动设置线程和使用PulseEvent唤醒所以的等待线程。而且,您不需要调用ResetEvent,因为PluseEvent在唤醒所有等待线程后为你重置事件。
4:信号量
首先,信号量可以同步不同进程,相同进程中的线程。
其次,信号量跟其他3种同步机制不同的是,上面3种的同步机制的特性“要没有,要么没有”,信号量则不同,它始终代表可用资源的数量。锁定信号量会减少资源数,释放信号量则增加资源数。只有在资源数为0的时候,线程才会被阻塞。
Linux 下的多线程同步方式
① 互斥锁
② 条件变量
③ 信号量
11、stl容器的size方法的时间复杂度是多少?
O(n)
12、new delete malloc free 等的区别于联系?
13、线程和进程有什么区别和联系?
1.定义
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
2.关系
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
3.区别
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2) 线程的划分尺度小于进程,使得多线程程序的并发性高。
3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
4.优缺点线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
14、给定N张扑克牌和一个随机函数,设计一个洗牌算法
<span style="font-size:18px;">void shuffle(int cards[],int n)
{
if(cards==NULL)
return ;
srand(time(0));
for(int i=0;i<n-1;++i)
{
//保证每次第i位的值不会涉及到第i位以前
int index=i+rand()%(n-i);
int temp=cards[i];
cards[i]=cards[index];
cards[index]=temp;
}
}</span>