深信服 第一面

开心啊,终于有一次电面了,投腾讯,爱奇艺笔试过了面试未去(第一太远,第二感觉实力不够),美团,蘑菇街未收到通知,上午发的消息, 今天晚上面试,比预期提早了半个小时,还好没啥影响,于是复习了一下午c/c++,真的猜对了好多题,但是第一次面试太紧张了,总是没说中关键点。中途由于机房手机信号不好尴尬的5分钟左右。。。。想吐槽这个机房。。。。

简单个人介绍

首先重c开始问

1.全局变量和局部变量的区别

作用域不同,全局变量存放在静态区,不初始化时默认为0,局部变量存放在栈区,不初始化,不确定其值。

局部同时有全局和局部的相同名字的变量,可通过::来使用全局变量

2.const 和 define 的区别

3.const char*  和 const *char 的区别

const char* p =char const *p 指针的值不可修改

char *const p   指针地址不可修改

const定义的值可以修改吗?

可以。

#include <stdio.h>
#include <iostream>
int main()
{
const volatile int a=99; //volatile强制将a存放到内存,也就是内存的读取。如果不加volatile,输出值仍是99,但是实际上*p的值是可以并且已经改变了的,由于编译器的优化,有a的地方全部被常量99替换了。
int *p=(int*)&a;
*p=100;
printf("%d", a);
return 0;
}
  1. const全局变量存储在全局存储空间,其值只有可读属性,不能修改;
  2. const局部变量存储在堆栈中,可通过指针修改其值;
  3. const变量在预处理时处理,编译器只对其值读取一次。

 

(1)const修饰函数参数
          1.如果输入的参数采用“指针传递”,那么加上const可以防止意外的改动该指针,起到保护作用
        例如:void func(char *dest_str, const char *src_str)
        
        2.如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就不需要保护所以
        不用加const也可以
        例如:void func(int x)不用写成void func(const int x)
       
        3.但是对于非内部的数据类型的参数而言,例如void func( A a)这样声明的函数注定效率低,因为函数体内
        将自动产生临时对象,用于复制该参数a,而临时变量的构造,拷贝构造,析构都将耗费时间。所以为了提高效率
        可以写成这样void func(A &a)用“引用传递”就不用产生临时对象,如果不想改变参数a就写成
        void func(const A &a)
 (2)const修饰函数返回值 
        1.以“指针传递”方式的函数返回值加上const修饰,那么该函数的返回值的内容不能被修改
         例如:const char *get_string(void)
         
         2.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部的临时存储单元中,所以加上const修饰
         没有意义,
         例如:int get_number(void)不用写成const int get_number(void)
        
         3.但是返回值不是内部数据类型,例如:A get_string(void),这样会产生一个临时的对象用来存放返回的数据,
         会调用拷贝构造函数,这样效率会低,所以采用“引用传递”A &get_string(void),如果加上const那么返回值的
         内容就不会被改变const A &get_string(void)
 (3)const修饰成员函数 
         1.任何不会修改成员的函数都应该声明为const类型。
         例如计数函数不会对成员进行修改所以int get_count(void)const;注意const成员函数的声明将const放到
         函数的尾部。
         
         2.const成员函数不可以修改对象的数据。
            const对象只能访问const成员函数,非const对象可以任意访问任意的成员函数。
         
         3.class A{
            public:
                int get_count(int)const;
            }
          int get_count(int)const准确的说这个const修饰的是this指向的对象,其实
a.const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
b.const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.
c.const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.
e.然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的

1、const
const int * :指向const对象的指针
     1、不能通过该指针来修改对象的值,但是可以使指针指向不同的对象;
     2、非const对象的指针不能指向一个const对象;
     3、非const对象的地址可以赋给const对象的指针;
 

int * const:指向非const对象的const指针:不能使该指针指向新的对象,但是可以改变它指向的值;
const int* const

const参数和非const参数,算函数重载;

dynamic_cast:   通常在基类和派生类之间转换时使用,run-time   cast
const_cast:   主要针对const和volatile的转换. 
static_cast:   一般的转换,no   run-time   check.通常,如果你不知道该用哪个,就用这个。   

reinterpret_cast:   用于进行没有任何关联之间的转换,比如一个字符指针转换为一个整形数。

强制类型转换

