嵌入式C语言常见考察点(2)

1、用数组实现一个循环队列,支持入队、出队、判空、判满。例如数组大小固定为8。

cpp 复制代码
#include <stdio.h>

#define QUEUE_MAX_SIZE 8

typedef struct
{
    int buf[QUEUE_MAX_SIZE];
    int head;  // 队头索引
    int tail;  // 队尾索引
    int count; // 队列元素实际数量
} LoopQueue_t;

/**
 * @description: 循环队列初始化
 * @param {LoopQueue_t} *q
 * @return {*}
 */
void loop_queue_init(LoopQueue_t *q)
{
    if (q == NULL)
    {
        return;
    }
    q->head = q->tail = 0;
    q->count = 0;
}

/**
 * @description: 队列是否为空
 * @param {LoopQueue_t} *q
 * @return {*}
 */
int loop_queue_is_empty(LoopQueue_t *q)
{
    if (q == NULL)
    {
        return -1;
    }
    return (q->count == 0) ? 1 : 0;
}

/**
 * @description: 队列是否为满
 * @param {LoopQueue_t} *q
 * @return {*}
 */
int loop_queue_is_full(LoopQueue_t *q)
{
    if (q == NULL)
    {
        return -1;
    }
    return (q->count == QUEUE_MAX_SIZE) ? 1 : 0;
}

/**
 * @description: 向队列中添加一个元素
 * @param {LoopQueue_t} *q
 * @param {int} val
 * @return {*}
 */
void loop_queue_push(LoopQueue_t *q, int val)
{
    if ((q == NULL) || loop_queue_is_full(q))
    {
        return;
    }

    q->buf[q->tail] = val;
    q->tail = (q->tail + 1) % QUEUE_MAX_SIZE;
    q->count++;
}

/**
 * @description: 向队列中取出一个元素
 * @param {LoopQueue_t} *q
 * @param {int} *val
 * @return {*}
 */
void loop_queue_pop(LoopQueue_t *q, int *val)
{
    if ((q == NULL) || loop_queue_is_empty(q))
    {
        return;
    }

    *val = q->buf[q->head];
    q->head = (q->head + 1) % QUEUE_MAX_SIZE;
    q->count--;
}

2、C语言实现冒泡排序,并优化(如果一轮没有交换则提前结束)

cpp 复制代码
#include <stdio.h>

/**
 * @description: 冒泡排序法 升序
 * @param {int} arr 排序数组
 * @param {int} len 数组长度
 * @return {*}
 */
void bubble_sort(int arr[], int len)
{
    for (int i = 0; i < len - 1; i++)
    {
        int flag = 0;
        for (int j = 0; j < len - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;
            }
        }
        if (flag == 0) // 一轮没有发送交换=>有序 直接退出
            break;
    }
}

int main()
{
    int arr[10] = {2, 5, 3, 8, 4, 1, 0, 7, 9, 6};
    int len = 10;

    printf("raw: ");
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");

    bubble_sort(arr, len);

    printf("sort: ");
    for (int i = 0; i < len; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");

    return 0;
}

3、使用二分查找法,在已排序数组中查找目标值,返回索引,不存在返回 -1

cpp 复制代码
#include <stdio.h>

/**
 * @description: 冒泡排序法 升序
 * @param {int} arr 排序数组
 * @param {int} len 数组长度
 * @return {*}
 */
void bubble_sort(int arr[], int len)
{
    for (int i = 0; i < len - 1; i++)
    {
        int flag = 0;
        for (int j = 0; j < len - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;
            }
        }
        if (flag == 0) // 一轮没有发送交换=>有序 直接退出
            break;
    }
}

/**
 * @description: 对升序排序的数组进行二分查找
 * @param {int} arr
 * @param {int} len
 * @param {int} tar
 * @return {int} 目标元素下标
 */
