嵌入式八股C语言---函数篇
最全嵌入式八股收录
函数
- 函数指针与指针函数
- 函数指针 这是一个指针 只不过指向的是一个函数
int (*fun) (int x,int y); // 这个指针指向了一个函数 函数返回值为int 参数为两个int
int sum(int x,int y) {
return x + y;
}
fun = sum;
fun(x,y); - 指针函数
这个是指这个函数的返回值是一个指针 最常见的就是我们的malloc函数
- 函数参数的压栈顺序
压栈顺序是从右到左的 好处是支持一些可变参数的输入 比如printf / scanf - 栈帧
整个函数的运行都是在栈上运行的,那栈在哪里呢–在嵌入式环境下 我们最开始启动的时候就会给SP指针传值 这个是就是栈顶的值啦
那么如何管理整个运行期间的栈呢 X86靠的是ESP和EBP(栈底) 而AR靠的就是SP(栈顶)和 FP(栈底)指针
我们把每个函数的栈空间叫做栈帧 靠上面的两个指针维护 - 函数的调用过程
整个函数的运行都是在栈上运行的
-
step1: 把参数都压栈
此时会看到一部分参数 直接放入了 r0 - r3寄存器 另外一部分则是根据fp指针压入了栈中
str r3,[fp,#-8]
-
step2:push{fp, lr} 把当前的栈底 和 lr寄存器的值压入
- 为什么要压入上一级的fp(栈底)呢?是
因为在函数调用中 对于变量的访问都是基于fp的偏移量来的,所以当你想调用新的函数的时候,一定得把老的fp保存 要不等会返回来怎么访问变量呢 - 至于lr 不是百分百需要的
试想一下 如果a函数调用了b 但是b又不会调用新函数 那就没必要保存b的返回地址啦 但是a的返回地址得保存;要不b执行完了可咋返回a

- 为什么要压入上一级的fp(栈底)呢?是
-
step3:设置新的fp指针
此时是最容易迷糊的有的时候是
add fp, sp, #4 // 由 push {lr}
有的时候是
add fp, sp, #0
这是为啥呢 起始主要原因是因为我们的新的fp是要看你把老的fp保存在哪里了
-
step4: 开辟栈空间
sub sp, sp,#16
伟大的编译器将会帮我们算出来此次函数调用需要多深的栈,所以无序我们显示指定栈的大小(赞美编译器)
-
step5 : 操作
-
step6 : 重新设置sp栈顶指针

-
step7 : pop{fp,pc}
- 主要是把 fp的值设置回去 设置为老fp的值
- 把lr的值给pc 告诉pc下条指令的地址从而恢复执行

-
step8: bx lr
这个主要是lr寄存器的低位有用 会判断此时返回到哪里 是特权模式还是ARM模式
- 函数的隐式声明
当你调用一个函数但是不在本文件定义过,你也没声明 是不会报错的 编译器会觉得这玩意在其它位置 所以会有一个隐式声明,返回值为int.
如果你调用的函数返回类型正好是int,那么皆大欢喜,程序的运行不会出现任何问题。如果你调用的函数返回类型是float,而编译器声明的函数类型为int,则程序运行时会发生不可预期的结果。 - malloc与free
- malloc(0) 与 malloc(-1)
- malloc(0) 发生什么都是有可能的 看你怎么实现的 可能是直接错误 也可能是把0对齐到某个MIN_SIZE
- malloc(-1)的话 因为malloc的参数是 size_t 所以相当于传入一个很大的正数 直接报错
- malloc(4G) 在操作系统下
在操作系统下 malloc分配的就不是实打实的物理内存而是虚拟内存的
对于32位操作系统 虚拟内存顶破天就是2的32次方也就是4G 所以分配不了
但是64位操作系统不一样 虚拟内存是够用的 能分配 ;
但是不一定能使用 当真正使用的时候会触发缺页异常然后找物理内存就会发现不够用 - malloc 和 new的区别
malloc分配的内存要么在堆区 要是大一点是在mmap区 new好像是在堆区?- malloc
需要指定分配多大的字节
返回值是一个 void * 自己用的话需要强制转换为自己需要的 - new
分配的时候 是new 一个对象,不用指定大小;
并且会调用相关的构造函数
返回指针就是对应的
- malloc
-
printf
printf()传入的参数是变参数的 此时从右到左的好处就显示出来了 char * format就知道几个参数了
大概就是每看到% 然后通过一个switch case 就知道要通过指针查询多少数据了
-
设计自己的strlen strcpy
- strlen
size_t mystrlen(const char * str) {
if(str == NULL)
return 0;
const char *ptr = str;
while(*ptr != '\0') {
ptr++;
}
return (size_t)(ptr - str); // 计算从开始到结束的距离,并返回
}
- strcpy
char * strcpy(char * dest,char * res) {
if(res == NULL || dest == NULL)
return NULL;
char * org = dest;
while(*dest++ = *res++);
return org;
}
- strstr函数
char * mystrstr(const char * sub,const char * str) {
if(sub == NULL || str == NULL)
return NULL;
if( ! *sub)
return (char * )str;
while(*str) {
const char * start = str;
const char * s = sub;
while(*start && *s && *start == *s) {
start++;
s++;
};
if(!*s)
return (char *)str;
str++;
}
return NULL;
- 链表操作函数
定义一个链表结构 实现基本的插入 查询 删除动作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ListNode
{
int val;
struct ListNode *next;
}ListNode;
ListNode * global_Node = NULL;
void init() {
global_Node = (ListNode *)malloc(sizeof(ListNode));
global_Node->val = 0;
global_Node->next = NULL;
}
// 插入头部
void add_tail(int val)
{
ListNode * dummy = global_Node;
while(dummy->next != NULL) {
dummy = dummy->next;
}
ListNode * tmp = (ListNode *)malloc(sizeof(ListNode));
tmp->val = val;
tmp->next = NULL;
dummy->next = tmp;
}
// 插入尾部
void add_head(int val)
{
ListNode * tmp = (ListNode *)malloc(sizeof(ListNode));
tmp->val = val;
tmp->next = NULL;
ListNode * next = global_Node->next;
global_Node->next = tmp;
tmp->next = next;
}
// 删除第num个(num >= 0)节点 global_Node不算
void deleteNode(int num)
{
ListNode * dummy = global_Node;
num = num - 1;
while(dummy && dummy->next && num--)
dummy = dummy->next;
if(dummy && dummy->next)
{
ListNode * temp = dummy->next;
dummy->next = dummy->next->next;
free(temp);
temp = NULL;
}
}
// 查询 num>= 1
ListNode * get(int num) {
ListNode * dummy = global_Node;
while(dummy && num--)
dummy = dummy->next;
return dummy;
}
// 遍历
void traverse() {
ListNode * dummy = global_Node;
while(dummy) {
printf("%d->",dummy->val);
dummy = dummy ->next;
}
printf("\r\n");
}
int main()
{
// 初始化链表
init();
// 添加一些元素到链表尾部
add_tail(10);
add_tail(20);
add_tail(30);
// 打印当前链表
printf("After adding to tail:\n");
traverse(); // 应输出: 10->20->30->NULL
// 添加一个元素到链表头部
add_head(5);
printf("After adding to head:\n");
traverse(); // 应输出: 5->10->20->30->NULL
// 添加一个元素到链表头部
add_tail(15);
printf("After adding to tail:\n");
traverse(); // 应输出: 5->10->20->30->NULL
// 删除第二个节点
deleteNode(2);
printf("After deleting node at position 2:\n");
traverse(); // 应输出: 5->20->30->NULL
// 获取并打印第三个节点的值
ListNode *node = get(3);
if (node) {
printf("Value at position 3: %d\n", node->val); // 应输出: Value at position 3: 30
} else {
printf("Node not found\n");
}
// 清理链表
ListNode *current = global_Node->next;
while (current != NULL) {
ListNode *next = current->next;
free(current);
current = next;
}
free(global_Node);
global_Node = NULL;
return 0;
return 0;
}
- 设计FIFO
定义一个先入先出队列 实现数据的读/写
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MIN(x,y) ({\
typeof(x) _x = x;\
typeof(y) _y = y;\
(void) (&x == &y);\
_x > _y ? _y : _x;})
struct fifo {
unsigned int size;
unsigned int readIndex;
unsigned int writeIndex;
unsigned char * buffer;
};
unsigned int up_power_of_two(unsigned int val)
{
if(val == 0 || val == 1)
return 2;
return 1 << (32 - __builtin_clz(val - 1));
}
struct fifo * alloc(unsigned int size) {
size = up_power_of_two(size);
struct fifo * g_fifo = malloc(sizeof(struct fifo));
g_fifo->size = size;
g_fifo->readIndex = 0;
g_fifo->writeIndex = 0;
g_fifo->buffer = (char *)malloc(size);
return g_fifo;
}
// 从fifo中读
unsigned int fifo_get(struct fifo * fifo, void * buffer, unsigned int readSize)
{
readSize = MIN(readSize,fifo->writeIndex - fifo->readIndex);
unsigned int first_len = MIN(readSize, fifo->size - (fifo->readIndex) & (fifo->size - 1));
memcpy(buffer,&fifo->buffer[fifo->readIndex & (fifo->size - 1)],first_len);
memcpy(buffer + first_len, fifo->buffer, readSize - first_len);
fifo->readIndex += readSize;
return readSize;
}
unsigned int fifo_put(struct fifo * fifo, const void * buffer, unsigned int writeSize)
{
writeSize = MIN(writeSize, fifo->size - fifo->writeIndex + fifo->readIndex); // 还可以写入的空间
unsigned int first_len = MIN(writeSize, fifo->size - (fifo->writeIndex)&(fifo->size - 1));
memcpy(&fifo->buffer[fifo->readIndex & (fifo->size - 1)],buffer,first_len);
memcpy(fifo->buffer,buffer + first_len,writeSize - first_len);
fifo->writeIndex += writeSize;
return writeSize;
}
int main() {
// 分配一个小的FIFO缓冲区(例如大小为4)
struct fifo * fifo = alloc(10);
if (!fifo) {
printf("Failed to allocate FIFO\n");
return 1;
}
printf("FIFO allocated with size: %u\n", fifo->size);
// 写入数据
const char * data_to_write = "HelloWorld";
unsigned int written = fifo_put(fifo, data_to_write, strlen(data_to_write));
printf("Written %u bytes: ", written);
for (unsigned int i = 0; i < written; ++i) {
printf("%c", fifo->buffer[i % fifo->size]);
}
printf("\n");
// 读取数据
char read_buffer[20];
unsigned int read = fifo_get(fifo, read_buffer, sizeof(read_buffer));
printf("Read %u bytes: ", read);
for (unsigned int i = 0; i < read; ++i) {
printf("%c", read_buffer[i]);
}
printf("\n");
// 尝试写入更多数据以触发溢出
const char * more_data = "OverflowTest6666666666666";
written = fifo_put(fifo, more_data, strlen(more_data));
printf("Written additional %u bytes: ", written);
for (unsigned int i = 0; i < written; ++i) {
printf("%c", fifo->buffer[(fifo->writeIndex - written + i) % fifo->size]);
}
printf("\n");
// 再次读取数据
read = fifo_get(fifo, read_buffer, sizeof(read_buffer));
printf("Read %u bytes after overflow: ", read);
for (unsigned int i = 0; i < read; ++i) {
printf("%c", read_buffer[i]);
}
printf("\n");
// 释放分配的内存
free(fifo->buffer);
free(fifo);
return 0;
}
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)