C/C++面试(一)

1、

int main(void)
{
printf("%s , %5.3s\n","computer","computer");
return 0;
}

输出

computer , com
%m.ns 输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格。

2、
虚函数动态绑定
动态联编就是程序在运行的时候知道该调用哪个函数,而不是编译阶段,所以这个机制应该是由虚函数支持的,即运行时的多态,基类的某个成员函数声明为虚函数,派生类继承,而且同样重写该函数,那么当声明一个派生类的指针或者引用时,它所调用的函数是由该指针指向的对象确定的,这就是动态联编

3、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class parent  
{  
    public:  
    virtual void output();  
};  
void parent::output()  
{  
    printf("parent!");  
}  
       
class son : public parent  
{  
    public:  
    virtual void output();  
};  
void son::output()  
{  
    printf("son!");  
}
1
2
3
4
son s; 
memset(&s , 0 , sizeof(s)); 
parent& p = s; 
p.output(); 
 执行结果是()
没有输出结果,程序运行出错
memset 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 
本代码中, 不虚函数链表地址也清空了, 所以p.output调用失败。 output函数的地址编程0

4、
#pragma pack(2)
class BU
{
    int number; // 4
    union UBffer
    {
        char buffer[13]; // 13
        int number; // 4
    }ubuf; // union的大小取决于它所有的成员中,占用空间最大的一个成员的大小,并且需要内存对齐,这里因为#pragma pack(2),所以union的大小为14,如果不写#pragma pack(2),那么union大小为16【因为与sizeof(int)=4对齐】
    void foo(){} //0
    typedef char*(*f)(void*); //0
    enum{hdd,ssd,blueray}disk; // 4
}bu;
因此sizeof(union) = 4+14 +0 +0 +4 = 22

C++ 类占用空间计算方式

1、一个类占用的空间主要是属性占用空间,而成员函数一般不占用空间,但是虚函数占用空间,需要说明的是,无论多少个虚函数,只要占用4个字节即可,也就是索引指向一个虚拟表的首位置。另外需要说明的是占用空间都考虑了对齐,所以不足4个的按照满4个的算。


2、类的继承,子类占用空间是父类基础上增加本类空间即可。所以说可以认为,子类就是直接拷贝了父类的内容,然后结合自身的内容。而且存储空间也是这个顺序,即先父类分配空间,然后才是子类空间。


3、静态成员变量不占用类空间,应该是确实没有放入这个类的里面,而且没有指针指向它,只能通过类::来访问,也就是说静态成员是随着类的存在而存在,而 不依赖于对象,它的存在意义主要还是区分,否则如何确定其意义,这还是体现了相关的都方一起的思想,比全局变量或者常量更方便使用和理解。


4、需要说明的是,虚函数对应的虚拟表在空间的其他位置,和对象是没有联系的,但是虚拟表地址是和类统一的,也就是说一旦确定,无论在哪个对象中,其指针 值是一样的,即虚拟表位置是一定的。指针放在对象的最前面,首先是指向虚函数的虚拟表指针,然后才是其他成员变量空间。

5、
我认为浅拷贝是一个不喜欢思考的懒汉,而深拷贝则是一个思维严谨,喜欢思考的人。对于懒汉来说,虽然给了他任务,但是他总是想尽量的少做一些事情,所以很多时候做出来的东西就是只看到了表面,不会去思考对不对。

struct X { int x; int y; }; 

对于懒汉来说,他很直白的看到了x,看到了y,然后就拷贝x和y,然后就不管了,反正我完成我的拷贝了,至于对不对,我不管。

而一旦有了引用或者指针,事情就不一样了
struct X { int x; int y; int* p; }; 
懒汉依然只是直接表面级别的拷贝,于是拷贝x, y , p,但是他没有思考接下来的事情对不对。对于指针或者引用来说,若是只是拷贝表面,那么拷贝后的物体的指针也和原来的指针指向的是同一个对象,所以虽然目的想完成一个完美的克隆体,但是却发现克隆体和原来的物体中间还有一根线连着,没有完美的分离。

int *p = new int(47); int *q = p; 

如q与p都是指向一个物体一样。

那么如果原来的物体销毁了,但是现在拷贝的物体还在,那么这时候你拷贝后的物体的成员指针就是一个悬挂指针,指向了不再存在的物体,那么你访问的话,那就不知道会发生什么了。

而对于深拷贝,这一个勤奋的人,他不会只做表面,他会把每一个细节都照顾好。于是,当他遇到指针的时候,他会知道new出来一块新的内存,然后把原来指针指向的值拿过来,这样才是真正的完成了克隆体和原来的物体的完美分离,如果物体比作人的话,那么原来的人的每一根毛细血管都被完美的拷贝了过来,而绝非只是表面。所以,这样的代价会比浅拷贝耗费的精力更大,付出的努力更多,但是是值得的。当原来的物体销毁后,克隆体也可以活的很好。

