大道五十,天衍四十九,人遁其一!

# 写在前面

智能涂鸦整体感觉还好,没有想象中的难。涉及到 linux 基础,c 语言,makefile 方向的知识

# Linux 方向

linux 这一块考了 一个 malloc 是系统那部分提供的方法,一个 linux 端口占用查看
这一方面我的知识比较欠缺,端口查看是 netstat -tunlp , malloc 的提供方盲猜了个 glibc , 事实也就是这个。当时是想 malloc 也是个库函数,八成和 GUN 有关系。还真就是 gun libraries c .
PS: glibc 是 linux 下面 c 标准库的实现,即 GNU C Library

# makefile

这个就考了一个问题:

定义一个可以跨文件的 makefile 变量的关键字

很巧,我就会这一个,考其他的。我还真不一定能能记得,因为 makefile 也是大一的时候学的,都忘完了つ﹏⊂
答案是 export

# C 语言

余下的都是 C 语言的题目,毕竟这个才是重头戏.
老生常谈的考了 static 的作用,这个在这篇文中说过我就不再细说
接下去就说一些不一样的

# 字节对齐

#pragma pack(x) 的作用和一字节对齐.
题目大概是这样的:

求 下面结构体的大小

#pragma  pack(1)
struct node{
  char a
  int b;
  double c;
};

结构体大小必然需要考虑到 内存对齐,但是这里比较有意思的就是 有一句 #pragma pack(1) , 这表示结构体按一字节对齐
如果没有这句宏那么,结果就是 32 位机下 1+4+8+(3) 字节,后面 这三个字节是补全 char 类型 4 字节对齐.
但是因为有 就变成的 1 字节对齐,所以结果是 13 字节
PS: #pragma pack () 是解除 1 字节对齐

实际测试代码如下 (偷个懒,用 cpp 输出比较快):

#include <iostream>
using namespace std;
#pragma pack(1)   // 一字节 对齐
struct node
{
    char a;
    int b;
    double c;
};
#pragma pack()  // 解除 1 字节对齐
struct Node
{
    char a;
    int b;
    double c;
};
int main(int, char **)
{
    cout << sizeof(node) << endl;
    cout << sizeof(Node) << endl;
}

# 柔性数组 (0 长数组)

这道题是我唯一不确定的题目,应为用到的太少了 (个人)
题目大致如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Data_t
{
    char value;
    int temp;
    short len;
    char arr[0];
} Flex_Array_t;
int main(void)
{
    int len = 2;
    char *buf = (char *)malloc(sizeof(Flex_Array_t) + len);
    for (int i = 0; i < sizeof(Flex_Array_t) + len; i++)
    {
        buf[i] = i;
    }
    Flex_Array_t *Array = buf;
    printf("array.value = %x\n", Array->value);
    printf("array.temp = %x\n", Array->temp);
    printf("array.len = %x\n", Array->len);
    printf("array.arr[0] = %x\n", Array->arr[0]);
    printf("array.arr[1] = %x\n", Array->arr[1]);
    return 0;
}

这里考了两个知识点,一个是字节对齐,另一个是柔性数组.
在 32 位机的条件下,4 字节对齐,所以大小是 1+4+2 +(3+2)= 12 字节,0 长数组不占空间,
0 长数组,在 ISO 标准里是不支持的,但是 gcc 在 c99 中预先提供了支持。相关文档再此:"Arrays of Length Zero."
我感觉 0 长数组的结构体其实就和 extern int arr[] 极其类似 (不过没有跨文件作用域的效果), 在没有定义变量之前都不会产生存储, sizeof 的结果下他们并不占用空间。对于 Flex_Array_t 也是同一个道理.
其实,严格意义上来说,0 长就类似数组首地址,而数组的首地址仅仅是一个标签,不占用空间,例如

#include <stdio.h>
int arr[10];
int main(int argc, char const *argv[])
{
    arr[9] = 0xff;
    return 0;
}

其汇编代码如下
ASM_Array
arr 存储在 为一个内存标签,和 main 标签作用类似 (.comm 是声明未初始化的内存段空间)
PS: 这也是指针和数组首地址的最大不同

回到题目,这道题目在弄清楚这两个问题和就变简单了, buf 的大小为 14, 那么 buf 被 赋给 Array 的时候,就是从 arr[0] 开始赋值到 arr[13] 为止

那么就可以先得出 Array.arr[0]Array.arr[1] 分别为 0x0a0x0b , 因为结构体的数组不占空间,多出的那 len 必然是给 arr[0] 的.
余下的就很好推断了 Array.value = 0 , 补齐 3 字节, Array.temp = 0x07060504 , int 本为 4 字节不用补齐. Array.len = 0x0908 , short 类型补齐 2 字节.
嗯?!到这里就有一个大问题了既然补齐了 short 两个字节,那么为什么 arr 数值不是未知数呢?前面说过 柔性数组 大小为 0, 我们开辟的空间大小为 结构体大小大再多上 2 字节.
我们使用 malloc 函数开辟出的空间是连续的所以对于 柔性数组来说,不管前面补没补齐,都从上一个数据类型结束初开始计算,算是一个 c99 的特性吧.
具体原因我以后在琢磨琢磨.

# 冒泡排序

冒泡排序的实现难度较低,我之前写过一遍介绍冒泡排序及其优化的文章,感兴趣的可以去看看传送门.

# memmove 实现

这就是面完没两个小时的丙甲,刚问过这就直接撞上了,都是缘分啊。感兴趣可以去看看传送门.


大道五十,天衍四十九,人遁其一!

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

黑羊 支付宝

支付宝