笔试的一些题目

1:怎么防止头文件重复引用?为什么?

#ifndef _MY_FILE_

#def _MY_FILE_

....

....

#endif   _MY_FILE_

 

2:指针和引用的区别?

① 非空区别,在任何情况下都不能使用指向空值的引用,一个引用必须总是指向某些对象。所以如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时应该将这个变量声明为指针,这样就可以给这个变量赋值;相反如果这个变量肯定指向一个对象,并且这个对象不允许为空,那么就应该声明为引用,引用的效率要高于指针效率;

② 合法性区别,我们在写代码的时候,对于指针通常要进行非空验证,if(p == NULL) return NULL  ,但引用是不需要的;

③ 可修改性,指针是可以修改的,但是引用总是指向初始赋值的对象内容;

3winsock建立连接的步骤?

服务器端: 

1)、socket()建立套接字, 
2)、绑定(bind)并监 听(listen), 
3)、用accept()等待客户端连接
4)、accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连接。 
5)、该新产生的套接字使用send()recv()写读数据,直至数据交换完毕, 
6)、closesocket()关闭套接字。 

客户端: 
1)、socket()建立套接字, 
2)、连接(connect)服务器,连接上后使用send()recv(),在套接字上写读数据,直至数据交换完毕, 
3)、closesocket()关闭套接字。

4STL是什么?有什么作用?写出你平常使用的三种STL容器并简要说明?

STL是标准模板库,有一下几个优点:可以方便地实现搜索数据或对数据排序等一系列算法。 调试程序时更加方便和安全。跨平台使用。

常用的有:list—链表的形式存储数据;vector—可扩容的的数组;queueFIFO队列;map—键值的映射关系,deque—双端队列,stackLIFOpriority-queue—优先队列,

Multimap—关联式容器


532位机和64位机下面各类型sizeof的大小

类型

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内的指针就是野指针了,出现运行错误。

深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

8C语言中static关键字用法小结

① 修饰全局变量 放在栈上,静态变量 作用域从开始到结束

② 修饰局部变量 放在堆上, 具有记忆功能

③ 修饰函数  只能本文件内调用,防止同名函数的误调用

9const关键字的用法

① 定义const常量,具有不可变性

② 便于类型检查

③ 避免模糊数字出现,同宏定义一样

④ 保护被修饰的内存,防止被篡改

⑤ 可以节省空间,避免不必要的内存分配

⑥ 提高了效率,保存在符号表中,效率更高

10window下的多线程同步机制有哪些

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  等的区别于联系?

1、malloc和free是C语言标准函数库中的两个函数,new/delete是C++语言中两个运算符。

2、malloc/free和new/delete都是用来申请动态内存的。

3、new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free 也不会调用析构函数。

4、malloc得到的指针无类型,new出来的指针是带有类型信息的。

5、对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时 要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free

6、如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如 果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以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>

一、C++中不能使用random()函数
     random函数不是ANSI C标准,不能在gcc,vc等编译器下编译通过。但在C语言中int random(num)可以这样使用,它返回的是0至num-1的一个随机数。 可改用C++下的rand函数来实现。
     1、C++标准函数库提供一随机数生成器rand,返回0-RAND_MAX之间均匀分布的伪随机整数。 RAND_MAX必须至少为32767。rand()函数不接受参数,默认以1为种子(即起始值)。 随机数生成器总是以相同的种子开始,所以形成的伪随机数列也相同,失去了随机意义。(但这样便于程序调试) 
      2、C++中另一函数srand(),可以指定不同的数(无符号整数变元)为种子。但是如果种子相同,伪随机数列也相同。一个办法是让用户输入种子,但是仍然不理想。 
     3、 比较理想的是用变化的数,比如时间来作为随机数生成器的种子。 time的值每时每刻都不同。所以种子不同,所以,产生的随机数也不同。 
// C++随机函数(VC program) 
#include <stdio.h> 
#include <iostream> 
#include <time.h> 
using namespace std; 
#define MAX 100 
int main(int argc, char* argv[]) 
       srand( (unsigned)time( NULL ) );//srand()函数产生一个以当前时间开始的随机种子.应该放在for等循环语句前面 不然要很长时间等待
for (int i=0;i<10;i++) 
cout<<rand()%MAX<<endl;//MAX为最大值,其随机域为0~MAX-1
   return 0; 
二、rand()的用法 
     rand()不需要参数,它会返回一个从0到最大随机数的任意整数,最大随机数的大小通常是固定的一个大整数。 
/*   maximum   value   returned   by   "rand"   function   
  */   
  #define   RAND_MAX   0x7fffu   
    
这个是bcc55中的定义,说明这个整数的最大数是0x7fffu,u代表unicode编码。
 
这样,如果你要产生0~10的10个整数,可以表达为: 
int N = rand() % 11; 
     这样,N的值就是一个0~10的随机数,如果要产生1~10,则是这样: 
int N = 1 + rand() % 10; 
总结来说,可以表示为: 
a + rand() % n
     其中的a是起始值,n是整数的范围。 
  a + rand() % (b-a+1) 就表示 a~b之间的一个随机数
若要0~1的小数,则可以先取得0~10的整数,然后均除以10即可得到随机到十分位的10个随机小数,若要得到随机到百分位的随机小数,则需要先得到0~100的10个整数,然后均除以100,其它情况依
此类推。 
     通常rand()产生的随机数在每次运行的时候都是与上一次相同的,这是有意这样设计的,是为了便于程序的调试。若要产生每次不同的随机数,可以使用srand( seed )函数进行随机化,随着seed的不同,就能够产生不同的随机数。 
     如大家所说,还可以包含time.h头文件,然后使用srand(time(0))来使用当前时间使随机数发生器随机化,这样就可以保证每两次运行时可以得到不同的随机数序列(只要两次运行的间隔超过1秒)。 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值