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;
- 问题分析:
status_reg 指向一个硬件寄存器地址(0x40021000)
该寄存器的值由硬件改变,不是由软件改变
但编译器不知道这一点,它看到循环内没有修改 *status_reg 的代码
编译器优化:"这个值不会变,我把它缓存到寄存器里,只读一次"
结果:程序可能永远读的是旧值,陷入死循环
- volatile 的作用:
告诉编译器:这个变量可能会在程序控制之外被改变
禁止编译器对该变量进行优化:
不能缓存到寄存器
不能删除"无用"的读写操作
每次使用都必须从内存重新读取
- 嵌入式中必须使用 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;
}