006 单片机嵌入式中的C语言与代码风格规范——常识

news2024/11/25 12:48:50

00 环境准备:

配置MDK支持C99

内置stdint.h介绍

stdint.h 是从 C99 中引进的一个标准 C 库的文件
路径:D:\MDK\ARM\ARMCC\include

 01 C语言基础语法

一般的bug很有可能是C语言功底不扎实导致……

1.结构体

由若干基本数据类型集合组成的一种自定义数据类型,也叫聚合类型

struct student
{
    char  	*name;		/* 姓名 */
    int     	num;  		/* 学号 */
    int     	age; 			/* 年龄 */
    char	group;  		/* 所在学习小组 */
    float  	score;  		/* 成绩 */
};
struct student stu3,stu4;
stu3.name = "张三";
stu3.num = 1; 
stu3.age = 18; 
stu3.group = 'A';
stu3.score = 80.9;

2.枚举

定义枚举变量:

    enum{FALSE = 0, TRUE = 1} EnumName;

3.指针

指针就是内存的地址,指针变量是保存了地址的变量

char * p_str = “This is a test!”;
*p_str:取p_str 变量的值
&p_str:取p_str变量的地址
uint8_t  buf[5] = {1, 3, 5, 7, 9}; 
uint8_t  * p_buf = buf;
*p_buf = ? 
p_buf[0] = ?
p_buf[1] = ?
p_buf++;
*p_buf = ?
p_buf[0] = ?

指针使用的2大最常见问题:

1,未分配(申请)内存就用,如下:

char * p_buf;
p_buf[0] = 100;

2,越界使用,如下:

int8_t  buf[5] = {1, 3, 5, 7, 9}; 
uint8_t  * p_buf = buf;
p_buf[5] = 200;
p_buf[6] = 250;

02 嵌入式常用语法

1.位操作

运算符

含义

运算符

含义

&

按位与

~

按位取反

|

按位或

<<

左移

^

按位异或

>>

右移

//& 与操作
0 & 0 = 0  
1 & 0 = 0    
1 & 1 = 1
//| 或操作
0 | 0 = 0  
1 | 0 = 1   
1 | 1 = 1
//^ 异或操作
0 ^ 0 = 0  
1 ^ 0 = 1  
0 ^ 1 = 1  
1 ^ 1 = 0
//~ 取反操作
~ 11100100 = 00011011
//>>、<< 移位操作
11100100 << 2 =10010000
11100100 >> 2 =00111001

 举例:给第6位赋值为0
temp &= ~(1<<6);

2.宏定义

是指在编译前对源代码进行文本替换。宏定义本身并不分配内存空间,而是在预处理阶段将宏名在全局作用域中替换为相应的替换列表。
替换不会占用运行时间,只占用编译时间

#define   目标标识符   原替换列表

应用:

#define     PI    3.14159

带参数的宏定义 

#define LED1(x)   do{  x ? \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
                  }while(0)

//上述等效=#define LED1(x)   do{  x ?  HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) :  HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET);}while(0)

其中“\”表示继续符,用于一行的结尾,表示本行与下一行连接起来 

另外还有:

\n 换行符(LF)

\r 回车(CR) ,相当于键盘上的"Enter"

\t 跳到下一个TAB位置

\0 空字符(NULL)

\' 单引号(撇号)

\" 双引号

\\ 代表一个反斜线字符''\' 等,详细可百度“转义字符”。

建议使用  do{ ... }while(0) 来 构造宏定义 这样不会受到大括号、分号、运算符优先级等的影响,总是会按你期望的方式调用运行!

3.条件编译

与普通if的区别:让编译器只对满足条件的代码进行编译,不满足条件的不参与编译!

应用:

头文件
#ifndef 	_LED_H
#define 	_LED_H
#include "./SYSTEM/sys/sys.h"
……
#endif
 预编译处理

可以让同一套代码适用于微小变化的开发板或芯片之间

#define  SYS_SUPPORT_OS 1
#if   (SYS_SUPPORT_OS==1)
    ……
#elif  (SYS_SUPPORT_OS==2)
    ……
