c语言基础(一)
开个头:嘿嘿,最近终于有的闲,好好整理一下码农的工具箱,收拢一下一些基础知识吧,go go go ............
——————————————————————————————————
目录
1、结构体字节对齐
2、大端小端
3、结构体相关的一些有意思的宏
4、函数指针的使用
——————————————————————————————————
1、结构体字节对齐
1)系统相关
char short int long long long float double
32位 1 2 4 4 8 4 8
64位 1 2 4 8 8 4 8
2)如何指定字节对齐
#pragmapack(2)//编译器命令,按照2个字节对齐
..................................................
..................................................
#pragmapack()//取消指定对齐,恢复缺省的对齐
3)空结构体
sizeof(空结构) gcc为0 g++为1
4)编译选项
-fpack-struct
5)位域
struct
{
unsigned char ucJust :4;
unsigned char ucKidding:3;
unsigned char ucTest :3;
unsigned char ucBit :4;
}SB;
sizeof(SB)为 2
2、大端小端
大端:数的低位存在高地址中,高位存在低地址中 (大-低高高低);
小端:数的低位存在低地址中,高位存在高地址中 (小-低低高高);
网络字节序:大端
1)如何判断机器是大端还是小端?
int IsThisABigEndianCookie()
{
short sCookie = 0x1234;
char *pCookie = (char *)(&sCookie);
if(*pCookie == 0x12)
{
return 1;
}
else
{
return 0;
}
return -1;
}
2)ip地址点分十进制的输出
int main(int argc, char* argv[])
{
int a = 0x12345678;
unsigned char *p = (unsigned char *)&a;
char buf[20]={0};
sprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[3], p[2], p[1], p[0]);
//snprintf(buf, sizeof(buf), "%d.%d.%d.%d", a >> 24, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff);
printf(“%s\n”,str);
return 0;
}
大端输出:120.86.52.18
小端输出:18.52.86.120
3)位域的大小端问题
union {
struct
{
unsigned char a1:2;
unsigned char a2:3;
unsigned char a3:3;
}x;
unsigned char b;
}d;
int main(int argc, char* argv[])
{
d.b = 100;
printf("%d %d %d\n", d.x.a1, d.x.a2, d.x.a3);
return 0;
}
小端输出: 0 1 3
大端输出: 3 1 0
4)位域结构体的一般定义形式
struct {
#ifdef BIG_ENDIAN
inta:1;
intb:2;
intc:3;
intd:4;
#else
intd:4;
intc:3;
intb:2;
inta:1;
#endif
} B;
3、结构体相关的一些有意思的宏
1)根据结构体的某一成员地址获取结构体的地址
step1:根据结构体成员地址获取该成员的offset
step2:根据上述的offset获取结构体的地址
#define OFFSET_OF(TYPE, MEM) ((long) &((TYPE *)0)->MEM)
#define MEM_TO_TYPE(pMEM, TYPE, MEM) (TYPE *)((long)pMEM - OFFSET_OF(TYPE, MEM))
应用场景:vxworks里使用这种方式设计了可以适用于任何数据结构的单双链表。
4、函数指针的使用
typedef int (*FUNCPTR)();
1)使用场景-类Strategy模式
举个例子吧,如队列的实现策略有“先入先出队列”,“优先级队列”等,如果每一种队列都对外提供一组操作接口,那上层如果要更改调用策略的话,就要把所有接口名都改了,为了达到松耦合,我们设计一组统一的操作接口,接口实际调用了具体类型的处理函数,由上层在使用时指定队列类型(动态绑定),然后调用,如果上层要更改策略,则把队列类型改了就行,用结构体打包一组接口(函数指针类型),把该打包的地址提供给外围使用。
//打包接口的类型
typedef struct qClass
{
(FUNCPTR)qCreate;
(FUNCPTR)qInit;
(FUNCPTR)qDelete;
(FUNCPTR)qInsertRtn;
(FUNCPTR)qPut;
(FUNCPTR)qGet;
(FUNCPTR)qRemove;
}Q_CLASS;
//具体的实现类型-fifo队列
LOCAL Q_CLASS qFifoClass =
{
(FUNCPTR)qFifoCreate,
(FUNCPTR)qFifoInit,
(FUNCPTR)qFifoDelete,
(FUNCPTR)qFifoNullRtn,
(FUNCPTR)qFifoPut,
(FUNCPTR)qFifoGet,
(FUNCPTR)qFifoRemove
};
/* globals */
Q_CLASS_ID qFifoClassId = &qFifoClass;
//具体的实现类型-pri队列
LOCAL Q_CLASS qPriDeltaClass =
{
(FUNCPTR)qPriCreate,
(FUNCPTR)qPriInit,
(FUNCPTR)qPriDelete,
(FUNCPTR)qPriTerminate,
(FUNCPTR)qPriPut,
(FUNCPTR)qPriGet,
(FUNCPTR)qPriRemove
};
/* globals */
Q_CLASS_ID qPriClassId = &qPriClass;
2)使用场景-回调
如libc里面qsort定义
void qsort(void *base,int nelem,int width,int (*fcmp)(const void *,const void *));