这些 C 语言的题,各位不用电脑纯手写的话,能做到什么程度呢?

146 天前
 AFOX

这些 C 语言的题,各位不用电脑纯手写的话,能做到什么程度呢?

今天下午笔试被挂了,题目如下:

1. 下面的输出是什么,请说明原因(钓鱼执法.....很容易就做错了)

int main() {
     int a[5] = {1, 2, 3, 4, 5};
     int *p = &a + 1;
     printf("%d, %d", *(a+1), *(p - 1));

     return 0;
}


2. 请用一个 C 语言表达式判断某个数是否为 2 的 N 次幂 (没搞懂如何用一个表达式计算,这道题不谷歌真的做不出吧.....)

3. 计算某个数的质因数(例如:12 = 2 * 2 * 3 ) (这里想太多了,空在那做后面的去了)

4. 写出单链表反转的过程(写太多遍了,直接秒)

5. 写一个函数判断回文数(没写出最优解,我转换成字符串用双指针判断了)

6. 删除字符串中的数字并压缩字符串的空间,例如: 输入为 "abc123de45fg" ,输出 "abcdefg" (这里也是双指针做的,不需要开辟新的空间,判断字符串结尾是'\0')

 下面的 7 和 8 选一道做
7. 微积分题,计算 y=x 和 y=x^2 围成的面积(想了一下怎么求积分)
8. 在[0, 2Π]的区间内,计算 y=sinx 与 x 轴围成的面积

9. 矩阵计算,这里具体数字记不清了,随便填一下不影响
A = [[1,2,3], [4,5,6], [7,8,9]]
B = [[3,2,1], [6,5,4], [9,8,7]]

a. 计算 AB 的值 (还记得怎么算)
b. 计算 A 的逆矩阵(不记得相关概念了,没做对)

10. 写出 TCP/IP 的系统调用函数及用法(之前的工作基本不用 tcp/ip ,这里写不出)
11. libudev 的设备通信过程(这个也不了解)
12. 讲一下 linux 的进程间通信(这个就是八股文那几个了)
13. 讲一下 linux 系统从开机到出现登陆界面的详细过程(讲了下 cpu 会从某个地址加载 bootloader ,然后 bootloader 会初始化哪些环境,然后把控制权交给内核了,最后内核从 init 函数开始启动,这里我了解的其实也不是很详细)
14. 还有一道题和前面几道类似的题,记不起来了

15.一道英语翻译题,应该是 I2C 总线手册上的(正常阅读没啥问题)

我看了下我大概写出来了一半的题,被 hr 狠狠的羞辱说题目很基础,认为我大学没认真上 qwq...

然后面的岗位是嵌入式工程师,没有要求必须要相关经验,之前做过三年机器人相关的开发。

我这水平是不是菜到家了?

5716 次点击
所在节点    程序员
52 条回复
AFOX
146 天前
来自 ChatGPT 对第一题的解释:

1. **数组声明和初始化**:
```c
int a[5] = {1, 2, 3, 4, 5};
```
这行代码声明了一个包含 5 个整数的数组 `a`,并将其初始化为 1, 2, 3, 4, 5 。

2. **指针初始化**:
```c
int *p = &a + 1;
```
这一行代码比较复杂。我们逐步解析:

- `&a` 是数组 `a` 的地址。`&a` 的类型是 `int (*)[5]`,即指向一个包含 5 个整数的数组的指针。
- `&a + 1` 表示将指针 `&a` 向前移动一个单位,这里的单位是整个数组 `a` 的大小(即 5 个整数的大小)。因此,它指向的是数组 `a` 之后的内存位置。
- `&a + 1` 的类型仍然是 `int (*)[5]`。
- 将 `&a + 1` 赋值给 `int *p` 时发生了隐式转换。最终 `p` 指向的是紧跟在数组 `a` 之后的内存位置。

总结来说,`p` 指向的是数组 `a` 结束之后的那块内存位置。

为了更好地理解这一点,我们来看一下内存布局:

```
地址 值
0x1000 1 (a[0])
0x1004 2 (a[1])
0x1008 3 (a[2])
0x100C 4 (a[3])
0x1010 5 (a[4])
0x1014 ? (p 指向这里)
```

在这里,`p` 指向地址 `0x1014`,也就是在 `a[4]` 之后的内存位置。

### 访问 `p`
在这种情况下访问 `*p` 是未定义行为,因为它指向的是数组 `a` 结束之后的那块内存位置。

你是否需要更多关于指针运算的信息,或者对这个代码有其他具体的问题?
basncy
146 天前
关于第一题, 联想后面的 I2C 和 TCP, 表面上是指针, 实际是通过 offset 快速定位. 通信协议里经常用 offset 取特定信息 .https://zh.wikipedia.org/zh-cn/IPv4