#else
    ……
#endif
以宏定义的方式控制某些功能开|关
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
		{
			/* When using preemption tasks of equal priority will be
			timesliced.  If a task that is sharing the idle priority is ready
			to run then the idle task should yield before the end of the
			timeslice.

			A critical region is not required here as we are just reading from
			the list, and an occasional incorrect value will not matter.  If
			the ready list at the idle priority contains more than one task
			then a task other than the idle task is ready to execute. */
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )
			{
				taskYIELD();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
错误提示
#error error-message
#if configMAX_TASK_NAME_LEN < 1
	#error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h
#endif

4.类型别名(typedef)

typedef   原类型   新名字

为现有数据类型创建一个新的名字,或称为类型别名,用来简化变量的定义

应用: 

简化标识符
typedef   unsigned  char 	uint8_t;
typedef struct
{
     __IO uint32_t   CRL;
     __IO uint32_t   CRH;
     …
} GPIO_TypeDef;
GPIO_TypeDef    gpiox
/*原:
Struct GPIO_TypeDef
{
     __IO uint32_t   CRL;
     __IO uint32_t   CRH;
     …
};
Struct    GPIO_TypeDef    gpiox
*/

5.外部声明

extern

放在函数/变量前,表示此函数/变量在其他文件定义,以便本文件引用

应用:

	extern void KeyTask(void *params);
xTaskCreate( KeyTask, "KEYTask", 128, NULL, osPriorityNormal, NULL);

6.关键字

static:

使定义为静态变量,进而不能被其他文件调用,适合大工程防重复作用域

static void ModeShow_Task(void *params)//界面显示任务

__attribute__

 在程序中,当需要指定某个变量的内存地址时,MDK 提供了一个关键字“__attribute__”实现该功能,这种用法通常也是为了把变量指定到外部扩展的存储器,而 sct 文件存储器管理取代或改进了这种地址分配方式。在此处先补充一下关键字“__attribute__”的使用说明

1 /* 定义一个要指定的地址 */
2 #define USER_ADDR ((uint32_t)0x20005000)
3 /* 使用 atribute 指定该变量存储到 USER_ADDR, 这种方式必须定义成全局变量 */
4 uint8_t testValue __attribute__((at(USER_ADDR)));
5 testValue = 0xDD;

这种方式使用“__attribute__((at()))”来指定变量的地址,代码中指定 testValue 存储USER_ADDR地址 0x20005000 中0

volatile:

防止被编译优化

volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错

系统将总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。精确地说就是,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问

const 定义来代替宏

03 代码规范

一般的开发团队,总是会C 语言代码规范和风格做出约束,方便代码维护和团队协助

代码规范整体

需要做到以下几点:
1 、简单、明了、清晰:
代码写出来重点是给人看的,因此简单、明了、清晰是第一要务
2 、精简
代码越长越难看懂,变量等一定要及时的清理掉!
3 、保持第三方代码风格
司内部代码风格必须做到统一,方便维护,如果有第三方代码允许两种风格并存
4 、减少封装
切忌对第三方代码库进行再封装,不要为了让第三方代码和我们的风格统一,而去修改第三方源码风格,或者重新写一套接口函数以便和我们代码风格统一。不做这个封装的话虽然影响到了代码风格的统一,但是却给学习者减少了困惑

排版格式和注释

代码缩进

统一规定:TAB 键为 4 个字符

1.在一些关键字后面要添加空格,如ifswichcasefordowhile

但是不要在 sizeof typedof alignof 或者 __attribute__ 这些关键字后面添加空格,因为这些
大多数后面都会跟着小括号,因此看起来像个函数
如:
s = sizeof(struct file);
2. 如果要定义指针类型,或者函数返回指针类型时,“ *”应该放到靠近变量名的一侧,而不是类型名,如:
char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);

3.基于二元或三元对象操作符两侧都要有一个空格,例如下面所示操作符:

= + - < > * / % | & ^ <= >= == != ? :

4..一元操作符后不要加空格,自加或者自减的一元操作符、“.”和“->”这两个结构体成员操作符也是一样,如:

