【C++】作业【六】笔试 进程线程| &&与|| |OSI七层|指针|union联合体|宏函数|创建进程函数

2019/7/23 15:00:51 人评论 次浏览 分类:学习教程

1.进程和线程的区别

  • 进程是资源分配的最小单位,线程是程序执行的最小单位,CPU调度的最小单位。
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行(这里不做介绍了)。
  • 多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

多进程多线程的区别和选择(总结)

 

2.&&和||运算次序

两个都是左结合(就是从左到右?)

&&运算优先级大于||,也就是说 a||b&&c,是先运算b&&c ,再 a||(b&&c)。

举例:a&&b||c,如果a是0,不表示它不会继续往下做,只是表示(a&&b)它不会在看b是0还是1了,它会将(a&b)打包为0,再继续后面的或运算。

 

3.OSI七层模型

应用层

与其它计算机进行通讯的一个应用,它是对应应用程序的通信服务的。例如,一个没有通信功能的字处理程序就不能执行通信的代码,从事字处理工作的程序员也不关心OSI的第7层。但是,如果添加了一个传输文件的选项,那么字处理器的程序员就需要实现OSI的第7层。示例:telnet,HTTP,FTP,NFS,SMTP等。

表示层

这一层的主要功能是定义数据格式及加密。例如,FTP允许你选择以二进制或ASCII格式传输。如果选择二进制,那么发送方和接收方不改变文件的内容。如果选择ASCII格式,发送方将把文本从发送方的字符集转换成标准的ASCII后发送数据。在接收方将标准的ASCII转换成接收方计算机的字符集。示例:加密,ASCII等。

会话层

它定义了如何开始、控制和结束一个会话,包括对多个双向消息的控制和管理,以便在只完成连续消息的一部分时可以通知应用,从而使表示层看到的数据是连续的,在某些情况下,如果表示层收到了所有的数据,则用数据代表表示层。示例:RPC,SQL等。

传输层

这层的功能包括是否选择差错恢复协议还是无差错恢复协议,及在同一主机上对不同应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包的重新排序功能。示例:TCP,UDP,SPX

网络层

这层对端到端的包传输进行定义,它定义了能够标识所有结点的逻辑地址,还定义了路由实现的方式和学习的方式。为了适应最大传输单元长度小于包长度的传输介质,网络层还定义了如何将一个包分解成更小的包的分段方法。示例:IP,IPX等。

数据链路层

它定义了在单个链路上如何传输数据。这些协议与被讨论的各种介质有关。示例:ATM,FDDI等。

物理层

OSI的物理层规范是有关传输介质的特性标准,这些规范通常也参考了其他组织制定的标准。连接头、帧、帧的使用、电流、编码及光调制等都属于各种物理层规范中的内容。物理层常用多个规范完成对所有细节的定义。示例:Rj45,802.3等。

 

4.windows消息调度机制是处理消息队列的顺序

 

5.以下程序运行时将在哪一行死掉

#include <stdio.h>

struct SS
{
	int i;
	int *p;
};

int main()
{
	struct  SS s;
	int *p = &s.i;
	p[0] = 3;    //让s.i的值为3
	p[1] = 3;    //让s.p的指针指向地址为3的空间,也就是s.p指针的内容是3
	s.p = p;     //让s.p指针与p指针指向同一块地方,也就是s.p指针的内容s.i的地址
	s.p[1] = 1;  //让s.p[1]指向的地方的内容为1,也就是他自己的内存内容为1
	s.p[0] = 2;  //此时s.p[0]指向地址为1的内存空间,让它等于2,这显然会出错,因为不知道那个内存空间是干什么的。前一步没有对这个位置内存进行修改,只是单纯指向,所以不会出错
	return 0;
}

将在 s.p[0] = 2; 这一行死掉

就算指针指向的不是数组,也能用数组形式表示指针,让他每次往后移动他本身所指向的数据类型的字节大小。

 