然而事实上是这个世界上大多都是懒汉,包括编程的人,编译器等,所以默认的行为都是浅拷贝,于是有时候你需要做一个勤奋的人,让事情做正确,自己去完成深拷贝所需要的事情。

6、
64位操作系统,不同类型变量对应的字节数为:(红色的表示与32位系统不同之处)
        char :1个字节         char*(即指针变量): 8个字节        short int : 2个字节         int:  4个字节         unsigned int : 4个字节         float:  4个字节         double:    8个字节         long:   8个字节        long long:  8个字节         unsigned long:  8个字节 
64位系统在内存管理方面遵循8字节对齐,原则:在8字节对齐的情况下,按8个字节为单位分配存储空间,如果不足,会自动补充,本次分配不足以存放下面的变量时,会重新分配空间。
7、
如果 const 位于 * 的左侧,则 const 就是用来修饰指针所指向的变量,即指针指向为常量; 
如果 const 位于 * 的右侧, const 就是修饰指针本身,即指针本身是常量。

8、
虚函数可以在子类中进行重载,也可以不重载而沿用父类中的方法。但纯虚函数必须重载,因为在其声明类中没有函数实现。vritual void func()=0;
包含纯虚函数的类为抽象类,抽象类不能声明对象,只能作为基类
9、
浮点数判断是否为0

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

10.

32位系统中,定义**a[3][4],则变量占用内存空间为 48bytes

**a[3][4]存储的是指向地址的二维指针数组,32位系统一个地址4个字节,12*4=48byte

11.

一个无向图 G=(V,E) 是连通的,那么边的数目大于等于顶点的数目减一:|E|>=|V|-1,而反之不成立。
如果 G=(V,E) 是有向图,那么它是强连通图的必要条件是边的数目大于等于顶点的数目:|E|>=|V|,而反之不成立。
没有回路的无向图是连通的当且仅当它是树,即等价于:|E|=|V|-1。
12.

在VC6.0中,stack的大小一般为2M,不会自动增加。stack一般存放函数的参数列表、返回值、局部变量。

stack 不存储全局变量的值的值

13、下列哪些http方法对于服务端和用户端一定是安全的?

HEAD,GET,OPTIONS和TRACE视为安全的方法,因为它们只是从服务器获得资源而不对服务器做任何修改,但是HEAD,GET,OPTIONS在用户端不安全。而POST,PUT,DELETE和PATCH则影响服务器上的资源。
TRACE: 这个方法用于返回到达最后服务器的请求的报文,这个方法对服务器和客户端都没有什么危险。OPTIONS:列出请求的资源所支持的所有方法。

14、下面函数的时间复杂度是

long foo(long x){

    if(x<2) return 1;

        return x*x*foo(x-1);

}

将这个递归转换成循环的形式,其实就是一层的循环,所有时间复杂度为O(N)

15、一棵非空的二叉树的先序遍历序列与后序遍历序列正好相反,则该二叉树一定满足?

只有一个叶子结点

16、HTTPS是使用(   )来保证信息安全的.

SSL

17、1.不建议在构造函数中抛出异常;

2.构造函数抛出异常时,析构函数将不会被执行,需要手动的去释放内存

1.析构函数不应该抛出异常;

2.当析构函数中会有一些可能发生异常时,那么就必须要把这种可能发生的异常完全封装在析构函数内部,决不能让它抛出函数之外;

3. 析构函数异常相对要复杂一些,存在一种冲突状态,程序将直接崩溃:异常的被称为“栈展开(stack unwinding)”【备注】的过程中时,从析构函数抛出异常,C++运行时系统会处于无法决断的境遇,因此C++语言担保,当处于这一点时,会调用 terminate()来杀死进程。因此,当处理另一个异常的过程中时,不要从析构函数抛出异常, 抛出异常时,其子对象将被逆序析构

18、

对于满足SQL92标准的SQL语句:
select foo,count(foo)from pokes where foo>10group by foo having count (*)>5 order by foo 
其执行顺序应该是?

FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY


19、在网络7层协议中,如果想使用UDP协议达到TCP协议的效果,可以在哪层做文章?

会话层


20、struct以最长的基本类型对齐。


21、开发C代码时,经常见到如下类型的结构体定义:

typedef struct list_t{
struct list_t *next;
struct list_t *prev;
char data[0];
}list_t;

最后一行char data[0];的作用是?


A方便管理内存缓冲区

B减少内存碎片化

开发C代码时,经常见到如下类型的结构体定义:

typedef struct list_t{
struct list_t *next;
struct list_t *prev;
char data[0];
}list_t;

最后一行char data[0];的作用是?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章