& * + - ~ ! sizeof typeof alignof __attribute__ defined ++ --
“.”和“->”

5.注释符“/*”和“*/”与注释内容之间要添加一个空格

6.逗号、分号只在后面添加空格,如:

int a, b, c;
以上 举例如下:
规范的写法:

void test_error(datax *p, int num, char baseval)
{
    int x1, x2;
    int t = 0;
     x1 = 32; 
    x2 = 23;
    for (t = 0; t <= num; t++) /* 循环赋值 */
    {
     datax->buf[t] = x1 * (x2 + t) + baseval;
    }
}

代码行相关规范
每一行的代码长度限制在 80 列。如果大于 80 列的话就要分成多行编写,并且在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要适当进行缩进与上一行代码对齐,如下所示:
1. 相对独立的程序块之间、变量说明之后,必须加空行。函数之间,必须加空行
if ((taskno < max_act_task_number)
    && (n7stat_stat_item_valid (stat_item)))
{
        ... /* program code */
}

report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)
                        && (n7stat_stat_item_valid (stat_item))
                        && (act_task_table[taskno].result_data != 0));
2.不要把多个语句放到一行里面,一行只写一条语句,不要在一行里面放置多个赋值语句。如下所示:
应改为:
a=x+y;
b=x-y;

3.if、fordowhilecaseswichdefault 等语句单独占用一行。且 iffordowhile 等 语句的执行语句部分无论多少都要加括号{},当且仅当 while 后为空,可以不加{}

不规范的写法: 
if (p_gpiox->IDR & pinx) return 1; /* pinx 的状态为 1 */
else return 0;                     /* pinx 的状态为 0 */

应改为:
if (p_gpiox->IDR & pinx)
{
    return 1;    /* pinx 的状态为 1 */
}
else
{
     return 0;   /* pinx 的状态为 0 */
}
当 while 语句没有代码的时候,可以不用加“ {} ”,但是,只要 while 有哪怕 1 条语句,就
必须加“ {} ”如下所示:
不规范的写法:
while (((RCC->CR & (1 << 17)) == 0) && (retry < 0X7FFF)){ retry++; }

应改为:
while (((RCC->CR & (1 << 17)) == 0) && (retry < 0X7FFF))
{
    retry++;
}

//while 后面没有代码的时候,可以不要“{}”
while ((QUADSPI->SR & (1 << 1)) == 0); /* 等待指令发送完成 */

