c/c++ 面试笔试知识点----牛客网(4)

76. 
*p++和*(p++)都是先取*p的值再将p自增
77. 
char *p, *q;
p = (char *)malloc(sizeof(char) * 20);
q = p;
scanf(“%s %s”, p, q);
printf(“%s %s\n”, p, q);

两个指针指向同样的地址,故值是一样的
78. 
本题的正确答案是“ 贪心算法求解最优解的一般过程
分析如下所示:( 复制粘贴来自: http://blog.csdn.net/winbobob/article/details/38314821 )
1.贪心法的设计思想
         贪心算法在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。贪心算法对于大部分的优化问题都能产生最优解,但不能总获得整体最优解,通常可以获得近似最优解。
该算法存在问题:
1). 不能保证求得的最后解是最佳的;
2). 不能用来求最大或最小解问题;
3). 只能求满足某些约束条件的可行解的范围。
Dijkstra算法、Prim算法和Kruskal算法都属于典型的贪心算法

引例 [找零钱]
一个小孩买了价值少于1美元的糖,并将1美元的钱交给售货员。售货员希望用数目最少的硬币找给小孩。假设提供了数目不限的面值为2 5美分、1 0美分、5美分、及1美分的硬币。售货员分步骤组成要找的零钱数,每次加入一个硬币。选择硬币时所采用的贪婪准则如下:每一次选择应使零钱数尽量增大。为保证解法的可行性(即:所给的零钱等于要找的零钱数),所选择的硬币不应使零钱总数超过最终所需的数目
引例分析
为使找回的零钱的硬币数最小,不考虑找零钱的所有各种方案,而是从最大面值的币种开始,按递减的顺序考虑各币种,先尽量用大面值的币种,只当不足大面值币种的金额才会去考虑下一种较小面值的币种。这就是在采用贪婪法。这种方法在这里之所以总是最优,是因为银行对其发行的硬币种类和硬币面值的巧妙安排。如果只有面值分别为1,5和11单位的硬币,而希望找回总额为15单位的硬币,按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬币,共找回5个硬币。但最优的解答应是3个5单位面值的硬币。
贪心法的求解过程 
           用贪心法求解问题应该考虑如下几个方面:
(1)候选集合C:为了构造问题的解决方案,有一个候选集合C作为问题的可能解,即问题的最终解均取自于候选集合C。例如,在付款问题中,各种            面值的货币构成候选集合。
(2)解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成一个满足问题的完整解。例如,在付款问题中,已付出的货币构成解集合。
(3)解决函数solution:检查解集合S是否构成问题的完整解。例如,在付款问题中,解决函数是已付出的货币金额恰好等于应付款。
(4)选择函数select:即贪心策略,这是贪心法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。例如,在付款             问题中,贪心策略就是在候选集合中选择面值最大的货币。
(5)可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。例如,在付款问题中,可行函数是每一步选              择的货币和已付出的货币相加不超过应付款。
贪心法的一般流程
[cpp] view plain copy
Greedy(C)  //C是问题的输入集合即候选集合  
{  
    S={ };  //初始解集合为空集  
    while (not solution(S))  //集合S没有构成问题的一个解  
    {  
       x=select(C);    //在候选集合C中做贪心选择  
       if feasible(S, x)  //判断集合S中加入x后的解是否可行  
          S=S+{x};  
          C=C-{x};  
    }  
   return S;  
2.贪心法的基本要素
      对于一个具体的问题,怎么知道是否可用贪心算法解此问题,以及能否得到问题的最优解呢?这个问题很难给予肯定的回答。
      但是,从许多可以用贪心算法求解的问题中看到这类问题一般具有2个重要的性质:贪心选择性质和最优子结构性质。
子问题:假设为了解决某一优化问题,需要依次作出n个决策D1,D2,…,Dn,对于任何一个整数k,1 < k < n,以Dk作为问题的初始状态,来进行以后的决策,这样的问题就成为是原问题的一个子问题。
1) 贪心选择性质
      所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这是贪心算法可行的第一个基本要素。
