嵌入式C语言

article/2025/8/22 20:31:05

文章目录

      • 一、学会使用char/short/int关键字
      • 二、学会使用 if & switch
      • 三、学会使用 for & while
      • 四、学会使用static关键字
      • 五、学会使用define关键字
      • 六、学会使用typedef关键字
      • 七、学会使用enum关键字
      • 八、学会使用struct类型
      • 九、学会使用指针类型
      • 十、学会使用回调函数
      • 十一、综合项目
        • 1. 可变参数的实现
        • 2. Zlog日志框架
        • 3. google测试框架

一、学会使用char/short/int关键字

  1. 不同数据类型存储的长度

    #include <stdio.h>char a;    //0x00~0xff
    short b;   //0x00~0xffff
    int c;     //0x00~0xffffffffvoid main()
    {printf("hello world!\n");printf("%d %d %d\n", sizeof(a), sizeof(b), sizeof(c));
    }
    
  2. 数据类型的转换

    #include <stdio.h>unsigned char time_buffer[4] = { 0x5F, 0xCB, 0x0F, 0x4F };
    unsigned int time;//数据转换函数
    unsigned int u8tou32B(unsigned char *buffer)
    {return (((((((unsigned int)buffer[0]) << 8) | buffer[1]) << 8) | buffer[2]) << 8) | buffer[3];
    }void main()
    {printf("hello world!\n");time = u8tou32B(time_buffer);printf("time = %ld s\n",time);
    }
    

二、学会使用 if & switch

条件判断语句ifswitch,当判断的类型较少的时候,使用if去判断;当判断的类型较多的时候,使用switch去判断。

#include <stdio.h>typedef enum{NET_INIT = 0,NET_CONNECTING,NET_CONNECT_SUCCESS,NET_CONNECT_FAIL,NET_ERROR,
}E_NET_STATUS;void main()
{printf("hello world\n");E_NET_STATUS net_status = NET_CONNECTING;//1. 第一种方式if (net_status == NET_INIT){}else if (net_status == NET_CONNECTING){}else if (net_status == NET_CONNECT_SUCCESS){}else if (net_status == NET_CONNECT_FAIL){}else if (net_status == NET_ERROR){}//2. 第二种方式switch (net_status){case NET_INIT:break;case NET_CONNECTING:break;case NET_CONNECT_SUCCESS:break;case NET_CONNECT_FAIL:break;case NET_ERROR:break;default:break;}
}

三、学会使用 for & while

  1. while 的用法

    # include <stdio.h>void main()
    {int wifi_array[5] = { 1, 2, 3, 4, 5 };int index = 0;while (index < sizeof(wifi_array)/sizeof(int)){printf("%d \n", wifi_array[index]);index++;}
    }
    
  2. for 的用法

    # include <stdio.h>void main()
    {int wifi_array[5] = { 1, 2, 3, 4, 5 };int sum = 0;for (int index = 0; index < sizeof(wifi_array) / sizeof(int); index++){sum += wifi_array[index];}printf("%d \n", sum);
    }
    

四、学会使用static关键字

此处我们需要创建两个文件

主函数main.cpp

# include <stdio.h>
//这里使用 "" 符号加载自己创建的头文件
# include "test.h"void main()
{printf("hello world\n");test();test1();test1();test1();
}

test.cpp文件

# include <stdio.h>
# include "test.h"//创建静态函数,表明函数只能在本文件中使用,其他文件若是调用会报错
//若其他文件想要使用,可以创建外部函数,把此函数放到外部函数内部。
static void test_printf()
{printf("test_printf\n");
}void test(void)
{test_printf();
}void test1()
{//创建静态变量,编译器只会执行一次这个语句static int num = 0;num++;printf("%d\n", num);
}

test.h文件

//文件多的时候,多个文件调用同一个文件,会出现重复定义的情况
//我们使用 #ifndef---#endif 来确保头文件重复创建#ifndef _TEST_H_
#define _TEST_H_void test(void);
//此处使用会报错
static void test_printf(void);
void test1(void);#endif

