最近在开发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修饰的局部变量、全局变量、函数都会存放在静态区中。
-
修饰局部变量(一次初始化)
当用static修饰局部变量的定义时,多次执行时只会执行一次。但是数值是可以改变的。 -
static修饰全局变量(像私有变量)
当static修饰全局变量时,具有修饰局部变量的性质,修饰的全局变量只能在被定义的源文件中使用,而不能用extern在不同的源文件中使用了,使其没有了外部链接,只有内部链接。 -
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
- 在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表。
void foo(...);
void foo(parm_list,...);
- 函数参数的传递原理
函数参数是以栈的形式存取,从右至左入栈。
参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
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 中的字符。