1)static_cast<T*>(a)
编译器在编译期处理
将地址a转换成类型T,T和a必须是指针、引用、算术类型或枚举类型。
表达式static_cast<T*>(a), a的值转换为模板中指定的类型T。在运行时转换过程中,不进行类型检查来确保转换的安全性。
static_cast它能在内置的数据类型间互相转换,对于类只能在有联系的指针类型间进行转换。可以在继承体系中把指针转换来、转换去,但是不能转换成继承体系外的一种类型
class A { ... };
class B { ... };
class D : public B { ... };
void f(B* pb, D* pd)
{
    D* pd2 = static_cast<D*>(pb);        // 不安全, pb可能只是B的指针
    B* pb2 = static_cast<B*>(pd);        // 安全的
    A* pa2 = static_cast<A*>(pb);        //错误A与B没有继承关系
    ...
}

2)dynamic_cast<T*>(a)
在运行期,会检查这个转换是否可能。
完成类层次结构中的提升。T必须是一个指针、引用或无类型的指针。a必须是决定一个指针或引用的表达式。
dynamic_cast 仅能应用于指针或者引用,不支持内置数据类型
表达式dynamic_cast<T*>(a) 将a值转换为类型为T的对象指针。如果类型T不是a的某个基类型,该操作将返回一个空指针。
它不仅仅像static_cast那样,检查转换前后的两个指针是否属于同一个继承树,它还要检查被指针引用的对象的实际类型,确定转换是否可行。
如果可以,它返回一个新指针,甚至计算出为处理多继承的需要的必要的偏移量。如果这两个指针间不能转换,转换就会失败,此时返回空指针(NULL)。
很明显,为了让dynamic_cast能正常工作,必须让编译器支持运行期类型信息(RTTI)。

3)const_cast<T*>(a)
编译器在编译期处理
去掉类型中的常量,除了const或不稳定的变址数,T和a必须是相同的类型。
表达式const_cast<T*>(a)被用于从一个类中去除以下这些属性:const, volatile, 和 __unaligned。
class A { ... };
void f()
{
    const A *pa = new A;//const对象
    A *pb;//非const对象
    //pb = pa; // 这里将出错,不能将const对象指针赋值给非const对象
    pb = const_cast<A*>(pa); // 现在OK了
    ...
}
对于本身定义时为const的类型,即使你去掉const性,在你操作这片内容时候也要小心,只能r不能w操作,否则还是会出错
const char* p = "123"; 
char* c = const_cast<char*>(p); 
c[0] = 1;   //表面上通过编译去掉了const性,但是操作其地址时系统依然不允许这么做。
const_cast操作不能在不同的种类间转换。相反,它仅仅把一个它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。
尽量不要使用const_cast,如果发现调用自己的函数,竟然使用了const_cast,那就赶紧打住,重新考虑一下设计吧。

4)reinterpret_cast<T*>(a)
编译器在编译期处理
任何指针都可以转换成其它类型的指针,T必须是一个指针、引用、算术类型、指向函数的指针或指向一个类成员的指针。
表达式reinterpret_cast<T*>(a)能够用于诸如char* 到 int*,或者One_class* 到 Unrelated_class*等类似这样的转换,因此可能是不安全的。
class A { ... };
class B { ... };
void f()
{
    A* pa = new A;
    void* pv = reinterpret_cast<A*>(pa);
    // pv 现在指向了一个类型为B的对象,这可能是不安全的
    ...
}
使用reinterpret_cast 的场合不多,仅在非常必要的情形下,其他类型的强制转换不能满足要求时才使用。

 

 

== ===========================================
== static_cast .vs. reinterpret_cast 
== ================================================
reinterpret_cast是为了映射到一个完全不同类型的意思,这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的,这是所有映射中最危险的。(这句话是C++编程思想中的原话) 
static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; 
static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。
另一方面;reinterpret_cast是C++里的强制类型转换符,操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。
例子如下:
int n=9; 
double d=static_cast < double > (n); 
上面的例子中, 我们将一个变量从 int 转换到 double。这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。

而reinterpret_cast 的行为却不同: 
int n=9; 
double d=reinterpret_cast<double & > (n);
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析. 
因此, 你需要谨慎使用 reinterpret_cast.

 

reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。
例如,假设你有一个函数指针数组:
typedefvoid(*FuncPtr)();//FuncPtr is一个指向函数的指针,该函数没有参数,返回值类型为void
FuncPtrfuncPtrArray[10];//funcPtrArray是一个能容纳10个FuncPtrs指针的数组