运行结果:

hello world
test_printf
1
2
3

五、学会使用define关键字

# include <stdio.h>//算法运算需要加上括号
# define A (3+1)//使用define关键字声明一个值,程序中使用 ifdef 来判断,
//可以很方便的应用于程序调试中
//# define VERSION_PRE 0
# define VERSION_GLOD 1void main()
{printf("hello world\n");printf("sum = %d \n", 10 * A);#ifdef VERSION_PREprintf("THIS is a Pre version\n");
#elif VERSION_GLODprintf("THIS is Gold version\n");
#endif
}

六、学会使用typedef关键字

# include <stdio.h>//注意typedef和define的区别
typedef char* PCHAR1;
#define PCHAR2 char*//c1、c2都为char*,typedef为char* 引入了一个新的别名
PCHAR1 c1, c2;
//等价于
char *c1, *c2;//相当于char* c3,c4; c3是char*,而c4是char
PCHAR2 c3, c4;
//等价于
char* c3;
char c4;//在单片机开发中,定义变量时使用unsigned char会过于繁琐,通常把unsigned char重定义为uint8
//当程序中需要定义unsigned char时,直接使用uint8即可。
typedef unsigned char  uint8;
typedef unsigned short uint16;
typedef unsigned int   uint32;

七、学会使用enum关键字

# include <stdio.h>//使用enum定义枚举类型
typedef enum{NET_INIT = 1,NET_CONNECTING,NET_CONNECT_SUCCESS,NET_CONNECT_FAIL,NET_ERROR,
}E_NET_STATUS;void main()
{//定义枚举类型变量,可以限制数据的范围,方便检查程序E_NET_STATUS ble_status = NET_CONNECTING;//以下判断网络的连接状态if (ble_status == NET_INIT){printf("Initing\n");}else if (ble_status == NET_CONNECTING){printf("Connecting\n");}else if (ble_status == NET_CONNECT_SUCCESS){printf("Connect success\n");}else if (ble_status == NET_CONNECT_FAIL){printf("Connect fail\n");}else{printf("error");}
}

八、学会使用struct类型

使用struct把同一事物的所有属性包含在一起,使程序结构整洁,条理清晰。

# include <stdio.h>typedef enum{NET_INIT = 1,NET_CONNECTING,NET_CONNECT_SUCCESS,NET_CONNECT_FAIL,NET_ERROR,
}E_NET_STATUS;typedef struct{int num;
}T_Manager;typedef struct{int num;int time;int money;
}T_Pay;typedef struct{int system_status;int net_status;int motor_status[4];int pay_status;T_Pay m_pay;T_Manager m_manager;
}T_Device;//定义两个不同的网卡
T_Device g_Device;
T_Device g_Device1;void main()
{g_Device.net_status = NET_INIT;g_Device1.net_status = NET_CONNECTING;g_Device.m_pay.num = 1;g_Device.m_manager.num = 2;printf("Net status = %d\n", g_Device.net_status);printf("Net1 status = %d\n", g_Device1.net_status);printf("m_pay = %d\n", g_Device.m_pay.num);printf("m_Manager = %d\n", g_Device.m_manager.num);
}

执行结果:

Net status = 1
Net1 status = 2
m_pay = 1
m_Manager = 2

九、学会使用指针类型

  1. 指针操作的是地址

    # include <stdio.h>void main()
    {int val = 10;int *p;p = &val;printf("p = %d *p = %d\n", p,*p);
    }
    

    在这里插入图片描述

执行结果:

p = 2096592 *p = 10

