面试题

来源:互联网 发布:简历制作软件 app 编辑:程序博客网 时间:2024/06/04 19:18

1、 64位的Linux编译后的程序只能在64位linux下运行,32位linux编译后的程序只能在32位的linux运行;64位的windows编译后的程序只能在64位windows下运行,32位windows编译后的程序可以在64位的windows运行。
2、数据类型字节大小
32位操作系统:
char :1个字节(固定)
(即指针变量): 4个字节(32位机的寻址空间是4个字节。同理64位编译器)(变化)
short int : 2个字节(固定)
int:4个字节(固定)
unsigned int : 4个字节(固定)
float: 4个字节(固定)
double: 8个字节(固定)
long: 4个字节
unsigned long: 4个字节(变化*,其实就是寻址空间的地址长度数值)
long long: 8个字节(固定)
64位操作系统 :
char :1个字节(固定)
*(即指针变量): 8个字节
short int : 2个字节(固定)
int: 4个字节(固定)
unsigned int : 4个字节(固定)
float: 4个字节(固定)
double: 8个字节(固定)
long: 8个字节
unsigned long: 8个字节(变化*其实就是寻址控件的地址长度数值)
long long: 8个字节(固定)
总结:
除了*与long随操作系统子长变化而变化外,其他的都固定不变(32位和64相比)
bool 1个字节
char 1个字节
int 4个字节
float 4个字节
double 8个字节
long long 8个字节
3、算法的五大特性:
输入、输出、有穷性、确定性、可行性。
4、运算符优先顺序:
赋值运算符 < 逻辑与运算符 < 关系运算符 < 算术运算符
5、short最大能存放多大的十六进制数?
7fff
6、对两个字符数组 a 和 b 进行初始化 char a[]=”ABCDEF”; char b[]={‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’};数组a比数组b长度长。
7、C语言中最简单的数据类型?
整型、浮点型、字符型。
8、关键字extern的用处是什么?
在声明中使用关键字extern表明一个变量或函数已经在其他地方被定义过了。
9、代码块作用域变量具有哪种链接?
空链接。
10、要打印%,要使用%%。
11、哪一存储类的变量可以在多个文件中使用?哪一存储类的变量只限于在一个文件中使用?
静态外部链接存储类和静态内部存储器变量。
12、哪一存储类的变量在包含他们的程序运行时期内一直存在?
静态空链接存储类、静态内部链接存储类和静态外部链接存储类。
13、哪一存储类生成的变量对于包含他们的函数来说是局部变量?
自动存储类、寄存器存储类和静态空链接存储类。
14、编译器的作用是把源代码转换成机器语言代码,也叫对象代码;链接器的作用是把多个来源的目标代码(如已编译的源代码、库代码)链接成一个单独的可执行程序。
15、程序的局部变量存在于堆栈中,全局变量存在于静态区中,动态申请数据存在于中。
16、Heap与stack的差别
Heap是堆,stack是栈。
Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区.
C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。
17、何时需要预编译?
1)总是使用不经常改动的大型代码体。
2)程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
18、关键字static的作用是什么?
1)定义静态局部变量,作用域从函数开始到结束;
2)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
3)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝。
19、关键字const有什么含意?
1)表示常量是不可以修改的变量;
2)可以修饰参数,作为输入参数;
3)修饰函数,防止以外的改动;
4)修饰类的成员函数,不改变类中的数据成员。
20、ASSERT()是干什么用的?
ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。
ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。
assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。
21、const常量与define宏定义的区别
(1) 编译器处理方式不同。define宏是在预处理阶段展开,生命周期止于编译期。 只是一个常数、一个命令中的参数,没有实际的存在。
define常量存在于程序的代码段。
const常量是编译运行阶段使用,const常量存在于程序的数据段。
(2)类型和安全检查不同。define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
(3)存储方式不同。define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(可以是堆中也可以是栈中)
请说出const与#define 相比,有何优点?
const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
22、论述含参数的宏和函数的优缺点
(1)函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换
(2)函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开是在编译时进行的,在展开时不进行
内存分配,不进行值传递处理,没有“返回值”概念
(3)对函数中的形参和实参都要定义类型,类型要求一致,如不一致则进行类型转换。而宏不存在类型问题
(4)调用函数只可得到一个返回值,而用宏则可以设法得到几个结果
(5)使用宏次数多时,宏展开后源程序变长,函数调用则不会
(6)宏替换不占用运行时间,只占编译时间,而函数调用占用运行时间
23、关键字volatile有什么含意?并给出三个不同的例子
一个定义为volatile的变量是说这变量可能会被意想不到地改变,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值
而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
深究:一个参数既可以是const还可以是volatile,一个例子是只读的状态寄存器, 它是volatile因为它可能被意想不到地改变,是const因为程序不应该试图去修改它。
一个指针可以是volatile,一个例子是当一个中服务子程序修改一个指向一个buffer的指针时。
24、什么是深浅拷贝?
浅拷贝是创建了一个对象用一个现成的对象初始化它的时候只是复制了成员(简单赋值)而没有拷贝分配给成员的资源(如给其指针变量成员分配了动态内存); 深拷贝是当一个对象创建时,如果分配了资源,就需要定义自己的拷贝构造函数,使之不但拷贝成员也拷贝分配给它的资源。
25、变量的本质是一段连续内存空间的别名;
数据类型的本质是固定内存大小的别名。
26、
栈区地址生长方向:地址由上往下递减
堆区地址生成方向:地址由下往上递增
27、main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?
可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void)。
28、i++ 相比 ++i 哪个更高效?为什么?
++i 比 i++效率高。i++要多调用一次类的构造和析构函数。
29、函数模板:
建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代替。
30、函数模板和普通函数的区别?
函数模板不允许自动类型转化,普通函数能够自动进行类型转化。
31、函数模板机制结论:
编译器并不是把函数模板处理成能够处理任何类型的函数
函数模板通过具体类型产生不同的函数
编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
32、Template有什么特点?什么时候用?
Template可以独立于任何特定的类型编写代码,是泛型编程的基础.
当我们编写的类和函数能够多态的用于跨越编译时不相关的类型时,用Template.
模板主要用于STL中的容器,算法,迭代器等以及模板元编程.
(C++的template是实现在库设计和嵌入式设计中的关键。template能实现抽象和效率的结合;同时template还能有效地防止代码膨胀)
33、C++中为什么用模板类?
1)可用来创建动态增长和减小的数据结构
2)它是类型无关的,因此具有很高的可复用性
3)它在编译时而不是运行时检查数据类型,保证了类型安全
4)它是平台无关的,可移植性
5)可用于基本数据类型
34、什么是封装?C++中是如何实现的?
封装来源于信息隐藏的设计理念, 是通过特性和行为的组合来创建新数据类型让接口与具体实现相隔离。C++中是通过类来实现的, 为了尽量避免某个模块的行为干扰同一系统中的其它模块,应该让模块仅仅公开必须让外界知道的接口。
35、什么是拷贝构造函数?
它是单个参数的构造函数,其参数是与它同属一类的对象的(常)引用;类定义中,如果未提供自己的拷贝构造函数,C++提供一个默认拷贝构造函数,该默认拷贝构造函数完成一个成员到一个成员的拷贝。
36、面向对象程序设计的优点?
开发时间短, 效率高, 可靠性高。面向对象编程的编码具有高可重用性,可以在应用程序中大量采用成熟的类库(如STL),从而缩短了开发时间,软件易于维护和升级。
37、什么是动态特性?
在绝大多数情况下, 程序的功能是在编译的时候就确定下来的, 我们称之为静态特性。 反之, 如果程序的功能是在运行时刻才能确定下来的, 则称之为动态特性。C++中, 虚函数,抽象基类, 动态绑定和多态构成了出色的动态特性。
38、重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
从定义上来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。
39、多态,虚函数,纯虚函数
多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;
在程序编译时多态性体现在函数和运算符的重载上;
虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。
纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在纯虚函数不具备函数的功能,一般不能直接被调用。
从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。
抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。
40、构造函数和析构函数是否可以被重载,为什么?
构造函数可以被重载,析构函数不可以被重载。因为构造函数可以有多个且可以带参数,而析构函数只能有一个,且不能带参数。
41、运算符重载的三种方式?
普通函数,友元函数,类成员函数。
42、不允许重载的5个运算符是哪些?
1)*(成员指针访问运算符号)
2)::域运算符
3)Sizeof 长度运算符号
4)?:条件运算符号
5).(成员访问符)
43、什么时候必须重写拷贝构造函数?
当构造函数涉及到动态存储分配空间时,要自己写拷贝构造函数,并且要深拷贝。
44、静态成员函数能不能同时也是虚函数?
不能。调用静态成员函数不要实例。但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。两者相互矛盾。
45、C++编译器自动为类产生的四个缺省函数是什么?
默认构造函数,拷贝构造函数,析构函数,赋值函数。
46、C++中哪些函数不能被声明为虚函数?
普通函数(非成员函数),构造函数,内联成员函数、静态成员函数、友元函数。
(1)虚函数用于基类和派生类,普通函数所以不能
(2)构造函数不能是因为虚函数采用的是虚调用的方法,允许在只知道部分信息的情况的工作机制,
特别允许调用只知道接口而不知道对象的准确类型的方法,但是调用构造函数即使要创建一个对象,
那势必要知道对象的准确类型。
(3)内联成员函数的实质是在调用的地方直接将代码扩展开
(4)继承时,静态成员函数是不能被继承的,它只属于一个类,因为也不存在动态联编等
(5)友元函数不是类的成员函数,因此也不能被继承
47、在C++程序中调用C编译后的函数,为什么要加extern C的声明?
因为C++支持函数重载,而C不支持函数重载,函数被C++编译后在库中的名字与C语言的不同。C++提供extern C来解决名字匹配问题。
48、引用必须初始化,指针则不必;
引用初始化以后不能改变,指针可以改变其指向的对象;
不存在指向空值的引用,但存在指向空值的指针;
引用是某个对象的别名,主要用来描述函数和参数和返回值。而指针与一般的变量是一样的,会在内存中开辟一块内存。
如果函数的参数或返回值是类的对象的话,采用引用可以提高程序的效率。
49、子类重新定义父类的虚函数的做法叫覆盖,override,而不是overload(重载),重载的概念不属于面向对象编程,重载指的是存在多个同名函数,这些函数的参数表不同..重载是在编译期间就决定了的,是静态的,因此,重载与多态无关.与面向对象编程无关。
50、c和c++中的struct有什么不同?
c和c++中struct的主要区别是c中的struct不可以含有成员函数,而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private。
51、C++四种强制类型转换
(1)const_cast
字面上理解就是去const属性,去掉类型的const或volatile属性。
struct SA{ int k};
const SA ra;
ra.k = 10; //直接修改const类型,编译错误
SA& rb = const_cast (ra);
rb.k = 10; //可以修改
(2)static_cast
主要用于基本类型之间和具有继承关系的类型之间的转换。用于指针类型的转换没有太大的意义。static_cast是无条件和静态类型转换,可用于基类和子类的转换,基本类型转换,把空指针转换为目标类型的空指针,把任何类型的表达式转换成void类型,static_cast不能进行无关类型(如非基类和子类)指针之间的转换。
int a;
double d = static_cast(a); //基本类型转换
int &pn = &a;
void *p = static_cast(pn); //任意类型转换为void
(3)dynamic_cast
你可以用它把一个指向基类的指针或引用对象转换成继承类的对象动态类型转换,运行时类型安全检查(转换失败返回NULL)基类必须有虚函数,保持多态特性才能用dynamic_cast,只能在继承类对象的指针之间或引用之间进行类型转换。
class BaseClass{public: int m_iNum; virtual void foo(){};};
class DerivedClass:BaseClass{public: char* szName[100]; void bar(){};};
BaseClass* pb = new DerivedClass();
DerivedClass *p2 = dynamic_cast(pb);
BaseClass* pParent = dynamic_cast(p2);
//子类->父类,动态类型转换,正确
(4)reinterpreter_cast
转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。主要是将一个类型的指针,转换为另一个类型的指针,不同类型的指针类型转换用reinterpreter_cast,最普通的用途就是在函数指针类型之间进行转换。
int DoSomething(){return 0;};
typedef void(*FuncPtr)(){};
FuncPtr funcPtrArray[10];
funcPtrArray[0] = reinterpreter_cast(&DoSomething);
* 52、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部 变量有什么区别?static函数与普通函数有什么区别?*
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
53、将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
54、定义各种类型指针
用变量a给出下面的定义
a)一个整型数
b)一个指向整型数的指针
c)一个指向指针的指针,它指向的指针是指向一个整型数
d)一个有10个整型数的数组
e)一个有10个指针的数组,该指针是指向一个整型的数
f)一个指向有10个整型数数组的指针
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