对于单片机开发来说,左括号“ { ”一律新起一行, 且位于程序块开始的同一列

注释风格

让别人一看你的代码就明白其中的含义和用法,但是不要过度注释,不要在函数注释 里解释代码是任何运行的,一般你的函数注释应该告诉别人代码做了什么,而代码注释才是怎么做的。
放弃使用:
// ……… 
/// ………

具体代码的注释:
/* ……… */
函数/文件说明注释格式:
/** 
* ………
* ………
* ……… 
*/
当且仅当屏蔽掉部分功能代码(以便后续调试 / 修改使用)时,可以使用 // 注释,如下:
 if (timeout == 0)
 {
    //printf("r fifo time out\r\n");
    SDMMC1->ICR = 0X1FE00FFF; /* 清除所有标记 */
     sys_intx_enable(); /* 开启总中断 */
     return SD_DATA_TIMEOUT;
 }
文件信息注释
在文件开始的地方应该对本文件做一个总体的、概括性的注释,比如:文件名( @file )、
作者( @author )、当前版本( @version )、修改日期( data )、简要说明( @brief )、版权声明
@copyright )和注意事项( @attention )等,注意事项包括:平台、网站、版本修改等内容。
具体风格如下:
/**
****************************************************************************** 
* @file delay.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2020-03-12
* @brief 串口初始化代码(一般是串口 1)
* @copyright Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
******************************************************************************
* @attention
*
* 实验平台:正点原子 STM32H750 开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20200312
* 第一次发布
*
******************************************************************************
*/
#include "delay.h"
函数的注释
/**
* @brief GPIO 通用设置
* @param p_gpiox: GPIOA~GPIOK, GPIO 指针
* @param pinx: 0X0000~0XFFFF, 引脚位置, 每个位代表一个 IO, 
*        第 0 位代表 Px0, 第 1 位代表 Px1, 依次类推.
*        比如 0X0101, 代表同时设置 Px0 和 Px8.
* @arg SYS_GPIO_PIN0~SYS_GPIO_PIN15, 1<<0 ~ 1<<15
*
* @param mode: 0~3; 模式选择, 设置如下:
* @arg SYS_GPIO_MODE_IN, 0, 输入模式(系统复位默认状态)
* @arg SYS_GPIO_MODE_OUT, 1, 输出模式
* @arg SYS_GPIO_MODE_AF, 2, 复用功能模式
* @arg SYS_GPIO_MODE_AIN, 3, 模拟输入模式
*
* @param otype:0~3; 输出类型选择, 设置如下:
*   @arg SYS_GPIO_MODE_IN, 0, 输入模式(系统复位默认状态)
*   @arg SYS_GPIO_MODE_OUT, 1, 输出模式
*   @arg SYS_GPIO_MODE_AF, 2, 复用功能模式
*   @arg SYS_GPIO_MODE_AIN, 3, 模拟输入模式
*
* @param ospeed:0~3; 输出速度, 设置如下:
*   @arg SYS_GPIO_SPEED_LOW, 0, 低速
*   @arg SYS_GPIO_SPEED_MID, 1, 中速
*   @arg SYS_GPIO_SPEED_FAST, 2, 快速
*   @arg SYS_GPIO_SPEED_HIGH, 3, 高速
*
* @param pupd:0~3: 上下拉设置, 设置如下:
*   @arg SYS_GPIO_PUPD_NONE, 0, 不带上下拉
*   @arg SYS_GPIO_PUPD_PU, 1, 上拉
*   @arg SYS_GPIO_PUPD_PD, 2, 下拉
*   @arg SYS_GPIO_PUPD_RES, 3, 保留
*
* @note: 注意: 在输入模式(普通输入/模拟输入)下, OTYPE 和 OSPEED 参数无效!!
*@retval 无
*/
void sys_gpio_set(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint32_t mode, uint32_t otype, 
uint32_t ospeed, uint32_t pupd)
{
    uint32_t pinpos = 0, pos = 0, curpin = 0;
    …… /* 省略代码 */
}
代码注释
void sys_stm32_clock_init(uint32_t plln, uint32_t pllm, uint32_t pllp, uint32_t pllq)
{
    RCC->CR = 0x00000001; /* 设置 HISON, 开启 RC 振荡,其他位全清零 */
    RCC->CFGR = 0x00000000; /* CFGR 清零 */
    RCC->D1CFGR = 0x00000000; /* D1CFGR 清零 */
    RCC->D2CFGR = 0x00000000; /* D2CFGR 清零 */
    RCC->D3CFGR = 0x00000000; /* D3CFGR 清零 */
    RCC->PLLCKSELR = 0x00000000; /* PLLCKSELR 清零 */
    RCC->PLLCFGR = 0x00000000; /* PLLCFGR 清零 */
    RCC->CIER = 0x00000000; /* CIER 清零, 禁止所有 RCC 相关中断 */

命名规则

变量、函数命名

我们使用大部分软件工程师常用的命名方式( unix 风格):单词用小写,然后每个单词用下划线“_ ”连接在一起,比如: read_adc1_value()
注意事项:
1 、命名一定要清晰!清晰是首位,要使用完整的单词或者大家都知道的缩写,让别人一读
就懂,避免不必要的误会,比如:
int book_number;
2.除了常用的缩写以外,不要使用单词缩写,更不要用汉语拼音!!!
具有互斥意义的变量或者动作相反的函数应该是用互斥词组命名,如:
add/remove   begin/end           create/destroy      insert/delete 
first/last   get/release         increment/decrement put/get add/delete
lock/unlock  open/close          min/max             old/new 
start/stop   next/previous       source/target       show/hide 
send/receive source/destination  copy/paste          up/down

变量命名可以使用 g_p_开头,来表示该变量是一个:全局变量、指针等

u8u16u32 等不再使用,统一改成更为规范的简写:

int8_t /* 8 位有符号 char 型 */
int16_t /* 16 位有符号 short 型 */
int32_t /* 32 位有符号 int 型 */
int64_t /* 64 位有符号 long long 型(对 stm32 来说) */
uint8_t /* 8 位无符号 unsigned char 型 */
uint16_t /* 16 位无符号 unsigned short 型 */
uint32_t /* 32 位无符号 unsigned int 型 */
uint64_t /* 64 位无符号 unsigned long long 型(对 stm32 来说) */

文件命名

文件统一采用小写命名

宏命名

常量宏和枚举标签,一般采用大写定义,特殊情况下可以用小写,对于数值等常量宏定义的命名,如非特殊情况,一般使用大写,单词之间使用下划线“_”连接在一起,比如:

#define PI_ROUNDED 3.14

函数功能

函数要简短而且漂亮、并且只能完成一件事,函数的本地变量数量最好不超过 5-10 个,函数要注意的事项如下:
1 、一个函数只能完成一个功能
如果一个函数实现多个功能的话将会给开发、使用和维护带来很大的困难,因此,在跟函数无关或者关联很弱的代码不要放到函数里面,否则的话会导致函数职责不明确,难以理解和修改.
2 、重复代码提炼成函数
重复的代码给人的直观感受就是啰嗦,明明说一遍别人就能记住的事情,非要说好几遍! 因此一定要消除重复代码,将其提炼成函数。
3 、不同函数用空行隔开
不同的函数使用空行隔开,如果函数需要导出的话,它的 EXPORT* 宏应该紧贴在他的结束
大括号下,比如:
int system_is_up(void)
{
    return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);
4 、函数集中退出方法
如果不需要清理操作的话就直接使用 return 即可
5 、函数嵌套不能过深,新增函数最好不超过 4
函数嵌套深度指的是函数中的代码控制块 ( 例如: if for while switch ) 之间互相包含的深度,嵌套会增加阅读代码时候的脑力,嵌套太深非常不利于阅读!
6 、对函数的参数做合法性检查
函数要对其参数做合法性的检查,比如参数如果有指针类型数据的话如果不做检查,当传入野指针的话就会出错。比如参数的范围不做检查的话可能会传递进来一个超出范围的参数值
7 、对函数的错误返回要做全面的处理
一个函数一般都会提供一些指示错误发生的方法,一般都用返回值来表示,因此我们必须对这些错误标识做处理

 8、源文件范围内定义和声明的函数,除非外部可见,否则都应该用 static 函数
如果一个函数只在同一个文件的其它地方调用,那么就应该用 static,static 确保这个函数只在声明它的文件是可见的,这样可以避免和其它库中相同标识符的函数或变量发生混淆

 变量功能

1 、一个变量只能有一个功能,不能把一个变量当作多用途
一个变量只能有一个特定功能,不能把一个变量作为多用途使用,即一个变量取值不同的
时候其代表的含义也不同,如:
错误示范:
int time;
time = 200; /* 表示时间 */
time = getvalue(); /* 用作返回值 */
上述代码中变量 time 用作时间,但是也用作了函数 getvalue 的返回值。应该改为如下:
int time,ret;
time = 200;
ret = getvalue();
2 、不用或者少用全局变量
单个文件内可以使用 static 修饰的全局变量,这可以为文件的私有变量,全局变量应该是
模块的私有数据,不能作用对外的接口,使用 static 类型的定义,可以防止外部文件对本文件的
全局变量的非正常访问。直接访问其它模块的私有数据,会增强模块之间的耦合关系。
局部变量和全局变量重名会容易使人误解
3 、严禁使用未经初始化的变量作为右值
如果使用变量作为右值的话,在使用前一定要初始化变量,禁止使用未经初始化的变量作
为右值,而且在首次使用前初始化变量的地方离使用的地方越近越好!未初始化变量是 C
C++ 最常见的错误源,因此在变量首次使用前一定要确保正确初始化,如:
/* 不可取的初始化:无意义 * /
int num = 2;
if(a)
{
num = 3;
}
else
{
num=4
}
/* 不可取的初始化:初始化和声明分离 */
int num;
if(a)
{
num = 3;
}
else
{
num=4
}
/* 较好的初始化:使用默认有意义的初始化 */
int num = 3;
if(a)
{
num = 4;
}
/* 较好的初始化:?:减少数据流和控制流的混合 */
int num=a?4:3;
5 、明确全局变量的初始化顺序
系统启动阶段,使用全局变量前,要考虑到全局变量该在什么地方初始化!使用全局变量
和初始化全局变量之间的时序关系一定要分析清楚!
6 、尽量减少不必要的数据类型转换
进行数据类型转换的时候,其数据的意义、转换后的取值等都有可能发生变化,因此尽量
减少不必要的数据类型转换。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2247298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part1

资料来自李宏毅老师《生成式 AI》课程&#xff0c;如有侵权请通知下线 Introduction to Generative AI 2024 Spring 该文档主要介绍了国立台湾大学&#xff08;NTU&#xff09;2024 年春季 “生成式人工智能&#xff08;GenAI&#xff09;” 课程的作业 5&#xff08;GenAI HW…

ZYNQ-7020嵌入式系统学习笔记(1)——使用ARM核配置UART发送Helloworld

本工程实现调用ZYNQ-7000的内部ARM处理器&#xff0c;通过UART给电脑发送字符串。 硬件&#xff1a;正点原子领航者-7020 开发平台&#xff1a;Vivado 2018、 SDK 1 Vivado部分操作 1.1 新建工程 设置工程名&#xff0c;选择芯片型号。 1.2 添加和配置PS IP 点击IP INTEGR…

JSONCPP 数据解析与序列化

常用类接口 Json::Value 类 用于存储 JSON 数据的核心类。它支持将数据解析为对象、数组或基本类型&#xff08;如字符串、数值等&#xff09; 赋值操作符&#xff1a;Value& operator(Value other); 用于将一个 JSON 值赋给另一个 JSON 值 Json::Value value; value &…

排序(Java数据结构)

1. 排序的概念及引用 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。(所有的排序都是默认从小到大排序) 稳定性&#xff1a;假定在待排序的记录序列中&#xff…

JavaParser 的全面介绍

JavaParser 是什么&#xff1f; JavaParser 的快速介绍可以参考&#xff1a; # JavaParser的快速介绍 JavaParser是一个用于解析Java源码的开源工具&#xff0c;它提供了一种简单而有效的方式来解析和操作Java代码。JavaParser解析源码的方式主要基于其将Java代码转换为抽象语…

【君正T31开发记录】8.了解rtsp协议及设计模式

前边搞定了驱动&#xff0c;先不着急直接上手撸应用层的代码&#xff0c;先了解一下大致要用到的东西。 设计PC端先用vlc rtsp暂时H264编码&#xff08;vlc好像不支持h265,这个后边我试试&#xff09;的视频流&#xff0c;先需要支持上rtsp server&#xff0c;了解rtsp协议是必…

大数据新视界 -- Hive 数据分区:精细化管理的艺术与实践(上)(7/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

strlwr(arr);的模拟实现(c基础)

hi , I am 36 适合对象c语言初学者 strlwr(arr)&#xff1b;函数是把arr数组变为小写字母,并返回arr 链接介绍一下strlwr(arr)&#xff1b;(c基础)-CSDN博客 下面进行My__strlwr(arr);模拟实现 #include<stdio.h> //返回值为arr(地址),于是用指针变量,原数组为字符型…

【Pytest+Yaml+Allure】实现接口自动化测试框架

一、框架思想 requestsyamlpytestallure实现接口自动化框架。结合数据驱动和分层思想&#xff0c;将代码与数据分离&#xff0c;易维护&#xff0c;易上手。使用yaml编写编写测试用例&#xff0c;利用requests库发送请求&#xff0c;使用pytest管理用例&#xff0c;allure生成…

Sigrity SPEED2000 General SI Simulation模式如何进行信号仿真分析操作指导-DDR

Sigrity SPEED2000 General SI Simulation模式如何进行信号仿真分析操作指导-DDR Sigrity SPEED2000 General SI Simulation模式可以对信号进行仿真分析,仿真将电源视为理想电源,以下图为例进行说明 附带驱动和接收端的IBIS模型 2D 视图

python中如何使用指数

exp()方法&#xff1a; exp(x)方法返回x的指数&#xff0c;e^x。 如x1&#xff0c;那么e的1次幂为2.7183… 语法&#xff1a; 注意&#xff1a;exp()是不能直接访问的&#xff0c;需要导入math模块&#xff0c;通过静态对象调用该方法。 实例&#xff1a; 运行结果&#xf…

AutoDL安装docker问题

在AutoDL上租了卡&#xff0c;安装docker遇到一些问题&#xff1a; 1.执行 sudo docker run hello-world 报错 docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? 解决方法 先查看docker有没有启动&#xff0c;…

基于SSM的婚庆管理系统+LW示例参考

1.项目介绍 系统角色&#xff1a;管理员、商家&#xff08;婚庆公司&#xff09;、用户功能模块&#xff1a;管理员&#xff08;用户管理、商家管理、摄影风格管理、礼服款式管理、案例管理、婚车品牌管理、婚纱拍摄管理、策划服务管理、婚宴酒店管理、婚车套餐管理、在线咨询…

Linux入门系列--文件与目录

一、介绍 在Linux中&#xff0c;有着一句话&#xff0c;叫做&#xff1a;一切皆文件。也就是任何东西都是以文件的形式存储的。 目录结构 bin&#xff1a;全程binary&#xff0c;含义是二进制。该目录中存储的都是一些二进制文件。我们学过C/C&#xff0c;其实也都知道机器能…

Linux离线安装Docker命令,简单镜像操作

解压安装包 首先&#xff0c;使用 tar 命令解压 docker-27.3.1.tgz 安装包&#xff1a; tar -zxvf docker-27.3.1.tgz 将二进制文件移动到可执行路径上的目录 接着&#xff0c;将解压出来的 Docker 二进制文件复制到系统的可执行路径&#xff08;通常是 /usr/bin/&#xff09…

鸿蒙主流路由详解

鸿蒙主流路由详解 Navigation Navigation更适合于一次开发,多端部署,也是官方主流推荐的一种路由控制方式,但是,使用起来入侵耦合度高,所以,一般会使用HMRouter,这也是官方主流推荐的路由 Navigation官网地址 个人源码地址 路由跳转 第一步-定义路由栈 Provide(PageInfo) pag…

harbor和docker配置https访问

如果配置了科学上网代理&#xff0c;一定要做免代理的配置&#xff0c;不然https访问会失败。 免代理配置 Docker免代理配置 [rootnode1 harbor]#cat /etc/systemd/system/docker.service.d/http-proxy.conf [Service] Environment"HTTP_PROXYhttp://10.0.0.1:7897"…

SageMaker强化学习部署

强化训练 强化学习的训练与一般的深度学习不太一样。需要有一个环境&#xff0c;对智能体的动作&#xff0c;给予一个奖励并进行状态转移。用于训练的数据&#xff0c;是在训练的过程中产生的。 这里以一个小车上坡的强化学习作为例子&#xff0c;相关文档在下方 https://sa…

MySQL面试-1

InnoDB中ACID的实现 先说一下原子性是怎么实现的。 事务要么失败&#xff0c;要么成功&#xff0c;不能做一半。聪明的InnoDB&#xff0c;在干活儿之前&#xff0c;先将要做的事情记录到一个叫undo log的日志文件中&#xff0c;如果失败了或者主动rollback&#xff0c;就可以通…

使用树莓派安装shairport-sync使老音响变身AirPlay音响

借助shairport-sync&#xff0c;可以让普通音响变成AirPlay无线音响&#xff0c;由于树莓派天生的低功耗&#xff0c;做这种事情最适合。所以架构就是树莓派安装Ubuntu24.04&#xff0c;在树莓派上安装shairport-sync&#xff0c;树莓派再通过3.5mm线连接音响。 安装Ubuntu24.…