十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

宇视科技C/C++软件岗笔试面试:内存对齐、指针与算法考点精讲

宇视科技C/C++软件岗笔试面试:内存对齐、指针与算法考点精讲 简介这是一份宇视科技软件类岗位笔试与面试的真题整理文档面向准备C/C方向校招或社招的求职者覆盖从笔试判断、选择、填空、编程到技术面试的完整链路。文档按题型分层梳理了逗号运算符优先级、结构体字节对齐与内存布局、二维数组与指针关系、自增自减、参数传递方式、递归求值等高频考点并给出字符串逆置、最长回文子串、链表找中间节点、快排排序等面试常考算法题的思路提示同时延伸至socket、进程与线程、IPC通信、图书管理系统与视频监控项目等进阶追问。整包仅1个PDF文件约107KB轻量易存适合手机或电脑随时翻阅。目前已有190人学习下载可帮助读者在有限时间内摸清宇视科技的技术考察范围定位自身知识盲区配合项目复盘做出针对性准备。1. 从一份 C/C 笔试卷看宇视科技软件岗的考察边界一小时的卷子判断题、选择题、填空题、编程题四段连在一起很多人写完选择题就只剩二十分钟。这是宇视科技软件岗笔试的常见节奏也是这份《宇视科技软件笔试面试》资料里最该先记住的一条信息。题目本身不难判断和选择考的是 C 语言常识错起来却成片真正拉开差距的是填空题里的sizeof字节对齐、递归求和、union内存布局以及编程题的两道大题——任意长字符串相加和 IP 地址分类判定。面试部分从字符串逆置、最长回文、链表找中点一路问到图书管理系统的 C/S 架构、多线程和 MySQL 建表。适合准备嵌入式、C/C 后端和音视频方向的应届生也适合工作几年后想回头补内存与指针基本功的人。下面按考点拆开能跑的都给出代码和参数。2. C 语言笔试题里的内存与类型陷阱2.1 逗号运算符优先级与常见误判判断题里那句「逗号运算符是最低运算符」答案是对的。C 运算符优先级从高到低大致是括号、成员访问 → 单目 → 乘除取模 → 加减 → 移位 → 关系 → 相等 → 位与异或或 → 逻辑与或 → 条件运算符 → 赋值 → 逗号。逗号排在最后所以int a (1, 2, 3);的结果是 3而int a 1, 2, 3;会直接编译报错因为逗号在这里被解析成了声明分隔符而不是运算符。实际踩坑最多的是循环和宏// 逗号表达式常用于 for 的多变量更新 for (int i 0, j 10; i j; i, j--) { /* ... */ } // 宏展开时逗号优先级陷阱 #define MAX(a, b) ((a) (b) ? (a) : (b)) // MAX(i, j) 会自增两次因为宏是文本替换不是函数参数说明i, j--整体是一个逗号表达式从左到右求值整个表达式的值等于右边j--的值但循环条件判断并不使用这个值所以这里只看副作用。宏那条更值得警惕笔试里经常拿它考「写出副作用发生次数」。2.2 结构体字节对齐sizeof 为什么和你算的不一样这题几乎是必考。规则只有两条每个成员的偏移量必须是该成员自身对齐数的整数倍结构体总大小必须是所有成员中对齐数最大者的整数倍。#include stdio.h struct A { char a; // 偏移 0占 1 字节 int b; // 对齐数 4偏移补到 4占 4 字节 short c; // 对齐数 2偏移 8占 2 字节 }; // 当前 10 字节补齐到 4 的倍数 - 12 struct B { int b; // 偏移 0 short c; // 偏移 4 char a; // 偏移 6 }; // 当前 7 字节补齐到 4 的倍数 - 8 int main(void) { printf(A%zu B%zu\n, sizeof(struct A), sizeof(struct B)); return 0; // 输出 A12 B8 }同样是char、int、short三个成员顺序一换就差 4 字节。这就是判断题第二句「合理排放结构体数据成员可以节省内存」的由来——不是玄学是对齐填充。成员排列各成员偏移填充字节数最终 sizeofchar, int, short0 / 4 / 83 2 512int, short, char0 / 4 / 60 1 18char, short, int0 / 2 / 418如果结构体要直接落盘或者和硬件寄存器映射就得强制取消填充常见做法是#pragma pack(1)或者编译选项__attribute__((packed))。注意用了 pack 之后访问未对齐成员在某些架构上会触发异常x86 能忍ARM 上不一定。2.3 常量表达式与 i / i 的求值选择题里「a、b 均为正数或均为负数」这种表达式考的是(a 0) (b 0)和a * b 0的差别。后者在溢出时可能翻转符号前者更稳。自增的坑集中在同一个表达式里多次修改同一变量int i 5; int a i i; // 未定义行为不同编译器结果不同别写 printf(%d %d\n, i, i); // 同样未定义参数求值顺序不确定常量表达式部分const int n 5; char buf[n];在 C99 里是变长数组在 C 里必须用constexpr才能算编译期常量。真题里常把sizeof(int)、sizeof(0)、sizeof(a)混在一起考其中sizeof(a)在 C 里等于sizeof(int)4在 C 里等于 1这个差异点过一次就不会忘。3. 指针、数组与参数传递的可复现写法3.1 数组名退化和二维数组与数组指针数组名在大多数表达式里退化成指向首元素的指针但有两个例外sizeof(arr)和arr。二维数组int a[3][4]的a退化后类型是int (*)[4]不是int **这是最高频的混淆点。int a[3][4]; int (*p1)[4] a; // 正确数组指针p11 跳过 4 个 int int *p2 a[0]; // 正确指向第一行首元素 // int **p3 a; // 错误类型不兼容a 不是指针的指针 printf(%d\n, *(*(a 1) 2)); // 等价于 a[1][2] printf(%d\n, p1[1][2]); // 同样等价参数说明p1 1的步长是4 * sizeof(int) 16字节p2 1的步长是 4 字节。函数传参时写void f(int a[][4])和void f(int (*a)[4])完全等价写成void f(int **a)就是错。3.2 普通传参、指针传参、引用传参的输出差异#include stdio.h void by_value(int x) { x 100; } // 改的是栈上副本调用方不可见 void by_pointer(int *x) { *x 100; } // 解引用写回原对象 void by_ref(int x) { x 100; } // C 引用语法像值传递语义是别名 int main(void) { int a 1, b 1, c 1; by_value(a); printf(a%d\n, a); // 1没有变化 by_pointer(b); printf(b%d\n, b); // 100 by_ref(c); printf(c%d\n, c); // 100需按 C 编译 return 0; }传参方式形参形态是否影响实参典型误用值传递int x否想在函数里交换两个数却忘了取地址指针传递int *x是需解引用只改指针本身如x other调用方看不到引用传递int x是忘记加const函数不小心改掉入参指针传递那条误用值得单独说void f(int *p) { p g; }改的是形参指针的值跟外面的实参指针没关系要改得写int **p再*p g。3.3 递归求和那道题的逐层展开原题是int m(char *s, int n) { if (n 1) return s[0] m(s[1], n - 1); // 当前字符 剩余部分的和 else return s[0]; // 只剩一个字符直接返回 }功能就是把前 n 个字符按字节值累加。但原题main里写的是int a[3] {1, 2, 3}; m(a, 3);形参类型是char *实参是int *这在 C 里直接编译不过在 C 里也只是警告。若把形参改成int *s结果是1 2 3 6这是出题人的本意。若强行按char *解释int数组在小端机器上字节序列是01 00 00 00 02 00 00 00 ...前三字节是1、0、0结果是 1。同一道题两种答案区别只在类型有没有对齐。笔试现场如果碰到这种题把两种解释都写上去比硬填一个数字更容易拿分。4. 编程题实战大数相加与 IP 分类4.1 两个任意长字符串相加原题给的函数原型是void add(char a[N], char b[N], char c[N1])长度 80 位明显超过了long long的范围只能按竖式模拟。常见做法是从低位往高位逐位相加用carry保存进位。#include string.h #define N 80 void add(char a[N], char b[N], char c[N 1]) { int i (int)strlen(a) - 1; // 指向 a 的末位个位 int j (int)strlen(b) - 1; // 指向 b 的末位 int k 0, carry 0; char tmp[N 1]; // 临时数组按低位在前的顺序存结果 while (i 0 || j 0 || carry) { int da (i 0) ? a[i--] - 0 : 0; // 短的那个数高位补 0 int db (j 0) ? b[j--] - 0 : 0; int sum da db carry; tmp[k] (char)(0 sum % 10); // 先存个位 carry sum / 10; // 进位留给下一轮 } for (int t 0; t k; t) // 反转成高位在前 c[t] tmp[k - 1 - t]; c[k] \0; // 别忘了字符串结尾 }参数说明a、b是输入的两个十进制字符串c是输出缓冲区必须给到N1因为结果最长可能比输入多一位如 999 1 1000。循环条件里的|| carry是关键最后一位进位如果不靠它兜住就丢了。sum % 10取当前位sum / 10取进位两步不要写反。要处理负数就在函数入口判断首字符是不是-把符号单独拿出来剩下的按大数减法写比较两个绝对值大小决定结果符号。4.2 IP 地址分类判定原题给了枚举和位模式本质是看首字节的高位#include stdio.h typedef enum { IP_A 0, IP_B, IP_C, IP_D, IP_E, IP_NO } w; w classify(const char *s) { int a 0, b 0, c 0, d 0; // 用 sscanf 拆四段返回值不是 4 说明格式不对 if (sscanf(s, %d.%d.%d.%d, a, b, c, d) ! 4) return IP_NO; // 每一段都必须在 0~255 之间否则是非法地址 if (a 0 || a 255 || b 0 || b 255 || c 0 || c 255 || d 0 || d 255) return IP_NO; if (a 1 a 126) return IP_A; // 0xxxxxxx if (a 127) return IP_NO; // 回环地址单独排除 if (a 128 a 191) return IP_B; // 10xxxxxx if (a 192 a 223) return IP_C; // 110xxxxx if (a 224 a 239) return IP_D; // 1110xxxx 组播 if (a 240 a 255) return IP_E; // 11110xxx 保留 return IP_NO; }类型首字节位模式首字节十进制范围常见用途A0xxxxxxx1 ~ 126超大规模网络B10xxxxxx128 ~ 191中等规模网络C110xxxxx192 ~ 223小型网络家用场景最多D1110xxxx224 ~ 239组播地址E11110xxx240 ~ 255保留实验用4.3 边界与异常输入两道编程题真正扣分的都不是主逻辑而是边界。大数相加要覆盖空串、单字符0、长度差极大80 位对 1 位、以及全是 9 的极端进位。IP 判定要覆盖0.0.0.0、127.0.0.1、256.1.1.1这类越界值还有1.1.1段数不足和a.b.c.dsscanf返回 0。一种偷懒但有效的验证方式是把函数放进一个循环里跑用例const char *cases[] {1.1.1.1, 127.0.0.1, 256.1.1.1, 224.0.0.1, 240.1.1.1, abc, NULL}; for (int i 0; cases[i]; i) printf(%-14s - %d\n, cases[i], classify(cases[i]));5. 内存布局与项目追问的答题骨架5.1 union 的w.x到底等于多少union { int x; char y[2]; } w; w.y[0] 10; w.y[1] 1; printf(%d\n, w.x);union所有成员共享同一块起始地址长度取最大成员。小端机器上y[0]落在x的最低字节y[1]落在次低字节所以x 1 * 256 10 266大端机器反过来是10 * 256 1 2561。但面试官想听的往往不止这个数y只有 2 字节x是 4 字节剩下 2 字节没被初始化就整体读x属于未定义行为实际输出依赖栈上残留。能主动补上这句比答对数字加分更多。5.2 快慢指针找链表中间节点typedef struct Node { int val; struct Node *next; } Node; Node *middle(Node *head) { Node *slow head, *fast head; // fast 为空 或 fast-next 为空就停两步判断缺一不可 while (fast fast-next) { slow slow-next; // 每次走一步 fast fast-next-next; // 每次走两步 } return slow; }节点数为奇数时slow正好落在中点偶数时落在中间偏左那个想要偏右就把fast初始指向head-next。链表为空或只有一个节点时循环不执行直接返回head不需要额外分支。5.3 图书管理系统与 C/S 架构追问面试后半段的两道大题问法基本固定先说模块划分用户、图书、借阅三条线再说数据结构链表或 B 树索引最后说持久化。C/S 版本紧接着会追多线程、数据库连接和 socket。数据库部分常被问到连接方式用 MySQL 的 C API 走一遍就够回答mysql_init初始化句柄mysql_real_connect建立连接mysql_query执行 SQLmysql_store_result取结果集最后mysql_free_result和mysql_close释放。建表语句要能随手写出来借阅表用user_id和book_id做联合索引避免全表扫描。socket 与多线程的答法一个accept主线程负责接入收到连接后pthread_create派发处理线程共享数据加互斥锁。进程和线程的区别落在地址空间和切换开销上IPC 种类提管道、消息队列、共享内存、信号量、socket 五类共享内存要配信号量做同步。项目追问时把「遇到什么问题、怎么定位、怎么改」讲成一条完整链路——比如视频监控里缓冲区溢出导致丢帧用valgrind定位到越界写把定长数组换成带长度校验的环形缓冲——比罗列技术栈有效得多。本文还有配套的精品资源点击获取
返回列表