函数

  1. 函数指针与指针函数
  • 函数指针 这是一个指针 只不过指向的是一个函数
    int (*fun) (int x,int y); // 这个指针指向了一个函数 函数返回值为int 参数为两个int
    int sum(int x,int y) {
    return x + y;
    }
    fun = sum;
    fun(x,y);
  • 指针函数
    这个是指这个函数的返回值是一个指针 最常见的就是我们的malloc函数
  1. 函数参数的压栈顺序
    压栈顺序是从右到左的 好处是支持一些可变参数的输入 比如printf / scanf
  2. 栈帧
    整个函数的运行都是在栈上运行的,那栈在哪里呢–在嵌入式环境下 我们最开始启动的时候就会给SP指针传值 这个是就是栈顶的值啦
    那么如何管理整个运行期间的栈呢 X86靠的是ESP和EBP(栈底) 而AR靠的就是SP(栈顶)和 FP(栈底)指针
    我们把每个函数的栈空间叫做栈帧 靠上面的两个指针维护
  3. 函数的调用过程
    整个函数的运行都是在栈上运行的
    在这里插入图片描述
  • step1: 把参数都压栈
    此时会看到一部分参数 直接放入了 r0 - r3寄存器 另外一部分则是根据fp指针压入了栈中
    str r3,[fp,#-8]
    在这里插入图片描述

  • step2:push{fp, lr} 把当前的栈底 和 lr寄存器的值压入

    • 为什么要压入上一级的fp(栈底)呢?是
      因为在函数调用中 对于变量的访问都是基于fp的偏移量来的,所以当你想调用新的函数的时候,一定得把老的fp保存 要不等会返回来怎么访问变量
    • 至于lr 不是百分百需要的
      试想一下 如果a函数调用了b 但是b又不会调用新函数 那就没必要保存b的返回地址啦 但是a的返回地址得保存;要不b执行完了可咋返回a

    在这里插入图片描述

  • 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模式

  1. 函数的隐式声明
      当你调用一个函数但是不在本文件定义过,你也没声明 是不会报错的 编译器会觉得这玩意在其它位置 所以会有一个隐式声明,返回值为int.
      如果你调用的函数返回类型正好是int,那么皆大欢喜,程序的运行不会出现任何问题。如果你调用的函数返回类型是float,而编译器声明的函数类型为int,则程序运行时会发生不可预期的结果。
  2. 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 一个对象,不用指定大小;
      并且会调用相关的构造函数
      返回指针就是对应的
  1. printf
    printf()传入的参数是变参数的 此时从右到左的好处就显示出来了 char * format就知道几个参数了
    大概就是每看到% 然后通过一个switch case 就知道要通过指针查询多少数据了
    在这里插入图片描述

  2. 设计自己的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;
  1. 链表操作函数
    定义一个链表结构 实现基本的插入 查询 删除动作
 #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;
}
  1. 设计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;
}
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