C语言查漏补缺
1.C99中struct的柔性数组
// C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,
// 但结构中的柔性数组成员前面必须至少一个其他成员。
// 柔性数组成员允许结构中包含一个大小可变的数组。
// sizeof 返回的这种结构大小不包括柔性数组的内存。
typedef struct st_type
{
int i;
int a[]; // int a[0];
}type_a;
// type_a 为 typede f定义的 结构体 st_type 的别名,方便使用
// 定义一个可变长的结构体,用 sizeof(type_a)得到的只有 4,就是 sizeof(i)=sizeof(int)。
// 给结构体分配内存
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
// 为结构体指针 p 分配了一块内存。用 p->a[n]就能简单地访问可变长元素。
// 我们再用 sizeof(*p)测试结构体的大小,发现仍然为 4。
// 在定义这个结构体的时候,模子的大小就已经确定不包含柔性数组的内存大小。
// 柔性数组只是编外人员,不占结构体的编制。
// 用 free 函数来释放内存:
// free(p);
2. 利用共用体测试机器大小端
int checkDuan( )
{
union
{
int i; // 4字节
char ch; // 存放在 低地址
}c;
c.i = 127; //大端模式:高地址:0x007F 低地址:0x0000
// 小端模式: 高地址:0x0000 低地址:0x007F
printf("%x",c.ch);
return c.ch==127;
}
3. typedef常见用法
1. 常规变量类型定义:typedef unsigned char uchar
描述:uchar等价于unsigned char类型定义
2. 数组类型定义:typedef int array[2];(注:可理解为typedef int[] array)
描述:array 等价于 int [2]定义; array a 声明等价于int a[2]声明
typedef int array[M][N];
描述: array等价于 int[M][N]定义; array a声明等价于int a[M][N]声明
3. 指针类型定义: typedef int * pointer;
描述: pointer等价于 int*定义; pointer p 声明等价于int* a声明
typedef int *pointer[M];
描述: pointer等价于 int*[M]定义; pointer p声明等价于int*a[M]声明
4.函数地址说明描述:int func(void);
unsigned long funcAddr=(unsigned long)func;
funcAddr 的值是 func 函数的首地址
5.函数声明例如:typedef int func(void);
func 等价于 int(void)类型函数
6.函数指针例如: typedef int (*func)(void)
func等价于int (*)(void)类型
func pf 等价于 int(*pf)(void)声明,pf是一个函数指针变量
4. 预处理
预处理名称 意 义
#define 宏定义
#undef 撤销已定义过的宏名
#include 使编译程序将另一源文件嵌入到带有#include 的源文件中
#if 条件编译,#if 的一般含义是如果#if 后面的常量表达式为 true,
则编译它与#endif 之间的代码,否则跳过这些代码。
#endif 命令 #endif 标识一个 #if 块的结束。
#else #else 命令的功能有点象 C 语言中的 else ,
#else 建立另一选择(在# if 失败的情况下)。
#elif #elif 命令意义与 else if 相同,
它形成一个 if else-if 阶梯状语句,可进行多种编译选择。
#ifdef/#ifndef 用#ifdef 与 #ifndef 命令分别表示“如果有定义”及“如果无定义”,
是条件编译的另一种方法。
#line 改变当前行数和文件名称,它们是在编译程序中预先定义的标识符,
命令的基本形式如下:
#line number["filename"]
#error 编译程序时,只要遇到 #error 就会生成一个编译错误提示消息,并停止编译
#pragma 为实现时定义的命令,它允许向编译程序传送各种指令
例如,编译程序可能有一种选择,它支持对程序执行的跟踪。
可用#pragma语句指定一个跟踪选择。
另外 ANSI 标准 C 还定义了如下几个宏:
_LINE_ 表示正在编译的文件的行号
_FILE_ 表示正在编译的文件的名字
_DATE_ 表示编译时刻的日期字符串,例如: "25 Dec 2007"
_TIME_ 表示编译时刻的时间字符串,例如: "12:30:55"
_STDC_ 判断该文件是不是定义成标准 C 程序
5. #pragma编译
1. #pragma message("消息文本")
它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
假设我们希望判断自己有没有在源代码的什么地方定义了_X86 这个宏可以用下面的方法:
#ifdef _X86
#pragma message(“_X86 macro activated!”)
#endif
2. #pragma code_seg()
另一个使用得比较多的 pragma 参数是 code_seg。
格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
3. #pragma once 编译一次
比较常用,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,
但是考虑到兼容性并没有太多的使用它。
4. #pragma hdrstop
#pragma hdrstop 表示预编译头文件到此为止,后面的头文件不进行预编译。
你可以用#pragma startup 指定编译优先级,
如果使用了#pragma package(smart_init) ,
BCB就会根据优先级的大小先后编译.
5. #pragma resource 载入资源
#pragma resource "*.dfm" 表示把*.dfm 文件中的资源加入工程。
*.dfm 中包括窗体外观的定义。
6. #pragma warning 错误信息
#pragma warning(disable:4507 34) // 不显示 4507 和 34 号警告信息
#pragma warning(once:4385) // 4385 号警告信息仅报告一次
#pragma warning(error:164) // 把 164 号警告信息作为一个错误。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为 n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。
7. #pragma pack(用于指定内存对齐的方式(按指定的字节数进行对齐)) 和 内存对齐问题(降低访存消耗)
struct TestStruct1
{
char c1; // 1字节 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。(大小为4的倍数)
short s; // 2字节
char c2; // 1字节
int i; // 4字节
}
|c1| - | s | 编译器在默认情况下按照4字节对齐,即#pragma pack(4)
|c2| - | - | - |
| i |
3*4 =12 字节
CPU对内存的读取不是连续的,而是分块读取的,块的大小只能是2^i字节数(i=0,1,2,3…)。
从CPU的读取性能和效率来考虑,若读取的数据未对齐,则需要两次总线周期来访问内存,因而效率会大打折扣。
某些硬件平台只能从规定的相对地址处读取特定类型的数据,否则产生硬件异常。
struct TestStruct2
{
char c1; // 1字节
char c2; // 1字节
short s; // 2字节
int i; // 4字节
}
|c1|c2|s |
|i |
2*4 = 8字节内存
使用指令#pragma pack (n),编译器将按照 n 个字节对齐。
使用指令#pragma pack (),编译器将取消自定义字节对齐方式。
在#pragma pack (n)和#pragma pack ()之间的代码按 n 个字节对齐。
#pragma pack(8)
struct TestStruct4
{
char a; // 1字节
long b; // 4字节
};
struct TestStruct5
{
char c; // 1字节
TestStruct4 d;
long long e;// 8字节
};
#pragma pack()
a b
TestStruct4 的内存布局: 1***,1111,
c TestStruct4.a TestStruct4.b d
TestStruct5 的内存布局: 1***, 1***, 1111, ****, 11111111
6. 函数指针类型
void(*)() , 指向类型为 void (void)的函数的 函数指针
(void(*) ())0 ,将 0 强制转换为函数指针类型, 0 是一个地址,也就是说一个函数存在首地址为 0 的一段区域内。
(*(void(*) ())0),取 0 地址开始的一段内存里面的内容,其内容就是保存在首地址为 0 的一段区域内的函数。
(*(void(*) ())0)(), 这是函数调用
同理 :
(*(char**(*) (char **,char **))0) ( char **,char **);
char * (*pf)(char * p); // 定义的是一个函数指针 pf, 指向一个类型为 char*(char*)的函数
char * (*pf[3])(char * p);// 定义一个 函数指针 数组, 数组名为 pf,数组内存储了 3 个指向函数的指针。
char * (*(*pf)[3])(char * p);// 定义一个指针,指向一个函数指针数组,该数组内存放3个 指向类型为char*(char*)的函数的函数指针。
7. 文档说明
/************************************************************************
* File Name : FN_FileName.c/ FN_FileName.h 文件名
* Copyright : 2003-2008 XXXX Corporation,All Rights Reserved. 版权亦称“著作权”
* Module Name : Draw Engine/Display 文件模块功能名称
*
* CPU : ARM7
* RTOS : Tron
*
* Create Date : 2008/10/01 创建日期
* Author/Corporation : WhoAmI/your company name 作者
*
* Abstract Description : Place some description here. 文件描述信息
*
*-----------------------Revision History----------------------- 版本信息
* No Version Date Revised By Item Description
* 1 V0.95 08.05.18 WhoAmI abcdefghijklm WhatUDo
*
************************************************************************/
<内容来自网络,如有侵权,请立即联系本人,自当立即删除>