int a;int* a;int** a;int a[10];int* a[10];int(*a)[10];int(*a)(int);int(*a[10])(int);

数组指针,函数指针的声明方式相对要复杂一些。这种情况下我们先找到变量名,然后再看哪个符号先和变量名结合,如果是号说明此变量是一指针变量。如果是[ ]符号则说明此变量是一数组。如f 和g中号先和a结合说明变量是一指针,而此时括号中再没有其它信息,我们再向右看,f右边是[ ]说明指向的是一个数组,最后我们再看左边说明指向的数组的元素是int类型; 在g中我们向右看是一对括号,表示指向的是一个函数参数为int,最后看左边为返回值类型。 h中我们看到a是一数组,括号中左边还有一个星号,说明数组元素为指针,括号中再没有其它信息,再向右看,(int)说明指向类型为函数,参数为int,最后向左看表示返回值为int。
55、已知float x=1,y;则y=++x*++x的结果是9.
56、有以下程序

#include <stdio.h>void main( ){  int x=102,y=012;   printf(“%2d,%2d\n”,x,y);}

执行后输出结果是102,10.
X=102是十进制,y=012是八进制,而%d是按照十进制打印,所以它会把012换算成十进制,就是10。
57、若有以下程序段:

#include <stdio.h>void main(){       int a=2,b=5;    printf("a=%%d,b=%%d\n",a,b);}

