乐趣区

关于c++:题解5道c面试题第一期含解题思路答案解析和实现代码

本篇文章送上 5 道 c /c++ 面试题目,并附上答案、解题思路以及扩大常识。

1. 求上面函数的返回值

#include <stdio.h>

int func(int x)
{
    int iCnt = 0;
    while(x)
    {
        iCnt++;
        x = x&(x-1);
    }
    return iCnt;
}

int main()
{printf("cnt = %d\n", func(9999));
    return 0;
}

这题问的是函数的返回值,而通过代码咱们能看到返回值的多少取决于 x 什么时候变为 0,而 x 的值又取决于 x&(x-1) 这个表达式,在 c ++ 中有一个规定,但凡看到 & 或者 | 这样的符号,那就把它左右两边的值转换为二进制去计算,假如 x 是 7,转换为二进制是 00000111,x- 1 那就是 00000110,那 x&(x-1) 就变成 00000110 了,再减一个 1,变成 00000101,那 x&(x-1) 就是 00000100,所以实际上这个表达式每执行一次,二进制就少一个 1,这样的话,这篇题目就转换成了,输出的数字转换为二进制有多少个 1,那么返回值就是多少。

9999 转换为二进制是 10011100001111,所以本道题目答案:cnt = 8

2. 上面的代码输入是什么?

给一段代码,如下:

#include <stdio.h>

void testputs()
{
    unsigned int a = 6;// 无符号整型
    int b = (-20);// 有符号整型
    (a+b) > 6 ? puts(">6"):puts("<6");
}

int main()
{testputs();
    return 0;
}

初一看,6+(-20)应该是 -14,那就应该输入 <6,然而这么简略的话,就不会有这么一道题了,咱们编译后实际上输入了>6 的后果,这是为什么呢,因为在 c 语言中,无符号和有符号进行运算或者比拟的时候,都会间接把有符号的转换为无符号,而后再进行运算或者比拟。

当初让咱们减少一行代码,看看输入后果,如下:

#include <stdio.h>

void testputs()
{
    unsigned int a = 6;
    int b = (-20);
    (a+b) > 6 ? puts(">6"):puts("<6");
    printf("%u\n", b);//%u 输入无符号整型
}

int main()
{testputs();
    return 0;
}

编译后输入如下后果:

>6
4294967276

也就是说 -20 转换为无符号整型当前变成了 4294967276,这个数字是怎么来的呢,首先这里波及到 int 和 unsigned int 的取值范畴, 如下:

  • int 类型取值范畴:-2^31~2^31-1;
  • unsigned int 类型取值范畴:0~2^32-1;

那有符号转换为无符号是什么样的一个规定呢,有符号的 0 转换为无符号也是 0,而后有符号的 - 1 转换为无符号其实就是 unsigned int 的最大值 2^32-1,也就是 4294967295,那 -20 的话,再减 19 那就是 4294967276,这样就失去了咱们先前输入的后果。

当然下面这是字面上的转换规则,还有一种方法,咱们能够依据内存的存储二进制去进行计算,实质上这个转换只是转换了类型,但并不会去动内存中存储的内容,那正数是怎么存储的呢,分三步:

  • 首先求出相应负数的二进制;
  • 而后按位取反;
  • 加 1;

那么 20 的二进制是 00000000000000000000000000010100,而后按位取反 11111111111111111111111111101011,加 1 当前变成 11111111111111111111111111101100,转换为无符号就是:4294967276。

3. 上面代码一共产生多少个过程?

看上面这段代码:

#include <unistd.h>
#include <sys/types.h>

int main()
{fork();
    fork()&&fork()||fork();
    fork();
    //while(1);
    return 0;
}

这题的要害有两点:

  • 第一个是要分明 fork 函数的作用,fork 函数是克隆出一个子过程,并且父过程返回子过程的过程 ID,而子过程则返回 0,并且在没有判断 fork 返回值的时候,父子过程共享所有的代码;
  • 第二是要晓得符号 &&||的用法,对于&&,如果它右边的表达式值为真,则执行左边的表达式,否则不再执行前面的表达式,而对于||,如果它右边的表达式为真,则左边的表达式不再执行,否则继续执行左边的表达式。