让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
int doSomething();

你不能不经过类型转换而直接去做,因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。在FuncPtrArray数组里的函数返回值是void类型,而doSomething函数返回值是int类型。

funcPtrArray[0] = &doSomething;//错误!类型不匹配
reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);
转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果

4.static

存放在静态区

作用域仅局限于本文件

static 修饰成员函数,修饰成员变量,全局变量,局部变量的区别。

在C++中静态成员只关联于类,而和类的具体对象不关联,类静态成员独立于任何一个对象而存在。
特别的静态成员的类型可以是它所在的类,而非静态成员,而对于非静态成员则不行,
非静态成员必须被声明为类对象的指针或者是引用。
static成员函数没有this指针
静态成员函数只能访问静态成员变量和静态成员函数,生存期大于class instance 
class Bar
{
public:
	//....
private:
	static Bar mem1; //OK
	Bar *mem2; //OK
	Bar mem3; //ERROR
};

#include <iostream>

using namespace std;

class test2
{
public:
    test2(int num) : y(num){}
    ~test2(){}

    static void testStaticFun()
    {
      cout << "y = " << y << endl; //Error:静态成员函数不能访问非静态成员
    }

    void testFun()
    {
        cout << "x = " << x << endl; 
    }
private:
    static int x;//静态成员变量的引用性说明
    int y;
};

int test2::x = 10;//静态成员变量的定义性说明

int main(void)
{
    test2 t(100);
    t.testFun();

    return 0;
}
#include <iostream>

using namespace std;

class Test
{
public:
    Test() : y(1), r(y), d(3){} //对于常量型成员变量和引用型成员变量,必须通过参数化列表的方式进行初始化。
    ~Test(){}

    int y;      //普通变量成员
    int &r;     //引用成员变量
    const int d;    //常量成员变量
    static int c;   //静态成员变量
    static const int x = 2;   //静态常量整型成员变量
    static const int xx;        //静态常量整型成员变量声明
    static const double z;  //静态常量非整型成员变量声明
    //const static int zz = 6.0;    //静态常量非整型成员变量 error
};
const int Test::xx = 4; //静态常量整型成员变量定义
const double Test::z = 5.1; ////静态常量非整型成员变量定义
int Test::c = 2;

int main(void)
{
    cout << Test::x << endl;    

    return 0;
}

5.switch

case 后面可以跟变量吗?答:可以跟const修饰的常量,不可跟变量

default的用途? 答:当前面所有case不满足

然后问c++

1.extern的作用

可用于c/c++混写。在别处声明的变量或者c文件(外链接),相当于拷贝到当前文件

原因:c与c++的链接方式和命名规则不同

2.虚函数的了解

3.基类的析构函数是否需要加virtual

要。

4.class和struct 的区别

5.printf如何输出多个参数(不清楚参数的情况下)

6.c语言如何调用c++的函数

extern

linux

1.i/o模型有几种

https://www.cnblogs.com/findumars/p/6361627.html

  (1) 阻塞I/O (Blocking I/O)

  (2) 非阻塞I/O (Non-Blocking I/O)

  (3) I/O复用(I/O Multiplexing)

  (4) 信号驱动的I/O (Signal Driven I/O)

  (5) 异步I/O (Asynchrnous I/O)

数据结构

1.排序算法有哪些,复杂度是多少

2.最快的查找

3.list和vector的区别

vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。
因此能高效的进行随机存取,时间复杂度为o(1);
但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。
另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。

list是由双向链表实现的,因此内存空间是不连续的。
只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n);
由于链表的特点,能高效地进行插入和删除

算法

判断一个链表内是否有环(当时有点蒙,这道题去年学长刚从北京回来,因为我替他跑步然后请我吃饭,和我提到过一下,用两个指针,当时不是特别理解,但是没管,于是口胡了一个和当时学长问我时同样的说法,所以面试官没继续问下去了。。。。当时好想说要是换道题我肯定会啊)

问:你会其他的吗,对虚拟化和云计算有了解吗

答:没,现在有在学网络编程

问:tcp和udp的区别

答案链接

问:简单的服务器实现

问:socket参数

问:tcp,udp传递时用的函数

问:有看过unix环境高级编程 

发布了196 篇原创文章 · 获赞 20 · 访问量 8万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览