C、C++、数据结构
1、编译系统的四个步骤: 预处理,编译,汇编,链接
2、整型和长整型的区别?
早期的计算机,16编译器, 整型是16位,长整型是32位的。
从C99规定开始,整型和长整型都是32位,也就是我们sizeof得出的4个字节
3、for(;;)和while(1)无限循环的区别:
for(;;)在c语言中指令较少,也能够节省内存,没有判断跳转,是比while(1)更好的无限循环
4、指针和数组的组合
int a; //整型数a
int *a; //整型指针a
int **a; //指向指针的指针,a指针返回一个整型
int a[10]; //数组a里有10个整型
int *a[10]; //数组a里面10个 指针,指针指向整型
int (*a)[10]; //指针a指向10个整型数组
int *a(int); //指针a指向函数,函数参数是整型,返回值是整型
int (*a[10])(int); //数组a里面10个 指针,指针指向函数,函数参数是整型,返回值是整型
5、关键字static的作用
- 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
- 在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
- 在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
- 在类的static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝
- 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量。
6、const的作用?
1)可以定义const常量量,具有不可变性。
2)便于进行类型检查,使编译器对处理内容有更好了解,消除一些隐患。
3)可以避免意义模糊的数字出现,同样可以方便进行参数的调整和修改可以保护被修饰的东西,防止意外的修改。
4)为函数重载提供一个参数。
5)可以节省空间,避免不必要的内存分配。
6)编译器通常不为普通的const常量分配内存,将他保存在符号表,没有存储和读取的操作,效率变高。
7、如果一个函数没有显式地声明返回值,那么默认的返回值就是int型的
在c语言中,如果一个函数没有显式地说明参数是void,那么是可以使用参数的进行调用的
在C++这样是不可以的
8、大小端问题值得注意:跟处理器有关,可以使用程序判定
#include <iostream>
using namespace std;
union Test
{
int a;
char b;
};
int main()
{
Test t;
t.a = 1;
if (t.b == 1)
cout << "小端" << endl;
else
cout << "大端" << endl;
return 0;
}
9、enum是在编译阶段确定其值的
10、const修饰的只读变量不能用来作为定义数组的维数,也不能放在case关键字后面
11、strlen和sizeof的区别?
1)strlen是一个函数,sizeof是一个运算符。
2)sizeof获得保证能容纳实现所建立的最大对象的字节大小,不能用来返回动态分配的内存空间的大小。
3)strlen要在运行时才能计算,参数必须是字符型指针,返回字符串的长度。
12、指针和引用的区别
1.引用是变量的一个别名,内部实现是只读指针
2.引用只能在初始化时被赋值,其他时候值不能被改变,指针的值可以在任何时候被改变
3.引用不能为NULL,指针可以为NULL
4.引用变量内存单元保存的是被引用变量的地址
5.“sizeof 引用" = 指向变量的大小 , "sizeof 指针"= 指针本身的大小
6.引用可以取地址操作,返回的是被引用变量本身所在的内存单元地址
7.引用使用在源代码级相当于普通的变量一样使用,做函数参数时,内部传递的实际是变量地址
13、C++中有了malloc / free , 为什么还需要 new / delete
- malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
- 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
- 因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
14、堆和栈的区别
- 栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等,操作方式类似于数据结构中的栈。
- 堆区(heap) : 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,作方式类似于数据结构中的链表。
15、不调用C/C++ 的字符串库函数,编写strcpy
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||strSrc==NULL))
return NULL;
char * strDestCopy=strDest;
while ((*strDest++=*strSrc++)!='\0');
*strDest = '\0';
return strDestCopy;
}
16、关键字static的作用
1.函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
2.在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问
3.在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内
4.在类的static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝
5.在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量
17、什么时候要用虚析构函数
通过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,造成内存泄漏。
原因:在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数。那么,析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。
注意:如果不需要基类对派生类及对象进行操作,则不能定义虚函数(包括虚析构函数),因为这样会增加内存开销。
18、C++怎样让返回对象的函数不调用拷贝构造函数
拷贝构造函数前加 “explicit” 关键字
19、请用简单的语言告诉我C++ 是什么?
C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。C++支持多种编程范式 --面向对象编程、泛型编程和过程化编程。 其编程领域众广,常用于系统开发,引擎开发等应用领域。
20、C和C++的区别?
C++在c的基础上增添类,C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。
21、什么是多态?
多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。
22、类的static变量在什么时候初始化?函数的static变量在什么时候初始化?
类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的static变量在执行此函数时进行初始化。
23、解释下封装、继承和多态?
封装:封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
继承:继承主要实现重用代码,节省开发时间。子类可以继承父类的一些东西。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
24、什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?
用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。
使用的时候要记得指针的长度。malloc的时候得确定在那里free.对指针赋值的时候应该注意被赋值指针需要不需要释放.动态分配内存的指针最好不要再次赋值.
25、常用的排序算法有哪些?简单描述几个排序算法的优缺点?
选择、冒泡、快速、希尔、归并、堆排等。
1.快排:是冒泡排序的一种改进。
优点:快,数据移动少
缺点:稳定性不足
2.归并:分治法排序,稳定的排序算法,一般用于对总体无序,但局部有序的数列。
优点:效率高O(n),稳定
缺点:比较占用内存
26、解释C++中静态函数和静态变量?
- 类静态数据成员在编译时创建并初始化:在该类的任何对象建立之前就存在,不属于任何对象,而非静态类成员变量则是属于对象所有的。类静态数据成员只有一个拷贝,为所有此类的对象所共享。
- 类静态成员函数属于整个类,不属于某个对象,由该类所有对象共享。
- 静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。
- 静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时 this指针时被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。
27、
28、重载 ( overload) 和覆盖 (override 重写)的区别?
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
29、多态的作用?
主要是两个:
- 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
- 接口重用:为了类在继承和派生的时候 ,保证使用家族中任一类的实例的某一属性时的正确调用 。
30、类成员函数的重载、覆盖和遮蔽区别?
a. 成员函数被重载的特征:
( 1 )相同的范围(在同一个类中);
( 2 )函数名字相同;
( 3 )参数不同;
( 4 ) virtual 关键字可有可无。
b. 覆盖是指派生类函数覆盖基类函数,特征是:
( 1 )不同的范围(分别位于派生类与基类);
( 2 )函数名字相同;
( 3 )参数相同;
( 4 )基类函数必须有 virtual 关键字。
c. 遮蔽是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
( 1 )如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。
( 2 )如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
31、什么是动态特性?
在绝大多数情况下, 程序的功能是在编译的时候就确定下来的, 我们称之为静态特性。 反之, 如果程序的功能是在运行时刻才能确定下来的, 则称之为动态特性。C++中, 虚函数,抽象基类, 动态绑定和多态构成了出色的动态特性。
32、在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?
函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。
33、内联函数INline和宏定义一起使用的区别。
内联函数是在编译的时候已经做好将对应的函数代码替换嵌入到对应的位置,适用于代码较少的函数。 宏定义是简单的替换变量,如果定义的是有参数的函数形式,参数不做类型校验。
34、一个空类默认存在的成员函数有哪些?
默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值运算符 这四个是我们通常大都知道的。但是除了这四个,还有两个,那就是取址运算符和 取址运算符 const
即总共有六个函数。
class Empty
{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
35、C++中哪些函数不能被声明为虚函数?
普通函数(非成员函数),构造函数,内联成员函数、静态成员函数、友元函数。
(1)虚函数用于基类和派生类,普通函数所以不能
(2)构造函数不能是因为虚函数采用的是虚调用的方法,
(3)内联成员函数的实质是在调用的地方直接将代码扩展开
(4)继承时,静态成员函数不能被继承的,它只属于一个类,因为也不存在动态联编
(5)友元函数不是类的成员函数,因此也不能被继承
36、类的静态成员和非静态成员有何区别?
类的静态成员每个类只有一个,静态成员为所有类的实例对象共享,静态成员有静态成员变量和静态成员函数,静态成员变量使用前必须初始化,静态成员变量可以被静态成员函数和非静态成员函数访问,而静态成员函数只能访问静态成员变量,因为静态成员函数属于类,其没有this指针。非静态成员每个对象都有一个。
37、继承的优缺点。
类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
38、在什么时候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就 应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
39、什么是浅拷贝?什么是深拷贝?
浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
一般来说,浅拷贝就是复制那个对象的指针。深拷贝就是复制了那个对象。
40、简述多态实现的原理
编译器发现一个类中有虚函数,便会立即为此类生成虚函数表 vtable。虚函数表的各表项为指向对应虚函数的指针。编译器还会在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)指向虚函数表。调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable,将类与此类的vtable联系了起来。另外在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,。如此才能真正与函数体进行连接,这就是动态联编,实现多态的基本原理。
41、谈谈你对面向对象的认识
解析:面向对象可以理解成对待每一个问题,都是首先要确定这个问题由几个部分组成,而每一个部分其实就是一个对象。然后再分别设计这些对象,最后得到整个程序。传统的程序设计多是基于功能的思想来进行考虑和设计的,而面向对象的程序设计则是基于对象的角度来考虑问题。这样做能够使得程序更加的简洁清晰。
42、C++中为什么用模板类。
- 可用来创建动态增长和减小的数据结构
- 它是类型无关的,因此具有很高的可复用性。
- 它在编译时而不是运行时检查数据类型,保证了类型安全
- 它是平台无关的,可移植性
- 可用于基本数据类型
43 、函数模板与类模板有什么区别?
函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。
44、C++是不是类型安全的?
不是。两个不同类型的指针之间可以强制转换
45、流操作符重载为什么返回引用
在程序中,流操作符>>和<<经常连续使用。因此这两个操作符的返回值应该是一个仍旧支持这两个操作符的流引用。其他的数据类型都无法做到这一点。
注意:除了在赋值操作符和流操作符之外的其他的一些操作符中,如+、-、*、/等却千万不能返回引用。因为这四个操作符的对象都是右值,因此,它们必须构造一个对象作为返回值。
46、多态, 虚函数, 纯虚函数
多态:不同对象接收相同的消息产生不同的动作。多态包括 编译时多态和 运行时多态
运行时多态是:通过继承和虚函数来体现的。 编译时多态:运算符重载上。
虚函数: 在基类中用virtual的成员函数。允许在派生类中对基类的虚函数重新定义。 基类的虚函数可以有函数体,基类也可以实例化。 虚函数要有函数体,否则编译过不去。 虚函数在子类中可以不覆盖。 构造函数不能是虚函数。
纯虚函数:基类中为其派生类保留一个名字,以便派生类根据需要进行定义。 包含一个纯虚函数的类是抽象类。 纯虚函数后面有 = 0; 抽象类不可以实例化。但可以定义指针。 如果派生类如果不是先基类的纯虚函数,则仍然是抽象类。 抽象类可以包含虚函数。
47、抽象类和接口的区别
在C++里面抽象类就是接口
抽象类:定义了纯虚函数的类是抽象类,不能实例化。
抽象类包括抽象方法(纯虚方法),也可以包含普通方法。 抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖。
虽不能定义抽象类的实例,但是可以定义抽象类的指针。
48、将引用作为函数参数有哪些特点
(1)与指针调用效果一样。
(2)引用传参,内存中并没有产生副本。
(3)用指针传递,也要给形参分配存储单元;并且需要使用"*变量的"的形式,可读性差;另外,调用的地方还得用地址作为实参。
49、引用作为函数返回值类型的格式,好处和规则?
int &fun(int a) {}
好处:不会生成副本。
规则:不能返回局部变量的引用;不能返回函数内部new分配的内存引用;如果返回成员的话,返回const
50、结构与联合的区别
联合是公用存储单元的,任何一个时刻只有一个被选中的成员。一旦赋值后,其他成员也覆盖了。
51、重载(overload)和重写(override)?
重载:多个同名函数,参数不同(个数不同,参数类型不同);是同一层级的函数;静态绑定;编译期绑定。
重写:子类重新定义父类函数的方法;是动态绑定。
52、main函数之前会执行什么代码?
全局变量的初始化。
53、const 与 #define相比优点?
const: 定义常量; 修饰函数参数; 修饰函数返回值; 修饰类成员函数。
好处: const 修饰的有数据类型,而宏没有,所以可以做类型检查;而宏仅作字符替换,无安全检查。
const常量可以调试
宏有副作用,不加括号的话有副作用。
54、为什么基类的析构函数是虚函数?
动态绑定,不会造成潜在的内存泄漏
55、子类不能继承父类的函数有哪些
子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数。
56、虚函数 VS 纯虚函数
虚函数为了重载和多态,在基类中是有定义的,即便定义为空。在子类中可以重写。
纯虚函数在基类中没有定义,必须在子类中实现。
多态的基础是继承,需要虚函数的支持。
57、开发中常用的数据结构:
A:数组和链表:
数组大小不能动态定义。链表和动态分配大小的。
数组不适应动态增/减的情况,因为大小固定,一旦定义就不能改变。
链表适合动态的增加、删除数据。
数组的随机访问快。
数组栈中分配; 链表在堆中。
B:二叉树遍历: 先序、中序、后序。
58、栈溢出的原因:
栈大小有限制:分过多的数组;
递归调用层太深;
59、频繁出现的短小的函数,在c/C++中分别如何实现
c中用宏定义; C++ 内联
60、 C++函数传参数方式
值传递、指针、引用
61、 .h头文件中的ifndef/define/endif作用
防止头文件重复包含
62、什么是平衡二叉树?
左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于 1 。
63、用两个栈实现一个队列的功能?要求给出算法和思路!
设 2 个栈为 A,B, 一开始均为空 .
入队 :
将新元素 push 入栈 A;
出队 :
(1) 判断栈 B 是否为空;
(2) 如果不为空,则将栈 A 中所有元素依次 pop 出并 push 到栈 B ;
(3) 将栈 B 的栈顶元素 pop 出;
这样实现的队列入队和出队的平摊复杂度都还是 O(1)
64、关键字 static 的作用是什么?
在 C 语言中,关键字 static 有三个明显的作用:
- 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
- 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
- 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
65、什么是预编译 , 何时需要预编译 ?
预编译又称为预处理 , 是做些代码文本的替换工作。处理 # 开头的指令 , 比如拷贝 #include 包含的文件代码, #define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段,主要处理 # 开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
66、Itearator 各指针的区别
游标是指针,但不仅仅是指针。游标和指针很像,功能很像指针,但是实际上,游标是通过重载一元的 ”*” 和 ”->” 来从容器中间接地返回一个值。将这些值存储在容器中并不是一个好主意,因为每当一个新值添加到容器中或者有一个值从容器中删除,这些值就会失效。在某种程度上,游标可以看作是句柄( handle )。通常情况下游标( iterator )的类型可以有所变化,这样容器也会有几种不同方式的转变: iterator——对于除了 vector 以外的其他任何容器,你可以通过这种游标在一次操作中在容器中朝向前的方向走一步。这意味着对于这种游标你只能使用 “++” 操作符。而不能使用 “--” 或 “+=” 操作符。而对于 vector 这一种容器,你可以使用 “+=” 、 “—” 、 “++” 、 “-=” 中的任何一种操作符和 “<” 、 “<=” 、 “>” 、 “>=” 、 “==” 、 “!=” 等比较运算符。
67、数组和链表的优缺点
数组,在内存上给出了连续的空间。链表,内存地址上可以是不连续的,每个链表的节点包括原来的内存和下一个节点的信息(单向的一个,双向链表的话,会有两个)。
数组优于链表的:
A. 内存空间占用的少。
B. 数组内的数据可随机访问,但链表不具备随机访问性。
C. 查找速度快
链表优于数组的:
A. 插入与删除的操作方便。
B. 内存地址的利用率方面链表好。
C. 方便内存地址扩展。
Linux部分
1、TCP和UDP有什么区别
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。
UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
2、编写socket套接字的步骤
服务器:建立套接字、绑定、监听、接收连接、读写数据、关闭连接
客户端:建立套接字、连接、读写数据、关闭连接
3、同步IO和异步IO的区别?
A. 同步
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
最常见的例子就是 SendMessage。
该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。
B. 异步
异步的概念和同步相对。
当一个异步过程调用发出后,调用者不会立刻得到结果。
实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。
4、进程间通信的方式有?
进程间通信的方式有 :共享内存, 管道(有名管道/无名管道),Socket ,消息队列 ,信号,信号量,内存映射等。
5、死锁的四个必要条件?
互斥,请求保持,不可剥夺,环路。
6、进程和线程的差别。
线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
7、多线程如何同步
windows线程同步有四种方式:临界区、内核对象、互斥量、信号量。
Linux线程同步有最常用的是:互斥锁、条件变量和信号量。