此处p代表变量地址, *p才代表变量里面的数值

  1. 交换a,b

    # include <stdio.h>//1. 不使用指针类(保存临时变量,交换不成功)
    void swap(int data1, int data2)
    {int temp = data2;data2 = data1;data1 = temp;
    }void main()
    {int a = 10, b = 20;swap(a, b);printf("a = %d, b = %d\n", a, b);
    }//2. 使用指针类型(直接交换变量的地址,交换成功)
    void swap(int *data1, int *data2)
    {int temp = *data2;*data2 = *data1;*data1 = temp;
    }void main()
    {int a = 10, b = 20;swap(&a, &b);printf("a = %d, b = %d\n", a, b);
    }
    
  2. 100个钥匙使用指针操作

    #include <stdio.h>void open(int *key, int num)
    {printf("data = %d\n", key[num]);
    }void main()
    {int key[100] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };open(key, 5);
    }
    

十、学会使用回调函数

使用场景

  1. 送餐机器人:底盘移动到目标位置后,如何通知应用程序?
  2. 智能音箱:网络状态更改后,如何通知应用程序?

传统方式

  1. 开放变量,让别人直接获取
  2. 通过getStatus()类似的函数定时获取

建议方式

​ 使用回调函数

具体实现

# include <stdio.h>//工具代码
typedef struct{int status;void(*statusChange)(int status);
}T_Device;T_Device g_Device;void addCallbackFunc(void (*pstatusChange)(int status)){g_Device.statusChange = pstatusChange;
}void run(){g_Device.status = 10;if (g_Device.status == 10){if (g_Device.statusChange != NULL){g_Device.statusChange(g_Device.status);}}
}//用户代码
void callBack(int status){printf("status = %d\n", status);
}void main()
{//当自己的状态等于10,并且有回调函数的时候,执行这个回调函数,//从而实现自己的状态到了某个值时主动执行回调函数addCallbackFunc(callBack);run();
}

十一、综合项目

1. 可变参数的实现

可变参数函数:可以在运行时取任意的实参个数并根据实参的个数自动处理不同实参的情形,或者至少可以在运行时指定任意个数的实参个数。

  1. c语言中的printf()函数就是典型的可变参数函数,它除了有一个固定的format参数外,后面的参数都是可变的。

  2. printf()函数通过第一个参数确定形参的个数,通过占位符确定形参类型。

  3. printf()函数源码

    #include <stdarg.h>int printf(const char *fmt, ...)
    {char printf_buf[1024];va_list args;      		int printed;va_start(args, fmt);  	printed = vsprintf(printf_buf, fmt, args);va_end(args);          puts(printf_buf);return printed;
    }
    
  4. 宏介绍:

    1. va_list:用于定义va_list类型的变量
    2. va_start:用于初始化va_list类型的变量
    3. va_end:关闭变参扩展,进行内存回收

