腾讯2017秋招笔试题难点记录

1.在vs编译环境下,以下代码的运行情况:

1
2
3
4
5
6
7
int f( int a, int b, int c)
{
     return 0;
}
int main(){
     return  f( printf ( "a" ), printf ( "b" ), printf ( "c" ));
}
解析:
该题考察函数的参数传递:
当用函数做实参时,编译器一般会根据参数传递顺序,先计算出函数的返回值,然后将返回值传递给原来的函数。
在x86架构(Linux或Unix系统)上,函数的参数是通过栈传递的。因此参数从右往左入栈顺序是:printf("c"),printf("b"),printf("a")。依次计算出结果:cba
在x86_64架构(Linux或Unix系统)上,函数的前6个参数是通过寄存器传递的,超出6个的部分(如第7个参数,第8个参数等等)通过栈传递。因此参数进寄存器顺序是:printf("a"),printf("b"),printf("c")。依次计算出结果:abc

2.具有3个节点的二叉树有几种形态?

腾讯2017秋招笔试题难点记录_第1张图片

3.在Linux上,对于多进程,子进程继承了父进程的下列哪些?

解析:

  • 子进程继承父进程
    • 用户号UIDs和用户组号GIDs
    • 环境Environment
    • 堆栈
    • 共享内存
    • 打开文件的描述符
    • 执行时关闭(Close-on-exec)标志
    • 信号(Signal)控制设定
    • 进程组号
    • 当前工作目录
    • 根目录
    • 文件方式创建屏蔽字
    • 资源限制
    • 控制终端
  • 子进程独有

    • 进程号PID
    • 不同的父进程号
    • 自己的文件描述符和目录流的拷贝
    • 子进程不继承父进程的进程正文(text),数据和其他锁定内存(memory locks)
    • 不继承异步输入和输出
  • 父进程和子进程拥有独立的地址空间和PID参数。子进程从父进程继承了用户号和用户组号,用户信息,目录信息,环境(表),打开的文件描述符,堆栈,(共享)内存等。经过fork()以后,父进程和子进程拥有相同内容的代码段、数据段和用户堆栈,就像父进程把自己克隆了一遍。事实上,父进程只复制了自己的PCB块。而代码段,数据段和用户堆栈内存空间并没有复制一份,而是与子进程共享。只有当子进程在运行中出现写操作时,才会产生中断,并为子进程分配内存空间。由于父进程的PCB和子进程的一样,所以在PCB中断中所记录的父进程占有的资源,也是与子进程共享使用的。这里的“共享”一词意味着“竞争“。

  • 4.设有一个递归算法如下
  • 1
    2
    3
    4
    int f( int n) {
         if (n<=3) return 1;
         else return f(n-2)+f(n-6)+1;
    }

    试问计算f(f(9))时需要计算()次f函数。

    解析:
    一、先算内层f(9)
        [1] 计算 f(9) = f(7) + f(3) + 1;
        [2] 计算[1]中 f(7) = f(5) + f(1) + 1;
        [3] 计算[2]中 f(5) = f(3) + f(-1) + 1;
        [4] 计算[3]中 f(3) = 1;
        [5] 计算[3]中 f(-1) = 1;
            {至此f(5)可计算得: f(5) = 1 + 1 + 1 = 3}
        [6] 计算(1)中f(1) = 1;
            {至此f(7)可计算得 :f(7) = 3 + 1 + 1 = 5}
        [7] 计算[1]中f(3) = 1;
            {至此f(9)可计算得:f(9) = 5 + 1 + 1 = 7}
    计算f(9)一共调用了7次函数

    二、计算外层f(7)
        由上面步骤可知,计算f(7)调用了5次函数

    所以一共调用了函数7+5=12次
    5.寝室有6个同学打dota,分为对立的两方,一方是天灾军团,一方是近卫军团。现请你设置赛程以及每场的对阵(每方最少1人、最多5人),请问至少得进行多少场比赛,才能使得赛程结束后每位同学都和其他同学做过对手()
  • 解析:用三位二进制来表示某个人三场比赛各场比赛所在的一方,比如我们用0代表在天灾,1代表在近卫,那么000就代表这个人三场比赛都在天灾,而001表示这个人前两场比赛在天灾,第三场比赛在近卫。那么三位二进制可以有8种表示,而每一种表示都与其他7种的表示至少在一个位置上的数字是不一样的,所以最多8人至少三场可以每个人都做过对手。
  • 6.

    以下代码打印的结果是(假设运行在 64 位计算机上):

    1
    2
    3
    4
    5
    6
    7
    8
    struct st_t {
         int status;
         short *pdata;
         char errstr[32];
    };
    st_t st[16];
    char *p=( char *)(st[2].esstr+32);
    printf (“%d”,(p-( char *)(st)));
    解析:
    根据字节对齐,在64位系统下struct st_t 结构体占用的字节为48个。
    struct st_t {
    int status;  //占用8个(后面的4个为对齐位)
    short *pdata;//占用8个
    char errstr[32];//占用32个
    };
    char *p=(char *)(st[2].esstr+32),p实际指向了st[3]
    p-(char *)(st)),即为&st[3]-&st[0],占用空间为3个结构体的大小,即3*48=144
  • 7.
    请选择下列程序的输出结果是()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include
    int main()
    {
         const int N=10;
         const int M=2;
         int * a= new int [N];
         for ( int i=0;i
             a[i]=(0==i%2)?(i+2):(i+0);
         int (*b)[N/M]=( int (*)[N/M])a;
         for ( int i=0;i
             for ( int j=0;j
                 printf (“%d”,b[i][j]);
         return 0;
    }
    解析:int a[] = {2,1,4,3,6,5,8,7,10,9};  //一维数组a
              int (*b)[N/M]=(int (*)[N/M])a;    //将一维数组a强制转化为数组指针并赋值给数组指针b;
      //上面两句可以拆分为以下几句理解;
             int a[2][N/M] = {2,1,4,3,6,5,8,7,10,9};
             int (*b)[N/M];   //b是数组指针,指向具有N/M个元素的一维数组;
            b = a;
    8.
  • 关于c++中的虚函数和析构函数还有构造函数的关系?
  • C++中 的虚函数的作用主要是实现了多态的机制。而虚函数是通过虚函数表(V-Table)实现的。
    构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。
    构造函数为什么不能声明为虚函数?
    1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。 
    2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。 析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。

    析构函数为什么声明为虚函数?
    如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。

    包含至少一个纯虚函数的类视为抽象类
  • 9. 值类型与引用类型区别:
  • 值类型

    引用类型

    存储方式

    直接存储数据本身

    存储的是数据的引用,数据存储在数据堆中

    内存分配

    分配在栈中的

    分配在堆中

    效率

    效率高,不需要地址转换

    效率较低,需要进行地址转换

    内存回收

    使用完后立即回收

    使用完后不立即回收,而是交给GC处理回收

    赋值操作

    创建一个新对象

    创建一个引用

    类型扩展

    不易扩展,所有值类型都是密封(seal)的,所以无法派生出新的值类型

    具有多态的特性方便扩展

    实例分配

    通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中

    总是在进程堆中分配(动态分配)


    10.
  • 下面代码的执行结果是()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main( void )
    {
         char *p[]={“TENCENT”,”CAMPUS”,”RECRUITING”};
         char **pp[]={p+2,p+1,p};
         char ***ppp=pp;
         printf (“%s”,**++ppp);
         printf (“%s”,*++*++ppp);
         return 0;
    }
    解析:

    腾讯2017秋招笔试题难点记录_第2张图片
  • 从题干当中,我们可以画出这样的一个图,这样就比较直观的看出了p,pp,ppp都指向哪里了,关键是最后两个printf语句。
    (1)printf(“%s”,**++ppp);即,ppp当前所指向的位置,再往下移一个位置,即pp的位置2,而pp的位置2指向的是p的位置2,p的位置2指向的是CAMPUS,所以先输出CAMPUS (2)printf(“%s”,*++*++ppp);这个语句等价于 printf(“%s”,*++(*++ppp));所以我们首先看,++ppp,第一个printf语句中ppp已经指向了pp的位置2,所以再往下移一个,指向了pp的位置3,而(*++ppp)则代表pp位置3所指向的内容,即p的位置1(pp的位置3指向的是p的位置1),在此基础上前面再加上一个++,则代表指针p在位置1的基础上再往下移动,即指针p的位置2,而p的位置2所指向的内容是CAMPUS,所以第二行输出的也是CAMPUS。
    所以正确答案是:CAMPUS CAMPUS






你可能感兴趣的:(牛客网刷题记录)