上面咱们用一张图来形容一下过程产生的过程:

上面咱们用文字对图进行讲解,如下:

  • 1 号过程是 main 函数产生的;
  • 调用第一个 fork 函数当前,产生了 2 号过程;
  • 此时曾经存在 1 号过程和 2 号过程,而后他们都调用第二个 fork 函数,那就产生了 3 号过程和 4 号过程,此时对于 fork 函数返回值,1 号过程返回了 3 号过程的 id,2 号过程返回了 4 号过程的 id,而 3 号过程和 4 号过程都返回 0;
  • 依据下面说的,对于&&,只有右边值不为 0,才会持续调用,所以只有 1 号过程和 2 号过程调用了第三个 fork 过程,别离产生了 5 号过程和 6 号过程,此时对于 fork 函数返回值,1 号过程返回 5 号过程 id,2 号过程返回 6 号过程 id,5 号过程和 6 号过程都返回 0;
  • 接下来是符号 ||,它右边为假,才会执行左边的表达式,而|| 的右边是fork()&&fork(),所以只有第二个 fork 函数和第三个 fork 函数的调用有任意一个返回值为 0,它都要执行第四个 fork 函数,而依据下面的第二点和第三点,3、4、5、6 这四个过程都要执行第四个 fork 函数,继而产生了 7、8、9、10 这四个过程;
  • 最初的第五个 fork 函数调用没有条件,所有现有的 10 个过程都要调用一次 fork 函数,最初就变成了 20 个过程。

所以答案是:20,咱们能够把代码外面的 while 循环正文放开,而后查看过程数量,就是 20 个过程。

4. 上面的代码输入什么?

代码如下:

#include <stdio.h>

int main()
{
    char *szName = "shengzhenjiayou";
    printf("%s %5.3s %3.5s %3.4s\n", szName, szName, szName, "aa");
    return 0;
}

先看输入后果:shengzhenjiayou she sheng aa

这就很纳闷了,很多时候咱们只有在输入浮点数的时候格局外面才会带小数点,这里输入字符串带小数点是什么意思呢?

其实这里 %5.3s 这样的格局,小数点后面的示意至多要输入的总宽度(其实就是对齐宽度),小数点前面的示意从右边开始字符串输入的最大宽度,所以 %5.3s 输入了 ’ she’ 这样的数据,它总共输入 5 列,但只取字符串后面 3 列,有余的局部补空格,之所以空格在右边,那是因为默认是右对齐的,那如果是 %-5.3s 这样的,就会变成左对齐。

得出结论如下:对于 %5.3s 这样的格局而言,小数点后面的示意起码要输入这个宽度,小数点前面的示意只能从字符串中截取这个宽度的数据,不够也不会进行补充。

5. 一个空类有多大?

首先看一下上面的代码:

#include <iostream>

class A
{
};

int main()
{printf("sizeof(A)=%d\n", sizeof(A));
    return 0;
}

输入后果如下:sizeof(A)=1

这题个别不理解的人就会很纳闷,咱们个别计算一个类占用多大空间,其实就是计算它的成员变量所占用的空间,而类 A 没有任何成员变量,那为什么长度会为 1 呢。

这是因为 c ++ 标准规定,类实例化对象占用内存的大小不能为 0,为什么这么规定呢。

咱们来看,不论是规范 c ++ 类型还是咱们自定义的类型(这里剔除蕴含纯虚函数的类),它都是能够实例化产生一个变量的,而变量都是要存储在内存中的,如果变量没有大小,是没有存储的,也没有方法取得一个地址,那如果类型 A 实例化了很多对象,没有地址的话,咱们就没有方法辨别各个对象了,所以编译器才会给空类一个字节的空间,这样咱们每一个对象都会领有一个举世无双的地址。

这里延长一下,空类大小是 1,那空构造体呢,基于以上同样的起因,空构造体实际上也是 1。

本篇是 c /c++ 题解第一期,后续会不定期公布更多的题解,如果文章对你有用,麻烦分享和再看哦!

退出移动版