6.以下程序的运行结果是

#include <stdio.h>

union
{
	struct
	{
		unsigned char c1:2;
		unsigned char c2:3;
		unsigned char c3:3;
	}s;
	unsigned char c;
}u;

int main()
{
	u.c = 100;
	printf("%d\n",u.s.c3);

	return 0;
}

答案:3

v是联合体(共用体)变量,共有两个元素x和c,都需要一个字节,它们分配于同一个地址。而x是结构体变量,共有三个元素s1、s2、s3,分别占2位、3位、3位。分配内存时低位在前,最位在后。当有v.c=100(其二进制为01100100)时,各变量的关系及内存存储情况见图所示。

其中x的成员s3为二进制的011,即十进制的3,所以输出结果为3。

 

7.什么时候应该用宏代替函数?宏函数与自定义函数的区别

首先,宏定义就是用#define,不是typedef!

问题一:

一般简短的,逻辑不复杂,但是经常使用的代码,可以定义为宏

问题二:

1:宏做的是简单的字符串替换,宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以危险性高;但因为省去了函数的调用,返回,释放,所以效率比自定义函数高。函数的参数需要传递,参数是有类型的。

2: 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参。

3.:宏在预编译时进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.

4: 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.

5.:函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.

6.宏不能进行递归
(7.宏函数没有返回值,自定义函数有)

 

8.创建进程的方式有哪些?各自特点?

在Linux中主要提供了fork、vfork、clone三个进程创建方法。 

总结:

1. fork出来的子进程是父进程的一个拷贝,即,子进程从父进程得到了数据段和堆栈段的拷贝,这些需要分配新的内存;而对于只读的代码段,通常使用共享内存的方式访问;而vfork则是子进程与父进程共享内存空间, 子进程对虚拟地址空间任何数据的修改同样为父进程所见;clone则由用户通过参clone_flags 的设置来决定哪些资源共享,哪些资源拷贝。 

2. fork不对父子进程的执行次序进行任何限制,fork返回后,子进程和父进程都从调用fork函数的下一条语句开始行,但父子进程运行顺序是不定的,它取决于内核的调度算法;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制;clone中由标志CLONE_VFORK来决定子进程在执行时父进程是阻塞还是运行,若没有设置该标志,则父子进程同时运行,设置了该标志,则父进程挂起,直到子进程结束为止。

分别说明:

fork:fork创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。所以,这一步所做的是复制。这样得到的子进程独立于父进程, 具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,共享内存等机制。 fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。 
在fork之后,子进程和父进程都会继续执行fork调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由cpu执行的机器指令。

vfork:vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。 但此处有一点要注意的是用vfork()创建的子进程必须显示调用exit()来结束,否则子进程将不能结束,而fork()则不存在这个情况。 用 vfork创建子进程后,父进程会被阻塞直到子进程调用exec(exec,将一个新的可执行文件载入到地址空间并执行之。)或exit。vfork的好处是在子进程被创建后往往仅仅是为了调用exec执行另一个程序,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的 ,因此通过vfork共享内存可以减少不必要的开销。

clone:系统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone() 是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的 clone_flags来决定。另外,clone()返回的是子进程的pid。

 

参考:Linux下创建进程的三种方式及特点

 

9.非C++内建型别A和B, 在哪几种情况下B能隐式转化为A?

// Case 1 : B公有继承自A
class B : public A
{
    ...
}

// Case 2 : B实现了隐式转化为A的转化---相当于int(..)这种强制转换?
class B 
{
    operator A();
}

// Case 3 : A实现了non-explicit的参数为B的构造函数
//在A中写参数为B的拷贝构造函数
class A 
{
    A(const B&);
}

// Case 4 : 重写A的=操作符
A & operator=(const A&);

 

 

相关资讯

    暂无相关的资讯...

共有访客发表了评论 网友评论

验证码: 看不清楚?
    -->