其输出结果是a=%d,b=%d。
两个%代表输出一个% 所以这里的d起不到作用。
58、有一段程序

#include <stdio.h>void main(){     double d=3.2;     int x,y;    x=1.2;y=(x+3.8)/5.0;    printf("%d\n",d*y);}

这里按照运算顺序结果=3.2,但是是按照%d来打印,所以打印小数会解析失败,所以会打印0。
59、有以下程序

#include <stdio.h>void main(){      unsigned char a,b; //0000 0100    a=4|3;             //0000 0011    b=4&3;    printf("%d %d\n",a,b);}

执行后输出结果是7,0.
这里打印的不是逻辑的值0与非零,在这里是要把4和3换算成2进制运算,然后再换算成十进制打印。
60、设a和b均为double型变量,且a=5.5、b=2.5,则表达式(int)a+b/b的值是6.000000。
这里(int)是只把a转换为int类型,不是把整个表达式的结果转换成int类型
(int)5.5+2.5/2.5 -> 5+1.000000 -> 6.000000
61、若x、a、b、c均 为int型变量,则执行表达式x= (a = 4, b = 8, c = 12)后,x的值为12.
逗号表达式的值就是最后一个逗号后面的表达式的值.
62、 以下程序的功能是进行位运算

#include <stdio.h>void main(){     unsigned char a, b;    0111  0100->1011    a=7^3; b= ~4 & 3;      0011        0011    printf("%d %d\n",a,b);}