int binary_search(int arr[], int len, int tar)
{
    int left = 0, right = len - 1;

    while (left <= right)
    {
        int mid = left + (right - left) / 2; // 防溢出
        if (arr[mid] == tar)
        {
            return mid;
        }
        else if (arr[mid] < tar)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    return -1;
}

int main()
{
    int arr[] = {34, 12, 5, 89, 21, 7};
    int len = sizeof(arr) / sizeof(arr[0]);
    int target = 21;

    // 先排序
    bubble_sort(arr, len);

    printf("排序后数组: ");
    for (int i = 0; i < len; i++)
        printf("%d ", arr[i]);
    printf("\n");

    // 再查找
    int idx = binary_search(arr, len, target);
    if (idx != -1)
        printf("找到 %d,索引为 %d\n", target, idx);
    else
        printf("未找到 %d\n", target);
    return 0;
}

4、C语言实现:数组中只有一个数字出现一次,其他都出现两次,找出这个只出现一次的数字

cpp 复制代码
/*
 假设数组为 [4, 1, 2, 1, 2]
 result = 4 ^ 1 ^ 2 ^ 1 ^ 2
 由交换律:result = 4 ^ (1 ^ 1) ^ (2 ^ 2)
 result = 4 ^ 0 ^ 0 = 4
 成对的数字相互抵消,只剩下单独的数字
*/
int find_single(int arr[], int len)
{
    int res = 0;
    for (int i = 0; i < len; i++)
    {
        res ^= arr[i];
    }
    return res;
}

5、分析下面代码在嵌入式开发中可能存在的问题。

uint32_t *status_reg = (uint32_t *)0x40021000;

while (*status_reg & 0x01)

{

// 等待硬件就绪

}

编译器可能认为status_reg不会被修改,所以只会读取status_reg一次,然后将这段while循环代码优化成if(*status_reg & 0x01){while(1){}},导致程序卡死。

修正办法:使用volatile关键字,避免编译器优化。volatile uint32_t *status_reg = (voltaile uint32_t *)0x40021000;

  1. 问题分析:
  • status_reg 指向一个硬件寄存器地址(0x40021000)

  • 该寄存器的值由硬件改变,不是由软件改变

  • 但编译器不知道这一点,它看到循环内没有修改 *status_reg 的代码

  • 编译器优化:"这个值不会变,我把它缓存到寄存器里,只读一次"

  • 结果:程序可能永远读的是旧值,陷入死循环

  1. volatile 的作用:
  • 告诉编译器:这个变量可能会在程序控制之外被改变

  • 禁止编译器对该变量进行优化:

  • 不能缓存到寄存器

  • 不能删除"无用"的读写操作

  • 每次使用都必须从内存重新读取

  1. 嵌入式中必须使用 volatile 的场景:
  • 硬件寄存器映射:外设寄存器值由硬件改变

  • 中断修改的变量:主循环中检查的标志位可能被 ISR 异步修改

  • 多任务共享变量:RTOS 中任务间共享的状态标志
    6、写一段代码判断系统是大端还是小端

cpp 复制代码
#include <stdio.h>

/* 
    小端存储:低字节存储低地址,高字节存储高地址
    大端存储:低字节存储高地址,高字节存储低地址
*/

/**
 * @description: 判断系统是否是小端存储-简化版
 *  判断原理:
        定义一个 int 变量赋值为 1(内存中二进制 0x00000001)
        用 char 读取第一个字节:
        如果是 1 → 低地址存的是低字节 → 小端
        如果是 0 → 低地址存的是高字节 → 大端
    使用联合体的原因:
        union 的所有成员共享同一块内存
        u.a 和 u.b 从同一地址开始
        通过 u.b 可以读取 u.a 的低地址字节
 * @return {*}
 */
int is_little_endian(void)
{
    union
    {
        int a;
        char b;
    } u;
    u.a = 1;
    return (u.b == 1) ? 1 : 0; // 小端:低地址存1
}

int main()
{
    printf("is little endian : %d\n", is_little_endian());
    return 0;
}

7、

struct S

{

char a;

int b;

char c;

};

对于32位系统,sizeof(struct S)是多少?如何优化?

cpp 复制代码
/*
内存对齐规则(32位):
- char:对齐要求 1 字节,任意地址
- int:对齐要求 4 字节,地址必须是 4 的倍数
- 结构体总大小必须是最大对齐数(这里是 4)的倍数
*/

/* 对于原始结构体布局:
Offset 0:  char a      (1字节)
Offset 1:  填充 3字节   (int 需要4字节对齐)
Offset 4:  int b       (4字节)
Offset 8:  char c      (1字节)
Offset 9:  填充 3字节   (总大小需为4的倍数)
总计:12 字节

优化原则:
- 将相同大小的成员放在一起
- 按大小降序或升序排列
*/

struct S_optimized
{
    char a; // Offset 0
    char c; // Offset 1
    // 填充 2字节
    int b; // Offset 4
}; // 总计:8 字节

8、自己实现一个atoi函数:将字符串转为整数,要求可处理前导空格、正负号、溢出。

cpp 复制代码
#include <limits.h>
#include <stdio.h>

int my_atoi(const char *str)
{
    if (str == NULL)
        return 0;

    /*1. 处理前导空格*/
    while (*str == ' ')
    {
        str++;
    }

    /*2,.处理符号*/
    int sign = 1; // 默认为正
    if (*str == '-')
    {
        str++;
        sign = -1;
    }
    else if (*str == '+')
    {
        str++;
    }

    /*3.提取数值*/
    long long res = 0; // 使用long long类型,避免溢出
    while (*str >= '0' && *str <= '9')
    {
        res = res * 10 + (*str - '0');

        // 溢出检查
        if (res * sign > INT_MAX)
            return INT_MAX;
        if (res * sign < INT_MIN)
            return INT_MIN;

        str++;
    }

    return (int)(res * sign);
}

int main()
{
    char *str = "   123   12";
    char *str1 = "  @123   12";
    char *str2 = "  12@3";
    char *str3 = "";

    printf("%d,%d,%d,%d\n", my_atoi(str), my_atoi(str1), my_atoi(str2), my_atoi(str3));

    return 0;
}

9、实现itoa函数:将整数转为字符串,支持负数。

cpp 复制代码
#include <limits.h>
#include <stdio.h>


char *my_itoa(int num, char *str)
{
    if (str == NULL)
    {
        return NULL;
    }
    if (num > INT_MAX)
        num = INT_MAX;
    if (num < INT_MIN)
        num = INT_MIN;

    /*符号判断*/
    int sign = 1;
    if (num < 0)
    {
        sign = -1;
        num = -num;
    }

    /*逐位提取*/
    int idx = 0;
    while (num) // 个位-十位-百位-...-符号位
    {
        str[idx++] = num % 10 + '0';
        num /= 10;
    }
    if (sign == -1)
    {
        str[idx++] = '-';
    }
    str[idx] = '\0';

    /*字符串逆序*/
    for (int i = 0; i < idx / 2; i++)
    {
        char tmp;
        tmp = str[i];
        str[i] = str[idx - 1 - i];
        str[idx - 1 - i] = tmp;
    }
    return str;
}

int main()
{
    int num = -1234;
    int num1 = 123456;

    char str[64];
    char str1[64];

    my_itoa(num, str);
    my_itoa(num1, str1);

    printf("str = %s, str1 = %s", str, str1);

    return 0;
}

10、C语言实现:查找整型数组中第 K 大的元素。

cpp 复制代码
#include <stdio.h>

/*
C语言实现:查找整型数组中第 K 大的元素
两个方法:选择排序实现 或 快速选择(基于快排思想)
*/

#define SELECT_SORT_METHOD /*QUICK_SELECT_METHOD  SELECT_SORT_METHOD*/
 


int partition(int arr[], int left, int right)
{
    int pivot = arr[right]; // 选最后一个作为基准
    int i = left;
    for (int j = left; j < right; j++)
    {
        if (arr[j] >= pivot)
        { // 降序排列
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
            i++;
        }
    }
    int tmp = arr[i];
    arr[i] = arr[right];
    arr[right] = tmp;
    return i;
}

int quick_select(int arr[], int left, int right, int k)
{
    if (left == right)
        return arr[left];

    int pivot_idx = partition(arr, left, right);

    if (pivot_idx == k - 1)
    {
        return arr[pivot_idx];
    }
    else if (pivot_idx > k - 1)
    {
        return quick_select(arr, left, pivot_idx - 1, k);
    }
    else
    {
        return quick_select(arr, pivot_idx + 1, right, k);
    }
}

// 由于数组进入函数后退化成指针,因此必须带着长度
int find_kth_largest(int arr[], int len, int k)
{
#ifdef SELECT_SORT_METHOD /* 1. 选择排序实现(相对简单易懂) */
/*
- 执行 k 轮选择排序
- 每轮找到剩余元素中的最大值,放到前面
- 第 k 轮结束后,arr[k-1] 就是第 k 大的元素
- 优点:代码简单,时间紧时稳妥
- 缺点:时间复杂度较高
*/
    for (int i = 0; i < k; i++)
    {
        int max = i;
        for (int j = i + 1; j < len; j++)
        {
            if (arr[j] > arr[max])
            {
                max = j;
            }
        }
        // 找到最大的,交换到前面去
        int tmp = arr[i];
        arr[i] = arr[max];
        arr[max] = tmp;
    }
    // k次降序处理,k-1个元素则为第k大的
    return arr[k - 1];
#endif

#ifdef QUICK_SELECT_METHOD /* 2. 快速选择实现 */
/*
- 基于快速排序的 partition 思想
- partition 后,基准元素的位置就是它在有序数组中的最终位置
- 如果基准位置正好是 k-1,直接返回
- 如果基准位置 > k-1,在左半部分继续找
- 如果基准位置 < k-1,在右半部分继续找
- 平均时间复杂度 O(n),最坏 O(n²)(可通过随机化基准优化)
*/
    return quick_select(arr, 0, len - 1, k);
#endif
}


int main()
{
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int Num = find_kth_largest(a, 10, 4);
    printf("num=%d\n", Num);

    return 0;
}
相关推荐
caimouse2 小时前
ReactOS 图形系统分析(50):画笔子系统 — pen.c
c语言·开发语言·stm32
caimouse2 小时前
ReactOS 图形系统分析(48):弧线绘制 — arc.c
c语言·开发语言
cypking3 小时前
Objective-C 语法完整学习手册(小白自学 + 开发备查)
c语言·开发语言·学习·objective-c
wuyk55512 小时前
98.C语言易混难点:字符数组与字符串指针的底层差异
c语言·开发语言·c++·stm32·嵌入式硬件·算法
wp123_113 小时前
IPX8 防水 Type‑C连接器:安费诺 124018792112A 与 TONEVEE TY48087‑24A 技术梳理
c语言·开发语言
2401_8628808219 小时前
数据结构 --- 栈
c语言·数据结构·算法
左手厨刀右手茼蒿20 小时前
数据仓库的架构演进:从MySQL到ClickHouse到数据湖的工程化实践
linux·嵌入式·系统内核
左手厨刀右手茼蒿20 小时前
创业团队技术选型:别用大厂架构解决小团队问题
linux·嵌入式·系统内核
DoLovya20 小时前
从零实现 ESP8266 + MQTT 智能门禁:公网一键开门、舵机控制、在线监控完整方案
python·物联网·mqtt·嵌入式·esp8266·门禁系统