平时主要还是C语言用的比较多,对C语言做一个总结吧,最基本的就不写了,把一下觉得重要的点总结一下吧。
-
static关键字
static有两种用法:
- 修饰变量,将变量放在静态区进行存储
- 修饰符号,static声明的变量名(函数名)仅能在文件内部访问,其实编译器的处理就是在编译的时候会对符号增加一个前缀,外部文件直接访问这个符号在链接的时候肯定是找不到的。要注意的是由于C语言并没有名称空间的概念,所以为了避免名称污染,在编写程序的时候应该将仅文件内部使用的函数和变量声明为static。
-
volatile
- 告诉编译器不要优化针对这个变量的访问,在嵌入式当中可能会出现你在等一个值的状态变化,但是这个值的状态变化是在中断中改变的。编译器在优化代码时发现,你等待的一个变量状态没有其他地方会改变它可能就优化掉了这个代码从而造成错误。当然了理论上是会有这个问题,但是我目前并没有遇到过,可能是现在的编译器都足够优秀了吧。
-
指针
C语言的另外一个迷惑点就是指针和数组,其实指针和数组的区别不大,都是通过一个符号指向一个内存空间。只是一个是内存空间位置可变和大小可变的(指针),一个是内存空间位置和大小不可变的(数组)。
-
define
-
宏只是代码展开,所以使用带参数的宏时最好在使用参数时都加上括号,避免在参数为表达式时出现错误。如:
#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
有意思的宏
以下代码实现了通过定义OPTION_USER_DEVICE,动态地声明函数与调用函数。
//设备函数声明 # define _DEVICE_INIT_DECLARATION(device) device##Init(void) # define DEVICE_INIT_DECLARATION(device) _DEVICE_INIT_DECLARATION(device) # define _DEVICE_POLL_DECLARATION(device) device##Poll(void) #define _CALL_DEVICE_FUNC(device, func, ...) device##func(__VA_ARGS__) #define CALL_DEVICE_FUNC(device, func, ...) _CALL_DEVICE_FUNC(device, func, __VA_ARGS__) //声明 void DEVICE_INIT_DECLARATION(OPTION_USER_DEVICE); void DEVICE_POLL_DECLARATION(OPTION_USER_DEVICE); int main(void) { //调用 CALL_DEVICE_FUNC(OPTION_USER_DEVICE, Init); CALL_DEVICE_FUNC(OPTION_USER_DEVICE, Poll); }
-
-
const
最好能习惯性地给只读的参数加上const修饰符,方便理解参数用途,如以下声明:
void strcpy(char *dst, const char *src);
-
typedef
- 将结构体定义成类型,方面使用。
typedef struct Item_st { char name[20]; int value; }Item_t;
- 定义回调函数的函数指针类型
typedef void (*Callback_t)(void); void Start(Callback cb);
-
字节序
网络或通信的数据要注意字节序的问题。
-
常用的C标准库
- stdlib(malloc, free)
- string (memcpy, strcpy)
- stdio (printf, sprintf)
位运算(|, &, ^)
-
printf
多用printf输出日志调试,一般我会在工程里定义一个Log宏
#define LOG(...) printf("%s::%s[%d]", __FILE__, __func__, __LINE__);printf(__VA_ARGS__);printf("\n")
单片机相关
-
中断处理函数
尽量不要在中断中处理耗时的任务,一般只是在中断中读出数据或置一个标志位,在主循环中再进行处理。
-
单片机的日志打印
在单片机上一般使用串口来输出日志信息,像iar或keil这类工具都支持通过printf来打印日志,只是需要实现putc,在putc中将输出转向UART。
常用数据结构
这里说说常用的数据结构,主要还是静态循环队列和链表两种。
-
静态循环队列
静态循环队列一般用于数据缓存方面,如串口通信时,在中断中读数据并将数据入队,在主循环里再从队列里将数据读出并处理。
-
链表
链表用处就很广了,一般支持3个操作,插入,删除,遍历。动态规模的数据需要存储就都可以用链表来存。
具体的这里封装了两个头文件(es_fifo.h, es_list.h),可以看一下。
es_fifo.h
#ifndef __ES_FIFO_H
#define __ES_FIFO_H
#define ES_FIFO(name) (name)
#define _ES_FIFO_SIZE(fifo) (sizeof(ES_FIFO(fifo).items) / sizeof(ES_FIFO(fifo).items[0]))
#define es_fifo_def(type, fifo, size) \
struct fifo##_st\
{ \
unsigned short front, back, count; \
type items[size]; \
}ES_FIFO(fifo)
#define es_fifo_in(fifo, item) \
do{ \
if(es_fifo_has_space(fifo)) \
{ \
ES_FIFO(fifo).items[ES_FIFO(fifo).back] = item; \
ES_FIFO(fifo).back = ES_FIFO(fifo).back + 1; \
ES_FIFO(fifo).back = ES_FIFO(fifo).back == _ES_FIFO_SIZE(fifo) ? 0 : ES_FIFO(fifo).back; \
ES_FIFO(fifo).count++; \
} \
}while(0)
#define es_fifo_has_space(fifo) (ES_FIFO(fifo).count < _ES_FIFO_SIZE(fifo))
#define es_fifo_is_empty(fifo) (ES_FIFO(fifo).count == 0)
#define es_fifo_count(fifo) ES_FIFO(fifo).count
#define es_fifo_peek(fifo) ES_FIFO(fifo).items[ES_FIFO(fifo).front]
#define es_fifo_out(fifo) \
do{ \
if(!es_fifo_is_empty(fifo)) \
{ \
ES_FIFO(fifo).front = (ES_FIFO(fifo).front + 1); \
ES_FIFO(fifo).front = ES_FIFO(fifo).front == _ES_FIFO_SIZE(fifo) ? 0 : ES_FIFO(fifo).front; \
ES_FIFO(fifo).count--; \
} \
}while(0)
#endif // __ES_FIFO_H
es_list.h
#ifndef __ES_LIST_H
#define __ES_LIST_H
#define ES_LIST_ENTRY(type) \
type *next;type *prev
#define es_list_first(list) ((list) ? (list) : NULL)
#define es_list_last(list) ((list) ? (list)->prev : NULL)
#define es_list_add(list, node) \
if(!list) { \
list = node; \
list->next = list; \
list->prev = list; \
} \
else { \
node->next = list; \
list->prev->next = node; \
node->prev = list->prev; \
list->prev = node; \
}
#define es_list_del(list, node) \
{ \
list = node == list ? (node->next == list ? NULL : node->next) : list; \
(node)->prev->next = (node)->next; \
(node)->next->prev = (node)->prev; \
}
#define ___ESLISTV(node, line) node##line
#define __ESLISTV(node, line) ___ESLISTV(node, line)
#define _ESLISTV(node) __ESLISTV(node, __LINE__)
#define es_list_foreach(list, node) \
node = list; \
void *_ESLISTV(_next) = node ? node->next : NULL; \
void *_ESLISTV(_flag) = NULL; \
void *_ESLISTV(_list) = list; \
for(; list && (node != (list) \
|| _ESLISTV(_flag) == NULL); \
node = _ESLISTV(_next), \
_ESLISTV(_next) = node->next, \
_ESLISTV(_flag) = _ESLISTV(_list) != list ? NULL : list, _ESLISTV(_list) = list)
#endif // __ES_LIST_H