贪心算法以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
      对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2) 最优子结构性质
       当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心算法求解的关键特征。
3.贪心算法与动态规划算法的差异
      贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。大多数时候,能用贪心算法求解的问题,都可以用动态规划算法求解。但是能用动态规划求解的,不一定能用贪心算法进行求解。(因为贪心选择性质比动态规划的两个属性约束更强)
79. 
一、静态成员的特点:
(1)static数据成员在类的内部声明,但只能在类的外部定义,在类的外部不能指定static,在类的定义时候进行初始化;
(2)static数据成员只能在类的外部进行初始化(特例:当整型const static数据成员被常量表达式初始化时,就可以在类的内部进行初始化,但还需要在外部进行定义)。
(3) static数据成员可以是该成员所属的类类型,而非static数据成员只能自身类的引用或者指针。
(4)static数据成员可以用作类成员函数的默认实参。
(5)static数据成员的值可以改变。
二、静态成员和非静态成员的区别:
(1) 静态变量使用 static 修饰符进行声明,在类被实例化时创建,通过类和对象都可以进行访问;
(2)不带有 static 修饰符声明的变量称做非静态变量,在对象被实例化时创建,通过对象访问;
(3) 一个类的所有实例的同一静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值。
(4) 静态函数的实现里不能使用非静态成员,如非静态变量、非静态函数等。
三、静态成员函数的特点:
(1) static 成员函数没有 this 形参,它可以访问所属类的 static 成员,但不能访问非 static 成员。
(2)static成员函数既可以在类的内部定义,也可以在类的外部定义,在外部定义时,不能重复指定static保留字。
(3)static成员函数不能声明为虚函数,不能实现动态绑定
(4)static 成员函数不能声明为const,因为const是针对this是不是const而言的
(5)构造函数、析构函数不能为静态函数。
80. 
当基类构造函数需要外部传递参数才能进行初始化时,派生类必须显式定义构造函数,为基类传递参数;基类如果不需要传递或者可以不传递参数,派生类可以不用显式定义构造函数。
81. 
mutalbe的中文意思是“可变的,易变的”,跟constant(既 C ++中的const)是反义词。
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。
82. 
char  a[] = "xyz", b[] = {'x', 'y', 'z'};
a容易理解,strlen(a)=3;
b是数组,元素在内存中是连续存储的,而strlen函数求字符串长度是要以'\0'结尾,但是b没有'\0',
strlen的内部函数指针会一直向后搜索,直至找到'\0',内存中的其他区域也是有数据的,只是没有意义,
所以,strlen的指针最后指到哪里无法确定,但是结果肯定大于等于3.
83. 
子类的指针访问虚函数访问的是子类的方法
子类的指针访问非虚函数访问的是子类的方法
父类的指针访问虚函数访问的子类的方法
父类的指针访问非虚函数访问的是父类的方法
84. 
用户态切换到内核态的   3   种方式
a.    系统调用
b.    异常
c.    外围设备的中断 
85. 
dynamic_cast :
继承体系安全向下转型或跨系转型;找出某对象占用内存的起始点
static_cast:
同旧式C转型,如int 到double
const_cast:
常用于去除某个对象的常量性
reinterpret_cast
不具备移植性,常见用途是转化函数指针类型
86.
map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。
unordered_map: unordered_map内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的
87. 
此处代码可以看出this指针内容为const,不允许进行修改,故编译不成功。
~myclass(){delete this;}会导致栈溢出,会不断调用析构函数。
88. 
int* p = new int (100) 是创建一个int型的内存,并赋值为100; 
int *p = new int[100] 是创建100个int型的内存;
一般用法是new一个数组的话一般是delete [] 其他的直接delete即可。
但是其实对于内置数据类型,其实是delete[] 和delete都可以的。
89. 
类型安全就是说,如果两个类型直接要相互转换,必须要显示的转换,不能偷偷摸摸的只用一个等于号就隐式转换了..
90. 
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
91. 
这种情况出现在多任务系统当中,在任务执行期间捕捉到 信号 并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时 中断 。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是 可重入 的,反之就是 不可重入 的。
满足下面条件之一的多数是不可重入函数:
(1)使用了静态数据结构;
(2)调用了malloc或free;
(3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
(4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
92. 
(1) 进程间通信方法有:文件映射、共享内存、匿名管道、命名管道、邮件槽、剪切板、动态数据交换、对象连接与嵌入、动态连接库、远程过程调用等
(2) 事件、临界区、互斥量、信号量可以实现线程同步
93. 
C++中函数可以嵌套调用,但是不可以嵌套定义。
94. 
深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。
95. 
malloc函数为C语言中的标准函数,标准中规定:在分配内存失败时会返回“NULL Pointer”空指针,而非为初始化的指针。
C++在分配内存失败时会抛出BAD_ALLOC异常。
野指针:指向垃圾内存的指针,而非空指针。
野指针产生原因:
    1.声明的指针未被初始化,指针默认值随机产生。创建指针应该将其初始化为NULL或者指向某一内存。
    2.free和delete掉的指针未重置为NULL,free后的指针仍指向该内存,但该内存已变为垃圾内存。
另:空指针不指向任何实际的对象或函数,反过来说对象或函数的指针也不可能为空指针。
96. 
声明纯虚函数的类是抽象类,不能实例化 ...基类被虚继承才是虚基类
97. 
 内联函数:
Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数.
定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用.
优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.
缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。
结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!
另一个实用的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).
有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.
-inl.h文件:
Tip: 复杂的内联函数的定义, 应放在后缀名为 -inl.h 的头文件中.
内联函数的定义必须放在头文件中, 编译器才能在调用点内联展开定义. 然而, 实现代码理论上应该放在 .cc 文件中, 我们不希望 .h 文件中有太多实现代码, 除非在可读性和性能上有明显优势.
如果内联函数的定义比较短小, 逻辑比较简单, 实现代码放在 .h 文件里没有任何问题. 比如, 存取函数的实现理所当然都应该放在类定义内. 出于编写者和调用者的方便, 较复杂的内联函数也可以放到 .h 文件中, 如果你觉得这样会使头文件显得笨重, 也可以把它萃取到单独的 -inl.h 中. 这样把实现和类定义分离开来, 当需要时包含对应的 -inl.h 即可。
98. 
E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法
99. 
CSingleLock
习多线程之三:线程同步--使用互斥
互斥跟临界区很相似,便远比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。通过CMutex来完成线程间的互质,即:CMutex Mutext;
从而我们可以这样来定义数据对象:
class CDataArray{private: int iArray[10]; CMutex Mutex;public: CDataArray(){}; ~CDataArray(){}; void SetData(int iValue); void GetDataArray(int aArray[10]);};成员函数实现如下:void CDataArray::SetData(int iValue){ CSingleLock SingleLock(&Mutex); SingleLock.Lock(); for (int i=0;i<10;i++) iArray[i]=iValue;}void CDataArray::GetDataArray(int aArray[10]){ CSingleLock SingleLock(&Mutex); SingleLock.Lock(); for (int i=0;i<10;i++) aArray[i]=iArray[i];}
为了访问一个互斥对象,务必建立一个CSingleLock或CMultiLock对象,用于访问控制。如果互斥没有被线程占用,那么当前的调用线程可以成为互斥的占用者。要实现对互斥的访问,就要调用CSingleLock的成员函数Lock(),即:
SingleLock.Lock();
如果一个线程占用了互斥,那么系统将挂起当前的调用线程,直到这个互斥被释放为止,这时,被挂起的线程将被唤醒并取得对互斥的控制。
释放互斥是通过调用CSingleLock的成员函数UnLock()来实现的。CDataArray的成员函数在退出时,将自动进行解锁操作。因为CSingleLock被创建在椎栈上,系统自动完成对UnLock()的调用。
100. int *p1=new int[10];该语句给指针p1分配内存,没有对指针初始化, int *p2=new int[10]();该语句给指针p2分配内存,并初始化指针

你可能感兴趣的:(c++,c语言,Liunx,c)