2. Zlog日志框架

  1. 第一种方式:整个c文件

    #include <stdio.h>
    //可变参数使用的头文件
    #include <stdarg.h>  //声明是否打开日志输出
    #define OPEN_LOG 1          
    //声明当前程序的日志等级状态,只输出等级等于或高于该值的内容
    #define LOG_LEVEL LOG_DEBUG   //日志等级,越往下越高
    typedef enum{LOG_DEBUG = 0,LOG_INFO,LOG_WARN,LOG_ERROR,
    }E_LOGLEVEL;//判断当前输入的日志等级
    char *EM_LOGLevelGet(const int level){if (level == LOG_DEBUG){return "LOG_DEBUG";}else if (level == LOG_INFO){return "LOG_INFO";}else if (level == LOG_WARN){return "LOG_WARN";}else if (level == LOG_ERROR){return "LOG_ERROR";}return "UNKNOW";
    }//日志输出函数
    void EM_LOG(const int level, const char* fun, const int line, char *fmt, ...){
    #ifdef OPEN_LOGva_list arg;va_start(arg, fmt);char buf[vsnprintf(NULL, 0, fmt, arg) + 1];vsnprintf(buf, sizeof(buf), fmt, arg);va_end(arg);if (level >= LOG_LEVEL)printf("[%s] [%s %d] %s\n",EM_LOGLevelGet(level), fun, line, buf);//TODO 存储
    #endif
    }//宏定义,隐藏形参,只需要日志等级和可变参数
    #define EMlog(level, fmt, ...) EM_LOG(level, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)void main(){int a = 10, b = 11;EMlog(LOG_DEBUG, "app start");EMlog(LOG_INFO, "a = %d, b=%d", a, b);EMlog(LOG_WARN, "app LOG_WARN");EMlog(LOG_ERROR, "app LOG_ERROR");
    }
    
  2. 独立多文件:main.cpp log.cpp log.h

    main.cpp

    #include <stdio.h>
    #include <stdarg.h>
    #include "log.h"void main(){int a = 10, b = 11;EMlog(LOG_DEBUG, "app start");EMlog(LOG_INFO, "A = %d", a);EMlog(LOG_WARN, "app LOG_WARN");EMlog(LOG_ERROR, "app LOG_ERROR");
    }
    

    log.h

    #ifndef _EM_LOG_H_
    #define _EM_LOG_H_//可变参数使用的头文件
    #include <stdarg.h>  //声明是否打开日志输出
    #define OPEN_LOG 1          
    //声明当前程序的日志等级状态,只输出等级等于或高于该值的内容
    #define LOG_LEVEL LOG_DEBUG   
    //是否保存日志文件
    #define LOG_SAVE 1//日志等级,越往下越高
    typedef enum{LOG_DEBUG = 0,LOG_INFO,LOG_WARN,LOG_ERROR,
    }E_LOGLEVEL;//函数声明
    void EM_LOG(const int level, const char* fun, const int line, char *fmt, ...);//宏定义,隐藏形参,只需要日志等级和可变参数
    #define EMlog(level, fmt, ...) EM_LOG(level, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)#endif
    

    log.cpp

    #include <stdio.h>
    #include "log.h"//判断当前输入的日志等级
    char *EM_LOGLevelGet(const int level){switch (level){case LOG_DEBUG: return "LOG_DEBUG"; break;case LOG_INFO: return "LOG_INFO"; break;case LOG_WARN: return "LOG_WARN"; break;case LOG_ERROR: return "LOG_ERROR"; break;default: return "UNKNOW"; break;}
    }//日志输出函数
    void EM_LOG(const int level, const char* fun, const int line, char *fmt, ...){
    #ifdef OPEN_LOGva_list arg;va_start(arg, fmt);char buf[1 + vsnprintf(NULL, 0, fmt, arg)];vsnprintf(buf, sizeof(buf), fmt, arg);va_end(arg);if (level >= LOG_LEVEL)printf("[%s] [%s %d] %s\n", EM_LOGLevelGet(level), fun, line, buf);//TODO 存储
    #endif
    }
    

3. google测试框架

#include <stdio.h>
#include <stdlib.h>typedef struct
{int output;int a;int b;int(*TeseFunc)(int, int);  int line;                   
}T_Test;// 结构体作为函数返回值(新建结构体)
T_Test *addFunc(int(*TeseFunc)(int, int), int a, int b, int output, int line){T_Test *m_Test = (T_Test *)malloc(sizeof(T_Test));  m_Test->a = a;m_Test->b = b;m_Test->TeseFunc = TeseFunc;m_Test->output = output;m_Test->line = line;return m_Test;
}// 宏定义函数,缺省line
#define addFunc(TeseFunc, a, b, output) addFunc(TeseFunc, a, b, output, __LINE__)//测试函数
void runTest(T_Test *p_Test){if (p_Test != NULL){int count = p_Test->TeseFunc(p_Test->a, p_Test->b);if (count == p_Test->output){printf("success \n");}else{printf("[LINE: %d] fail %d != %d\n", p_Test->line, count, p_Test->output);}free(p_Test);   }
}int add(int a, int b){return a + b;
}int main(){printf("Test app start.\n");T_Test *m_Test = addFunc(add, 1, 2, 4);runTest(m_Test);return 0;
}

http://chatgpt.dhexx.cn/article/Un3mGXWA.shtml

相关文章

嵌入式开发之C语言基础(一)

