一、static
关键字static,修饰变量时:
1、限制该变量的作用域:比如静态全局变量,只能在该模块中使用(本c文件中)。
2、决定该变量的存储位置:修饰为静态的变量,存储在静态数据区(非堆栈内)。(同比,全局变量也都存放在静态数据区中。)
带初值和不带初值的静态变量:(以TI DSP 54XX为例)
a.不带初值的静态变量,存储在.bss段中。
b.带初值的静态变量,存储在.bss段中,但其初值存储在.init中。在DSP Booter完成加载初始化后已经将init段的值赋值到.bss对应变量中。
关键字static,修饰函数时:限制该函数的作用域,仅能在本模块中使用(本c文件中)。
结论:static关键字对于使用者而言,最重要的是对变量或函数作用域的限制。
二、const
关键字const意味着“不变的”:
1、什么时候该用:定义一个函数时如果在输入参数前加上const,可以防止该输入变量被改写而引出的错误;函数定义时使用const可以起到函数API自注释作用;
2、怎么用:
以下摘录经典面试题中的例子:
const int a; int const a; const int *a; int * const a; int const * a const;
前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
三、volatile
关键字volatile意味着“易改变的”:
修饰为volatile的变量,编译器不会对其进行任何优化,每次求值时会到指定地址(物理或映射)去读取。因此适用于“并行设备的硬件寄存器(如:状态寄存器) ;一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) ;多线程应用中被几个任务共享的变量 ”,我个人只用到在IO和中断中。
四、堆与栈(heap and stack)
1) 堆是由用户维护的。比如我们malloc一段空间,此时开辟的空间便在堆中,调用free时再释放。过于频繁的malloc与free会导致堆中产生碎片(一个空间的地址不连续),影响读写速度。
2) 栈是由编译器控制的,我们无需干涉。比如我们定义的局部变量或是调用了一个函数。以函数调用为例,此时系统会将现场数据(主要是部分寄存器中的数据,如PC的保存可以在调用结束时程序回归此处继续运行)压入栈内,将寄存器空出迎接被调函数中的一系列计算和控制。被调函数执行完后再进行出栈操作,流程继续。
栈与队列的区别:
栈——先入后出,后入先出;
队列——先入先出,后入后出;
应用举例:
栈:函数调用时会将临时数据压栈;函数返回时再弹出来。
队列:一般,系统中的任务和消息经常使用队列。可以按任务或消息到来的先后顺序执行。
代码实现:
背景:使用C语言,在VS2008环境下,按栈与队列的原理,采用最简单易懂的方式,针对正整型(int)元素实现栈与队列。
原理:栈的存储空间使用数组构造,队列的空间又使用栈来构造,即,使用两个栈实现队列的功能。
文件:libStack.h/c;libQueue.h/c;
一、栈的源代码
libStack.h
#ifndef _LIBSTACK_H
#define _LIBSTACK_H
#ifdef __cplusplus
extern "C" {
#endif
#define STACK_MAX_LEN 2048
typedef struct tagstStack
{
int udwPc; /* 栈顶所在位置,指向空闲位置 */
int a[STACK_MAX_LEN];
}stStack;
/* 创建新的栈 */
extern stStack *stack_new();
/* 栈复位 */
extern void stack_reset(stStack *pstStack);
/* 入栈,栈满时忽略此次操作 */
extern void stack_push(stStack *pstStack,int udwTmp);
/* 出栈,栈空时返回-1 */
extern int stack_pop(stStack *pstStack);
/* 栈判空 1-空 0-非空 */
extern int stack_is_empty(stStack *pstStack);
/* 删除栈 */
extern void stack_delete(stStack *pstStack);
#ifdef __cplusplus
}
#endif
#endif
由于是C语言实现,所以必须添加extern "C"{};
libStack.c
#ifdef __cplusplus
extern "C" {
#endif
#include <malloc.h>
#include <stddef.h>
#include "libStack.h"
/* 创建新的栈 */
stStack *stack_new()
{
stStack *p;
p = (stStack *)malloc(sizeof(stStack));
p->udwPc = 0;
return p;
}
/* 栈复位 */
void stack_reset(stStack *pstStack)
{
if (NULL == pstStack)
{
return;
}
pstStack->udwPc = 0;
}
/* 入栈,栈满时忽略此次操作 */
void stack_push(stStack *pstStack, int udwTmp)
{
if (NULL == pstStack)
{
return;
}
if (STACK_MAX_LEN > pstStack->udwPc)
{
pstStack->a[pstStack->udwPc] = udwTmp;
pstStack->udwPc++;
}
}
/* 出栈,栈空时返回-1 */
int stack_pop(stStack *pstStack)
{
if (NULL == pstStack)
{
return -1;
}
if (0 == pstStack->udwPc)
{
return -1;
}
return pstStack->a[--pstStack->udwPc];
}
/* 栈判空 1-空 0-非空 */
int stack_is_empty(stStack *pstStack)
{
if (NULL == pstStack)
{
/* 空指针默认为空栈 */
return 1;
}
return (0 < pstStack->udwPc)?0:1;
}
/* 删除栈 */
void stack_delete(stStack *pstStack)
{
if (NULL != pstStack)
{
free(pstStack);
}
}
#ifdef __cplusplus
}
#endif
本套实现只支持正整型数,负数被作为异常值处理。
二、队列的实现
这里队列完全是用上述栈实现的,目的只是练习。
libQueue.h
#ifndef _LIBQUEUE_H
#define _LIBQUEUE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "libStack.h"
#define QUEUE_MAX_LEN STACK_MAX_LEN /* 队列大小取决与栈的大小 */
typedef struct tagstQueue
{
int udwCnt;
stStack *pstStack1;
stStack *pstStack2;
}stQueue;
/* 创建新队列 */
extern stQueue *queue_new();
/* 入队,队列已满时忽略此次操作 */
extern void in_queue(stQueue *pstQueue, int udwTmp);
/* 出队,队列为空时返回-1 */
extern int out_queue(stQueue *pstQueue);
/* 删除队列 */
extern void queue_delete(stQueue *pstQueue);
/* 队列判空 1-空 0-非空 */
extern int queue_is_empty(stQueue *pstQueue);
#ifdef __cplusplus
}
#endif
#endif
libQueue.c
#ifdef __cplusplus
extern "C" {
#endif
#include <malloc.h>
#include <stddef.h>
#include "libQueue.h"
/* 创建新队列 */
stQueue *queue_new()
{
stQueue *p;
p = (stQueue *)malloc(sizeof(stQueue));
if (NULL == p)
{
return NULL;
}
p->pstStack1 = stack_new();
if (NULL == p->pstStack1)
{
free(p);
return NULL;
}
p->pstStack2 = stack_new();
if (NULL == p->pstStack2)
{
free(p);
free(p->pstStack1);
return NULL;
}
p->udwCnt = 0;
return p;
}
/* 入队,队列已满时忽略此次操作 */
void in_queue(stQueue *pstQueue, int udwTmp)
{
if (NULL == pstQueue)
{
return;
}
if (QUEUE_MAX_LEN <= pstQueue->udwCnt)
{
return;
}
stack_push(pstQueue->pstStack1, udwTmp);
pstQueue->udwCnt++;
}
/* 出队,队列为空时返回-1 */
int out_queue(stQueue *pstQueue)
{
int ret = -1;
int tmp = 0;
if (NULL == pstQueue)
{
return -1;
}
/* 队列为空 */
if (stack_is_empty(pstQueue->pstStack1))
{
return -1;
}
/* 将栈1内容弹出并压入栈2 */
while(0 != pstQueue->pstStack1->udwPc)
{
tmp = stack_pop(pstQueue->pstStack1);
stack_push(pstQueue->pstStack2, tmp);
}
/* 取栈2顶元素出队 */
ret = stack_pop(pstQueue->pstStack2);
/* 将栈2内容压回栈1 */
while(0 != pstQueue->pstStack2->udwPc)
{
tmp = stack_pop(pstQueue->pstStack2);
stack_push(pstQueue->pstStack1, tmp);
}
pstQueue->udwCnt--;
return ret;
}
/* 删除队列 */
void queue_delete(stQueue *pstQueue)
{
if (NULL == pstQueue)
{
return;
}
if (NULL != pstQueue->pstStack2)
{
free(pstQueue->pstStack2);
}
if (NULL != pstQueue->pstStack1)
{
free(pstQueue->pstStack1);
}
free(pstQueue);
}
/* 队列判空 1-空 0-非空 */
int queue_is_empty(stQueue *pstQueue)
{
return (0 < pstQueue->udwCnt)?0:1;
}
#ifdef __cplusplus
}
#endif
三、简单的测试函数
这里我只写了队列的测试:
// ListStackQueue.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "libStack.h"
#include "libQueue.h"
int _tmain(int argc, _TCHAR* argv[])
{
int i,tmp;
stQueue *pstQ1 = queue_new();
stQueue *pstQ2 = queue_new();
for (i = 0; i < 20; i++)
{
in_queue(pstQ1,i);
}
for (i = 0; i < 20; i++)
{
tmp = out_queue(pstQ1);
printf("%d ",tmp);
}
printf("OK");
getchar();
return 0;
}
输出为:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 OK;实现了队列的先入先出功能。
如果试着将栈的大小调整为5,输出为:0 1 2 3 4 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 OK。满足超出空间上限不压栈不入队,出栈出队时若栈队为空则返回-1。
C语言作为面向过程的语言,想写出灵活的结构与封装需要很高技巧。
但由于C语言的高效,几乎所有操作系统和面向对象语言的最底层实现都使用了C语言。即,使用C完成面向对象的封装。
这次通过整理与仿写Linux的双向链表让我体会到了一些C语言封装的核心技巧。
这个双向链表的巧妙之处在于1)利用宏将“函数”入参扩展出了“结构类型”; 2)利用纯地址偏移获取结构体指针;
下面是具体实现:链表的实现由于是宏定义,都在libList.h中。
测试程序testList.c与testList.h。
libList.h
#ifndef _LIBLIST_H
#define _LIBLIST_H
#ifdef __cplusplus
extern "C" {
#endif
/* 链表头节点定义 */
typedef struct tagstList_Head {
struct tagstList_Head *next, *prev;
}stList_Head;
/* 初始化双向链表 */
#define list_init(head) do \
{ \
(head)->next = (head)->prev = (head); \
} while(0)
/* 在指定元素(where)之后插入新元素(item) */
#define list_add(item, towhere) do \
{ \
(item)->next = (towhere)->next; \
(item)->prev = (towhere); \
(towhere)->next = (item); \
(item)->next->prev = (item); \
} while(0)
/* 在指定元素(where)之前插入新元素(item) */
#define list_add_before(item, towhere) \
list_add(item,(towhere)->prev)
/* 删除某个元素 */
#define list_remove(item) do \
{ \
(item)->prev->next = (item)->next; \
(item)->next->prev = (item)->prev; \
} while(0)
/* 正向遍历链表中所有元素 */
#define list_for_each_item(item, head)\
for ((item) = (head)->next; (item) != (head); (item) = (item)->next)
/* 反向遍历链表中所有元素 */
#define list_for_each_item_rev(item, head) \
for ((item) = (head)->prev; (item) != (head); (item) = (item)->prev)
/* 根据本节点(item)获取节点所在结构体(type)的地址 */
/* 节点item地址(member的地址) - 该链表元素member在结构体中的偏移 */
#define list_entry(item, type, member) \
((type *)((char *)item - (char *)(&((type *)0)->member)))
/* 判断链表是否为空 */
#define list_is_empty(head) \
((head)->next == (head))
/* 获取指定位置上一元素 */
#define list_prev_item(item)\
((head)->prev)
#ifdef __cplusplus
}
#endif
#endif
testList.h
#ifndef _TESTLIST_H
#define _TESTLIST_H
#ifdef __cplusplus
extern "C" {
#endif
#include "libList.h"
typedef struct tagInteger
{
int idx;
stList_Head stListHead;
}stInteger;
extern void testlist();
#ifdef __cplusplus
}
#endif
#endif
这种双向链表的特点在于:在需要链表管理的结构中定义一个链表节点变量,然后所有的操作都是针对这个链表节点进行的。在需要获取该结构时使用list_entry即可。
testList.c
#ifdef __cplusplus
extern "C" {
#endif
#include <malloc.h>
#include <stddef.h>
#include "stdafx.h"
#include "testList.h"
void testlist()
{
int i = 0;
stList_Head stListHead;
stList_Head *pstListHead= NULL;
stInteger *pstInteger = NULL;
stList_Head *pstTmpList = NULL;
/* 链表初始化 */
pstListHead = &stListHead;
list_init(pstListHead);
/* 生成节点并挂入链表 */
for(i = 0; i < 32; i++)
{
pstInteger = (stInteger *)malloc(sizeof(stInteger));
if (NULL == pstInteger)
{
printf("pstInteger malloc fail!");
return;
}
pstInteger->idx = 100 - i;
/* 挂到头结点之前 */
list_add_before(&pstInteger->stListHead, pstListHead);
/* 挂到头结点之后 */
//list_add(&pstInteger->stListHead, pstListHead);
}
/* 正向遍历链表中每一个元素并输出 */
list_for_each_item(pstTmpList, pstListHead)
{
if(NULL != pstTmpList)
{
pstInteger = list_entry(pstTmpList, stInteger, stListHead);
printf("%d ",pstInteger->idx);
}
}
return;
}
#ifdef __cplusplus
{
#endif
刚刚成立的新群linux+arm+dsp+pcb+fuck交流群,欢迎各位fuck友加入。 群号211670713 群号211670713