#该笔试题只记录常见且典型的编程题,不记录算法题#

一、编写一个程序,判断当前是大端字节序还是小端字节序

大端字节序:高位字节存储在低地址,低位字节存储在高地址。

小端字节序:低位字节存储在低地址,高位字节存储在高地址。

方法一:使用指针判断

unsigned int num = 0x12345678;
char *p = (char *)#
if (*p == 0x78) {
    //小端字节序
}
else {
    //大端字节序
}

方法二:使用联合体判断(部分笔试题要求不使用指针

union {
    int num;
    char c;
}endian;

endian.num = 0x12345678;
if (endian.c == 0x78) {
    //小端
}
else {
    //大端
}

二、编写strcpy函数(字符串拷贝函数)的实现

        函数原型: char *strcpy(char *strDest, const char *strSrc);

        函数功能:将strSrc中的字符串拷贝到strDest

char *strcpy(char *strDest, const char *strSrc); 
{ 
     assert((strDest != NULL) && (strSrc != NULL)); //判断传参的正确性 
     char *address = strDest;                       //保存目标字符串首地址用于函数返回
     while((*strDest++ = *strSrc++) != '\0');       // 拷贝字符串  
     return address;                                //返回目标串首地址,目的是实现链式表达式
}

注意:部分实现中不会考虑地址strDest与strSrc地址重叠问题,此处也未考虑此问题。解决这个问题可以参考第四题的memcpy        

三、编写atoi函数(字符串转整型函数)的实现

        函数原型:int atoi(char *str);

int atoi(char *str)
{
    assert(str != NULL); //判断输入合法
    int result = 0;      //计算结果
    int sign = 1;        //正负数标记,正数为1,负数为-1
    int i = 0;           //遍历下标
    if (str[0] == '-') {
        sign = -1;
        i = 1;
    }
    for (; str[i] != '\0'; ++i) {
        assert(str[i] >= '0' && str[i] <= '9');   //中间存在非数字字符则报错
        assert(result < INT_MAX / 10);            //INT_MAX为int最大值,此处判断越界
        result = result * 10 + (str[i] - '0');
    }
    return result * sign;
}

进阶版A把一个字符串转化为一个逆序的整型数值

例如字符串:“123456”,转化成数字:654321。注意考虑边界情况,不考虑负数。

int reverse_atoi(char *str)
{
    assert(str != NULL); //判断输入合法
    int result = 0;      //计算结果
    int i = strlen(str) - 1;           //遍历下标,从后往前遍历

    for (; i>= 0; --i) {
        assert(str[i] >= '0' && str[i] <= '9');   //中间存在非数字字符则报错
        assert(result > INT_MAX / 10);            //INT_MAX为int最大值,此处判断越界
        result = result * 10 + (str[i] - '0');
    }
    return result;
}

和atoi版本区别在于此处字符串从后往前遍历即可。

进阶版B把一个16进制的字符串转换为整型数值

例如字符串:“12ab”,转化为数字:4779。16进制数只用小写字母表示

int hex_atoi(char *str)
{
    assert(str != NULL); //判断输入合法
    int result = 0;      //计算结果

    for (int i = 0; str[i] != '\0'; ++i) {
        assert(result > INT_MAX / 16);            //INT_MAX为int最大值,此处判断越界
        if (str[i] >= 'a' && str[i] <= 'f') {
            result = result * 16 + (str[i] - 'a' + 10);
        }
        else if (str[i] >= '0' && str[i] <= '9') {
            result = result * 16 + (str[i] - '0');
        }
        else {
            assert(0);    //输入不合法导致退出
            return 0;
        }
    }
    return result;
}

注:以上代码不喜欢断言也可以用 if 判断报错后返回错误码

四、编写memcpy函数的底层实现

void *memcpy(void *dest, const void *src, size_t n) 
{
    if (dest == NULL || src == NULL || dest == src) {
        return dest;
    }
    char *d = (char *)dest;
    const char *s = (const char *)src;

    // 判断内存重叠情况,如果重叠且重叠方向是原串结尾与目标开头重叠,则从后往前复制
    if (d > s && d < s + n) {
        d += n;
        s += n;
        while (n--) {
            *--d = *--s;
        }
    } 
    else {
        while (n--) {
            *d++ = *s++;
        }
    }

    return dest;
}

五、编写strstr函数(查找字符串子串函数)

函数原型:char *strstr(const char *str1, const char *str2);

函数功能:判断str2是否是str1的字串,如果是,函数返回str2在str1中首次出现的地址,否则返回NULL

char* strstr(const char* str1, const char* str2) 
{
    if (*str2 == '\0') {
        return (char*)str1;
    }

    const char* p1 = str1;
    while (*p1) {
        const char* p1_start = p1;
        const char* p2 = str2;

        while (*p1 && *p2 && *p1 == *p2) {
            p1++;
            p2++;
        }

        if (*p2 == '\0') {
            return (char*)p1_start;
        }

        p1 = p1_start + 1;
    }

    return NULL;
}

可以拆成两个功能去写,一个函数为strncmp判断两个字符串是否相等,然后再循环遍历str1,依次判断str2是否与之相等。

六、结构体与联合体大小计算

结构体对齐原则

1、变量所处内存位置起始地址必须为自身类型大小的倍数比如char类型,占1个字节,可以存放在任何地址上,short类型占2个字节,必须存放在2的倍数地址上,比如0,2,4,6的位置,int类型占4个字节,必须存放在4的倍数地址上

2、结构体总大小必须为其中元素的最大基础元素的倍数,比如结构体中只包含char,int,short,那么结构体总大小一定是4的倍数。

见以下示例

struct A
{
    char str;
    short s;
    int num;
};
//sizeof(struct A) = 8

struct B
{
    char str;
    int num;
    short s;
};
//sizeof(struct B) = 12

union C
{
    int num;
    char str;
};
//sizeof(union C) = 4

typedef union
{
    long i;
    int k[5];
    char c;
}DATE;

struct data
{
    int cat;
    DATE cow;
    double dog;
};
//sizeof(DATE) = 20
//sizeof(struct data) = 32

以下为struct A的内存样式(每格1字节,空格为补齐)

str s s num num num num

以下为struct B的内存样式(每格1字节,空格为补齐)

str num num num num s s

七、给绝对地址的内存赋值

        此题常见于嵌入式相关岗位笔试或硬件单片机岗位笔试

        设置一个绝对地址为0x6588的整形变量值为0xaabb

#define  REG	(*(volatile unsigned int *)0x6588)

REG &= ~(0xffffffff);  //先清零
REG |= 0xaabb;       //再赋值

结语

编写该文章目的主要为想从事相关工作的同学找到一份好的工作,以上题目在笔试中经常出现,如果有在外面试的朋友发现有更常见更经典的题目也可以私信告知,后续也会更新到博客当中。

如果有朋友想系统的学习嵌入式相关知识从事相关的行业,可以私信我,有一些经典的电子档书籍资料和开源网课学习链接

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