目前是一名大二升大三的学生&#xff0c;早就有想法写博文了&#xff0c;但是因为自己的拖延一直到现在才开始着手。关于博文&#xff0c;我有以下打算&#xff1a;把C语言再重新过一遍&#xff0c;毕竟嵌入式开发C语言的重要性不言而喻&#xff1b;接下来我还会有一些关于单片…

极简嵌入式C语言教程——从入门到入土(1)

文章目录 第一章&#xff1a;入门1.Hello World!2.C语言标识符与关键字(1)标识符&#xff1a;(2)关键字 3.数据类型与运算符(1)变量与常量<1>变量<2>变量的定义<3>常量 (2)运算符<1>算术运算符<2>自增、自减运算符<3>赋值与赋值组合运算符…

STM32中常用的C语言知识点,开始复习!

要学嵌入式&#xff0c;关注我要学嵌入式&#xff0c;嵌入式猛男的加油站。 C语言是单片机开发中的必备基础知识&#xff0c;这里就列举部分STM32学习中会遇见的C 语言基础知识点。 01 位操作 下面我们先讲解几种位操作符&#xff0c;然后讲解位操作使用技巧。C语言支持如下6中…

嵌入式C语言(入门必看)

目录 STM32的数据类型 const关键字 static 关键字 volatile关键字 extern关键字 struct结构体 enum typedef #define 回调函数 #ifdef 、#ifndef、#else 、#if 嵌入式开发中既有底层硬件的开发又涉及上层应用的开发,即涉及系统的硬件和软件,C语言既具有汇编语言操…

用单层感知器完成逻辑或运算的学习过程

用单层感知器完成逻辑或运算的学习过程 这道题目是我“认知科学”&#xff08;专业必修/doge&#xff09;课程的结课作业题之一&#xff0c;目的在于加深对单层感知器的理解&#xff0c;对于后续学习神经网络打下基础。 有关知识&#xff1a; B站有关视频 单层感知器的学习过…

python中逻辑运算_【多选题】Python 中用于表示逻辑与、逻辑或、逻辑非运算的关键字分别是( ) A. and B. add C. or D. not...

【多选题】Python 中用于表示逻辑与、逻辑或、逻辑非运算的关键字分别是( ) A. and B. add C. or D. not 更多相关问题 A.He has been asked to join the committee.B.There are several new people on the c A.fill inB.equipC.fitD.prove A.encounterB.discernC.seeD.fre…

逻辑运算符简介, 逻辑与,逻辑或,逻辑非和逻辑运算符里的短路运算规则

1&#xff0c;逻辑与 && 符号两边都为true&#xff0c;结果才为true 一假则假 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"…

R语言的逻辑与、逻辑或和元素逻辑与、元素逻辑或的区别

版权声明&#xff1a;转载请注明作者&#xff08;独孤尚良dugushangliang&#xff09;出处&#xff1a;https://blog.csdn.net/dugushangliang/article/details/116463648 参阅&#xff1a;https://www.runoob.com/r/r-basic-operators.html 下图为R运行结果。首先对a、b赋值&a…

Java逻辑操作符——逻辑非、逻辑与、逻辑或和逻辑异或

先上一段java代码&#xff0c;通过具体例子来理解抽象概念 public class 布尔值 {public static void main(String[] args) {boolean 逻辑非的值_测试1 true;boolean 逻辑非的值_测试2 false;System.out.println("逻辑非的值_测试1:"!逻辑非的值_测试1);System.ou…

逻辑与和按位与、逻辑或和按位或的区别

首先分别明确一下他们各自的概念。 按位与和按位或 按位与和按位或都属于位操作符。 注意&#xff1a;位操作符的操作数必须是整数。 按位与“&” 按二进制位对应的位进行与运算&#xff0c;对应位都为1时&#xff0c;结果才为1 3&5 3的二进制&#xff1a; 00000…

JS中的逻辑与和逻辑或

JS中的逻辑或||符号&#xff1a; 从字面上来说&#xff0c;只有前后都是 false 的时候才返回 false&#xff0c;否则返回 true。 console.log(5 > 6|| 6 > 5) //返回true5>6为false 但是 6>5为true 所以返回 true 总结&#xff1a;一真为真 特殊运算方法&#xff…