程序运行后的输出结果是4,3.
63、

#include <stdio.h>void main(){    int a=-1, b=4, k;    k = ( ++a < 0) && ( b += 5);    printf ("%d%d%d \n", k, a, b );}

程序的输出结果是004.
按优先级运算 ++a=0 0<0=0 后面无意义 k=0 a=0 b=4
64、在c语言中,非char型数据在内存中的存储形式是补码
在c语言中,char型数据在内存中的存储形式是ASCII码
65、有一段代码

#include <stdio.h>void main(){     int a,b,c;    a=b=c=1;    if (a++||++b) c++;    printf("%d,%d,%d\n",a,b,c);}

运行结果为2,1,2.
66、野指针:没有进行初始化操作的指针–>由于该指针变量内部所存储的地址是个随机值,因此是野地址(类型含义:指针)
注:指针类型的变量必须在其被创建的时候就需要进行初始化操作,否则就成了野指针,所谓野指针就是乱指向的指针,形成的就是一个随机垃圾地址
胡乱使用野指针所造成的现象:
(1).指向驱动程序所属内存,将会出现蓝屏现象
(2).指向普通的应用程序,会将本身的这个应用程序结束掉
67、strcpy();和strcat();区别:
strcpy();从目标字符串的首地址开始拷贝
strcat();从目标字符串的末尾字符串结束标识符’\0’开始拷贝
68、静态局部变量