但是 &a + 1 这种写法, vc++ 能过? 这么多年, linux 下从来没见过这种写法, 请 Linux 开发的面试官们手留情, 别误导青少年了.
https://ibb.co/K7Lr73v
AFOX
146 天前
@basncy
我很同意你的第一点,面试官应该是想考察通过 offset 快速定位的知识点,但是感觉题没出好 hh

第二点的话,我刚刚试了在 gcc 7.5 下还是可以编译通过,但是会报 warning 了。
yxd19
146 天前
@basncy
加个类型转换就行。
https://godbolt.org/z/f6jb1P661

也不用联想那么多。。联想到 C 里对多维数组的支持就理解了。
yxd19
146 天前
第一题关键在于想到 sizeof(a)是啥(是 5*sizeof(int))
basncy
146 天前
@yxd19 #24 (&a + 1);已经越界了, code review 过不了. 加个(int*)哄编译器吗?
ncisoft
146 天前
题目出得不错,公司的研发团队素质挺好的 ^-^
shijingshijing
146 天前
13 应该不会这么简单,要考虑是否启用 secure boot ,还有就是 x86 和 ARM 的启动还是有区别的。
w568w
146 天前
只说第一题哈,严格来说是不应该通过编译(或者说不鼓励写)的。

关键在于 (&a + 1)。指针的加法运算仅对「指向数组元素的指针」有定义,而 a 显然不是一个数组元素。所以 &a+1 的定义是依赖编译器实现的。

在连续的虚拟地址空间下,才能理想地认为 &a+1 代表 a 向后 5 个 sizeof(int) 字节的位置,然后再减 1 得到指向 a[4] 的指针。刻板地说,这题如果不加上这样那样的假设,那就是回答不了的。
majula
146 天前
你这题让我想起来以前面过的一家公司(甚至有可能是同一家)

题目类型也是先考察一下语法,然后几道算法题,几道 高数/线代/数理统计 题,几道 操作系统/计网/图形学 题,最后考察基英语读写能力。考察的方面很广,但都是基础题,并不难(尤其是对于应届生)

不过最后是我把这家公司挂了(有三面邀约但我没去),因为面试官的态度令人不爽,让我对团队的技术氛围很担忧
gpt5
146 天前
小时候打竞赛,老师都是要求手写,再上电脑验证。
sillydaddy
146 天前
前 2 道题有些奇技淫巧的意思:
第 1 题没啥意义,实际写代码基本不会用到。
第 2 题题目太离奇了,最好是改成选择题,给出 7,8 个选项让选,就能知道是否熟悉 bit 运算了,谁没事去记这些奇技淫巧啊。
cwxiaos
146 天前
第二题直接利用二进制数和 2 的幂的关系来做,这题我印象很深,配合 mask 还能做 4 的幂等等
cwxiaos
146 天前
9 题不能乱填数字,$A \prime = \frac{A^*}{|A|}$,这题一样看过去没思路,用定义好像有点难算
newtype0092
146 天前
看到地址加整数就不想看了,出题的估计是 C 语言之父谭大师的门下弟子。。。
smdbh
146 天前
要写完整的话,1 小时我做不完。谁手撸高斯消元啊。
现在觉得面试做卷子太浪费大家的时间了,写的人累,看的人也累。其实面对面聊个 15 分钟,基本就能看出水平了
AFOX
146 天前
@smdbh 他们不给我面聊的机会 qwq ,我线下面试都没见到面试官….
shijingshijing
146 天前
@AFOX 比较好奇这个职位的 JD 是什么,这些题目分布的范围很广,有的还有一定深度,senior 级别的?
geelaw
146 天前
第一个问题,我的理解是编译失败,因为 &a 是指向 int[5] 的指针,于是 &a + 1 也是指向 int[5] 的指针,它指向整个数组之后的 past-the-end 位置,然而 p 是指向 int 的指针,这是两个不兼容类型,当然初始化不能成功。至于具体的编译器接受此代码,只能说是不合标准。

编译失败的原因同

typedef int my_array_t[5]; /* 方便看清楚 */
my_array_t a;
my_array_t *q = &a;
my_array_t *r = q + 1;
int *p = r; /* 当然是无稽之谈 */

第二个问题,如果你说的是 unsigned 类型,那么 x != 0 && (x & (x - 1)) == 0 ,如果是 int 类型还需要判断符号。

后面略,这些问题是惟手熟尔。

@basncy #26 C 和 C++ 里面恰好超过最后一个元素位置的指针是有效的,只是不允许解除引用。考虑 int a[5]={}; int *begin = a, *end = a + 5; while (begin != end) printf("%d\n", *begin++); 当然是 OK 的代码。

@w568w #29 a 确实不是数组元素,但是对于指针运算来说,单个对象等同于具有一个元素的数组对象的惟一元素。也就是说 int b; int *begin = &b, end = &b + 1; 是正确的代码。
hefish
146 天前
我最讨厌用指针骚操作,又不用括号的代码。 可维护性极差, 写的人容易沾沾自喜,不利于团队和谐。 再牛逼也要当害群之马开掉。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1053424

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX