题解5道c++面试题第一期(含解题思路、答案解析和实现代码)

cpp加油站
• 阅读 1613

本篇文章送上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返回值的时候,父子进程共享所有的代码;
  • 第二是要知道符号&&||的用法,对于&&,如果它左边的表达式值为真,则执行右边的表达式,否则不再执行后面的表达式,而对于||,如果它左边的表达式为真,则右边的表达式不再执行,否则继续执行右边的表达式。

下面我们用一张图来描述一下进程产生的过程:

题解5道c++面试题第一期(含解题思路、答案解析和实现代码)

下面我们用文字对图进行解说,如下:

  • 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++题解第一期,后续会不定期发布更多的题解,如果文章对你有用,麻烦分享和再看哦!

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这