#include <iostream> using namespace std; int f(int a)                          //定义f函数,a为形参 {auto int b=0;                      //定义b为自动变量 static int c=3;                     //定义c为静态局部变量 b=b+1; c=c+1; return a+b+c; } int main( ) {int a=2,i; for(i=0;i<3;i++)     cout<<f(a)<<″ ″; cout<<endl; return 0; } 

运行结果为 7 8 9
**69、**strlen函数返回s所指的字符串的长度。该函数从s所指的第一个字符开始找’\0’字符,一旦找到就返回,返回的长度不包括’\0’字符在内。例如定义char buf[] = “hello”;,则strlen(buf)的值是5,但要注意,如果定义charbuf[5] = “hello”;,则调用strlen(buf)是危险的,会造成数组访问越界。
70、strlen与sizeof的区别
1、strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个’\0’,如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到’\0’停止。
若char aa[10]; 则strlen(aa)结果是不定的
2、而sizeof()返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个操作符,strlen是函数。
sizeof(aa) 返回10
int a[10]; sizeof(a) 返回40
3、sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
该类型保证能容纳实现所建立的最大对象的字节大小。
4、sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以”\0”结尾的。sizeof还可以用函数做参数。
5、数组做sizeof的参数不退化,传递给strlen就退化为指针了。
71、 memmove也是从src所指的内存地址拷贝n个字节到dest所指的内存地址,虽然叫move但其实也是拷贝而非移动。但是和memcpy有一点不同,memcpy的两个参数src和dest所指的内存区间如果重叠则无法保证正确拷贝,而memmove却可以正确拷贝。假设定义了一个数组char buf[20] = “hello world\n”;,如果想把其中的字符串往后移动一个字节(变成”hhello world\n”),调用memcpy(buf + 1,buf, 13)是无法保证正确拷贝的。
72、进程与线程的关系与区别
进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
(4)处理机分给线程,即真正在处理机上运行的是线程。
(5)线程是指进程内的一个执行单元,也是进程内的可调度实体。
线程与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
(4)系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。
73、redis面试题总结
1. 使用redis有哪些好处?
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
2. redis常见性能问题和解决方案:
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
3.mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
4.请用Redis和任意语言实现一段恶意登录保护的代码,限制1小时内每用户Id最多只能登录5次。具体登录函数或功能用空函数即可,不用详细写出。
用列表实现:列表中每个元素代表登陆时间,只要最后的第5次登陆时间和现在时间差不超过1小时就禁止登陆.用Python写的代码如下:

#!/usr/bin/env python3import redis  import sys  import timer = redis.StrictRedis(host=’127.0.0.1′, port=6379, db=0)  try:      id = sys.argv[1]except:      print(‘input argument error’)    sys.exit(0)if r.llen(id) >= 5 and time.time() – float(r.lindex(id, 4)) <= 3600:      print(“you are forbidden logining”)else:      print(‘you are allowed to login’)    r.lpush(id, time.time())    # login_func()

5.redis 数据结构使用场景
String——字符串
Hash——字典
List——列表
Set——集合
Sorted Set——有序集合

0 0
原创粉丝点击