逻辑或( || )和逻辑与( )的关系

逻辑或&#xff0c;符号为“||”&#xff0c;只有操作数都是假&#xff0c;结果才是假。&#xff08;全假才为假&#xff09; 逻辑与&#xff0c;符号为“&&”&#xff0c;只有操作数都是真&#xff0c;结果才是真。&#xff08;全真才为真&#xff09; 如下图&#xf…

计算机逻辑与 或 非的表达式,计算机算数和,逻辑与,逻辑或,逻辑非分别是什么意思...

蔷祀的回答: 1、算术和:算术和就是所有的加数都是非负的(整数或0)得到的和。 2、逻辑与:逻辑与即1101 & 0100,就是按位相与,与的概念可以同俗的理解为,一个电路有两个串联的开关,只有同时关闭两个开关电路才通,打开任意一个开关电路都不通,所以那两个数逻辑与的结…

逻辑与(),逻辑或(||),and(),or(|)

一、背景、 这四个逻辑运算符&#xff0c;大家都知道&#xff0c;但是有时候会凌乱&#xff0c;再者就是我自己想写一点基础的东西&#xff0c;巩固一下自己&#xff0c;也算是一种笔记&#xff0c;不但自己会了&#xff0c;还可以分享给大家一起学习。 二、目的、 巩固自己…

逻辑同或,逻辑异或,逻辑与,逻辑或

一、逻辑异或 真⊕假真 假⊕真真 假⊕假假 真⊕真假 或者为&#xff1a; True ⊕ False True False ⊕ True True False ⊕ False False True ⊕ True False 二、逻辑同或 相同为一&#xff0c;不同为零。 三、逻辑与 1.逻辑与&& 表示逻辑与的意思&#xff0…

按位或与逻辑或的区别

按位或&#xff08;|&#xff09;和逻辑或&#xff08;||&#xff09;的区别&#xff1a;逻辑或&#xff1a;逻辑或是三种逻辑运算符之一。 逻辑或相当于生活中的或者&#xff0c;当两个条件中有任一个条件满足&#xff0c;逻辑或的运算结果就为真。按位或&#xff1a;按位或运…

Axure8.0 注册码

我的Axure升级到8.1.0.3382&#xff0c;使用最后一个可用的&#xff01; 其他版本的小伙伴&#xff0c;请自行试验吧~ 升级了8.1.0.3377版本后&#xff0c;需要使用下面这组注册码 license&#xff1a;zdfans.com key&#xff1a;gP5uuK2gH iIVO3YFZwoKyxAdHpXRGNnZWN8Obn…

Axure下载安装-汉化-注册码

安装版本&#xff1a;8.1.0 一、下载、安装 链接&#xff1a;https://pan.baidu.com/s/1GbQKSn2aWnFOAkPsLITWQA 密码&#xff1a;qcf6 安装最后一步&#xff0c;不要点Run xxx 二、汉化 下载一个01053230.zip的压缩包&#xff0c;解压后把lang文件夹放到Axure的安装目录下…

RFID-RC522/STM32F103RB/KEIL5 简单实现读取卡片ID

文章目录 序章简单使用核心源码工程下载 序章 在这篇文章【 https://blog.csdn.net/qq_28877125/article/details/80437095 】的基础上修改完成&#xff01; 简单使用 1).环境配置 MCU芯片型号&#xff1a;STM32F103RB开发板&#xff1a;理论任何STM32开发板KIDE&#xff1…

STM32CubeMX(13)——SPI时序读写RFID-RC522

SPI时序读写RFID-RC522 目录 STM32 Cubemax(十三) ——SPI时序读写RFID-RC522 前言 一、SPI时序通信 二、模块接线 三.Cubemax配置 四.核心代码 延时函数 写RC522寄存器 读RC522寄存器 复位RC522 使用代码 1.复位 2.寻卡并得到其序列号 总结 前言 用RFID来学习一…