为了账号安全,请及时绑定邮箱和手机立即绑定

闲来无事看两个好玩的C语言面试题

标签:
C 面试

昨天最后一天工作日,让我碰到了两个有意思的C语言编程问题,周末前权当轻松一下,挺有意思就过去了,因为今晚雨夜通宵,就把它们记录了下来。

事件1:关于结构体对齐和packed属性

下午跟同事下楼抽烟,聊到一个有意思的问题,不讲故事了,直接看问题:

#include <stdio.h>#include <stdlib.h>#include <string.h>struct data{        char a;        int b;
};int main()
{        char *mem = calloc(1, 100);        struct data *dt;        memset(mem, 'c', 1);        memset(mem+1, 2, 1);        memset(mem+2, 0, 99);
        dt = (struct data*)mem;        printf("%c  %d\n", dt->a, dt->b);
}

很多人用这种方法给进行数据转换,想当然会认为a就是’c’,b在小端机器上就是2,大端机器上则是0x02000000,嗯,还不错,知道大小端。但是答案还是错了。

其实struct dt的size是8,并不是1+4=5,结构体默认被对齐了,所有在字段a后面会有3个字节的填充,如果直接用mem强转,那么3个填充的0恰好覆盖掉预期中的2,造成结果的错误。上面的代码正确答案应该是:c 0

如果你把结构体dt定义成:

struct data{        char a;        char c;        int b;
}

就知道那个丢失的2去哪里了。

如果希望达到预期的c 2,则只需要如下定义结构体即可:

struct data{        char a;        int b;
}__attribute__((packed));

这种问题在CPU和网络协议以及持久层序列化之间的矛盾处理中非常常见。毕竟不管是网络带宽也好还是存储介质也罢,每一个字节都是钱,能少一个字节甚至一个bit都是在节约,然而对于CPU而言,时间就是钱,空间反而不重要,所以CPU倾向于内存的对齐,于是乎CPU在处理网络协议头的时候非常容易犯上面的错,当前的Linux内核一般都不用结构体强转,都是手工转换,对于序列化程序而言,更是有诸如XML,json等上层库可用,免去了程序员的很多工作。

事件2:关于编译期的sizeof

周五下班早,这是在下班的路上被人怼的:
这里写图片描述
说实话,这个问题我不会,瞬间感觉被鄙视,然而我本来就承认自己不会编程,也就无所谓了。好在我有手机云编译器,就试了一下:
这里写图片描述

然后,我用这个问题调侃了朋友圈的几乎所有同行,当然除了前领导,现领导以及业界的比较牛的人,网上能搜到名字的人我也不敢调侃,这些人如果回答对了,显得我毫无水平且具有挑衅性情,万一回答不出来,我还想混呢…而且我怕被打…其他同等级别同等水平的就无所谓了,反正就是经常互相调侃,我这不就是被调侃的吗?

果然啊,不出所料,有人回答5,有人回答6,当然回答6的偏多。哈哈, 很多人跟我一样不会编程…我也就没什么觉得悲哀的了。

后来通过询问以及自己手机查找资料,知道了答案。

sizeof是一个编译期间预处理就决定结果的操作,编译期肯定不会执行任何语句,i++也好,++i也罢,都不会执行,到了运行期,sizeof的结果已经有了,就更不会去执行i++了…

后来朋友圈的回复不断,有人挖出了C语言标准,有人挖出了汇编码,有人挖出了编译器规程,不过最有意义的还是下面的图了:
这里写图片描述


就上面两个题目,面试的时候去问,相信大批量的人回答不对。但是这两个问题其实也就是知道了也就知道了,并没有什么一锤定音的功能,万万不能用这些问题去评价某个人,它们只是检测一个人对C语言的熟悉程度而已,编程者的事情知道的不多,不多说。


顺便一说,我学过谭浩强的书,但学的不好。

原文出处

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消