单片机开发,常用的一些知识

本文介绍了STM32开发中常用的关键字,如inline用于内联函数以提高效率,volatile确保变量在多线程或中断环境中的正确读取,__packed避免结构体填充,extern声明外部变量或函数,static实现局部或全局变量的静态存储,以及const、union、register、attribute等的用途。还讨论了函数参数传递和字符串处理函数的使用。
摘要由CSDN通过智能技术生成

最近在开发STM32的应用,使用了一些关键字,觉得有必要记录一下,加深印象。这里有只是进行相关知识分享,使用别人的一些相关说明。

1、inline关键字

Inline函数,内联函数,它是把函数内部的代码直接插入到调用者代码处的函数,也就是该函数不是通过子函数调用这种常见的方式来实现的,而是直接替换到函数调用处(这点上有点类似#define宏),由此带来的优势就是省去了调用/返回指令,通过避免调用带来的开销(包括函数调用带来的额外的执行周期和堆栈的使用)来提高代码的执行效率,而又同时保留了函数封装形式带来的可读性。

使用方法

/****************函数声明部分*****************/
void delay(void);//delay函数声明,此处不用加Inline关键词
 
void main(void)
{ 
  while(1)
  {   
      testPoint = 0;
      delay();// 定义了inline的delay函数,在编译连接的时候就会直接嵌入到该行代码处,
     		  //而不是通过调用实现
  }
}
/** 注意关键字inline 必须与函数定义体放在一起才能使函数成为内联,
将inline 放在函数声明前面不起任何作用**/
inline void delay(void)
{
  uint8 i=0;
  while((i++)!=200);
}

inline内联函数内部不能用复杂的运算,这算是一个限制了吧。既然Inline函数与#define宏的功能类似,那为什么不用#define宏呢,我的回答只能说是用inline更快且不容易出错。

2、volatile关键字

当程序运行在某一个函数内读取变量时,为了提高存取速度,编译器通常会先把变量读取到一个寄存器里,寄存器操作是最快的。然后以后需要再用到这个变量的时候,就直接从寄存器里拿,而不用再通过内存地址去访问内存再取一次了。注意,只有当该变量在本函数内被改变时,寄存器里的存储才会更新,才会和这次内存的改变保持一致。而当变量在其他地方,如中断,其他线程等等,被改变的时候,这个时候寄存器里的存储是不会更新的,这个时候寄存器里的值和实际的值就不一致了。加上volatile修饰正是为了防止这种优化。

总结作用:当变量被volatile修饰时,编译器不会在用到变量时直接从寄存器里拿,而是直接去取内存地址里的值,通过这种方法取到的值一定是最新的,实际的值。

3、__packed

__packed用于表示C语言中结构体的压缩,即:没有填充和对齐。

4、extern

extern是一种“外部声明”的关键字,字面意思就是在此处声明某种变量或函数,在外部定义

5、static

static代表静态的,被static修饰的局部变量、全局变量、函数都会存放在静态区中。

  1. 修饰局部变量(一次初始化)
    当用static修饰局部变量的定义时,多次执行时只会执行一次。但是数值是可以改变的。

  2. static修饰全局变量(像私有变量)
    当static修饰全局变量时,具有修饰局部变量的性质,修饰的全局变量只能在被定义的源文件中使用,而不能用extern在不同的源文件中使用了,使其没有了外部链接,只有内部链接。

  3. static修饰函数(像私有函数)
    修饰函数时,与修饰全局变量类似,不仅具有修饰局部变量的性质,但没有了外部链接,只有内部链接,只能在被定义的源文件中被调用。

6、const

const修饰变量、数组、指针和形参功能是对变量声明为只读特性,并保护变量值以防被修改。

7、union

共用体 共用起始地址的一段内容,

8、register

限制变量定义在寄存器上的修饰符

9、attrbute

attribute ((weak)) ----》弱引用(有强引用优先编译强引用,没有则编译器弱引用函数,避免编译出错,相当于:java的接口)
attribute((aligned(8))) —》指定对齐(数据按照指定长度对齐)
attrubte ((packed)) ----》字节对齐(紧凑型,不进行对齐,变量多大就占用多大内存)
attribute((at(0x0800F000))) ----》绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到RAM。

10、va_start和va_end

  1. 在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表。
void foo(...);
void foo(parm_list,...);
  1. 函数参数的传递原理
    函数参数是以栈的形式存取,从右至左入栈。

参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:

void func(int x, float y, char z);

那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z。从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。举个例子如下


#include <stdio.h> 
 
//获取参数列表中的所有参数,并打印
void PrintInt(int cnt, ...) 
{ 
  int *temp = &cnt;
  temp++;
  for (int i = 0; i < cnt; ++i) 
  { 
    printf("%d\n", *temp); 
    temp++; 
  } 
}
 
int main(void)
{
    int a = 1; 
    int b = 2; 
    int c = 3; 
    int d = 4;
    PrintInt(4, a, b, c, d); 
    return 0;
}

执行程序后输出:

1
2
3
4

sscanf()函数

sscanf通常被用来解析并转换字符串,其格式定义灵活多变,可以实现很强大的字符串解析功能。

int sscanf(const char *str, const char *format, ...)

详细用法

vsnprintf函数

#include <stdio.h>
#include <stdarg.h>

void RTI_LOG(const char* fmt, ...)
{
	char buffer[128];
	va_list ap;
	va_start(ap,fmt);
	vsnprintf(buffer, sizeof(buffer), fmt, ap);
	printf("%s",buffer);
	va_end(ap);
}

#define fota_debug(cat3, fmt, args...)  		\
	do{									\
		RTI_LOG("[%s] %s "fmt"\r\n",__func__,cat3,##args);			\
	}while(0)

int main()
{
	int a=2,b=1;
	fota_debug("4","fota %d %d",a,b);
   	return 0;
}

strcasecmp

C语言中判断字符串是否相等的函数,忽略大小写。s1和s2中的所有字母字符在比较之前都转换为小写。该strcasecmp()函数对空终止字符串进行操作。函数的字符串参数应包含一个(’\0’)标记字符串结尾的空字符。

strchr()

用来查找某字符在字符串中首次出现的位置,其原型为:

char * strchr (const char *str, int c);

strtok

分解字符串 str 为一组字符串

char *strtok(char *str, const char *delim)

strstr()

char *strstr(const char *haystack, const char *needle)

该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

strcspn()

size_t strcspn(const char *str1, const char *str2)

检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值