关键字 | 意义 |
---|---|
auto | 声明自动变量,缺省时编译器一般默认为 auto |
int | 声明整型变量 |
double | 声明双精度变量 |
long | 声明长整型变量 |
char | 声明字符型变量 |
float | 声明浮点型变量 |
short | 声明短整型变量 |
signed | 声明有符号类型变量 |
unsigned | 声明无符号类型变量 |
struct | 声明结构体变量 |
union | 声明联合数据类型 |
enum | 声明枚举类型 |
static | 声明静态变量 |
switch | 用于开关语句 |
case | 开关语句分支 |
default | 开关语句中的“其他”分支 |
break | 跳出当前循环 |
register | 声明寄存器变量 |
const | 声明只读变量 |
volatile | 说明变量在程序执行中可被隐含地改变 |
typedef | 用以给数据类型取别名(当然还有其他作用) |
extern | 声明变量是在其他文件正声明(也可以看做是引用变量) |
return | 子程序返回语句(可以带参数,也可不带参数) |
void | 声明函数无返回值或无参数,声明空类型指针 |
continue | 结束当前循环,开始下一轮循环 |
do | 循环语句的循环体 |
while | 循环语句的循环条件 |
if | 条件语句 |
else | 条件语句否定分支(与 if 连用) |
for | 一种循环语句(可意会不可言传) |
goto | 无条件跳转语句 |
sizeof | 计算对象所占内存空间大小 |
定义与声明:
定义:
定义就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名。但注意,这个名字一旦和这块内存匹配起来(可以想象是这个名字嫁给了这块空间,没有要彩礼啊。_),它们就同生共死,终生不离不弃。并且这块内存的位置也不能被改变。
声明:
定义声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存。
auto:编译器在缺省情况下,所有变都是 auto 的。
register:请求编译器尽可能的将变量存在 CPU 内部寄存器中而不是通过内存寻址访问以提高效率,注意是尽可能,不是绝对。
寄存器就是一块一块小的存储空间,只不过存取速度比内存快。数据从内存里拿出来先放到寄存器中,然后CPU从寄存器里读取数据来进行处理,处理完成后同样把数据通过寄存器存放到内存里,CPU不直接和内存打交到。
【注意】:
第一个作用:修饰变量。
被修饰的变量又分为局部变量和全局变量,但修饰后的它们都存在内存的静态区。
原本的局部变量存放在栈中,全局变量存放在静态数据区中。
第二个作用:修饰函数,函数前加 static 使得函数成为静态函数。
static的历史:起初,在 C 中引入关键字 static 是为了表示退出一个块后仍然存在的局部变量。随后,static 在 C 中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。
数据类型类似于模子,在内存中直接卡出相应大小的内存来进行后续数据的存储。
在 32 位的系统上:
short 咔出来的内存大小是 2 个 byte
int 咔出来的内存大小是4个byte
long 咔出来的内存大小是4个byte
float 咔出来的内存大小是4 个 byte
double 咔出来的内存大小是 8 个 byte
char 咔出来的内存大小是 1 个 byte
【注意】这里指一般情况,可能不同的平台还会有所不同,具体平台可以用 sizeof 关键字测试一下。
【规则 1-1】命名应当直观且可以拼读,可望文知意,便于记忆和阅读。标识符最好采用英文单词或其组合,不允许使用拼音。
【规则 1-2】命名的长度应当符合“min-length && max-information”原则。标识符的长度一般不要过长,较长的单词可通过去掉“元音”形成缩写。英文词尽量不缩写,特别是非常用专业名词,如果有缩写,在同一系统中对同一单词必须使用相同的表示法,并且注明其意思。
【规则 1-3】当标识符由多个词组成时,每个词的第一个字母大写,其余全部小写。
【规则 1-4】尽量避免名字中出现数字编号。
【规则 1-5】对在多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名的缩写作为范围限定符,如图形界面GUI_)。
【规则 1-6】标识符名分为两部分:规范标识符前缀(后缀) + 含义标识 ;非全局变量可以不使用范围限定符前缀。
作用域前缀:
数据类型前缀:
含义标识规则:
变量含义标识:目标词 + 动词(的过去分词)+ [状语]+[目的地]
函数含义标识:动词(一般现时)+目标词+[状语]+[目的地]
【规则 1-7】程序中不得出现仅靠大小写区分的相似的标识符。
【规则 1-8】一个函数名禁止被用于其它之处。
【规则 1-9】所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。
【规则 1-10】局部变量中可采用通用的命名方式,仅限于 n、i、j 等作为循环变量使用。
【规则 1-11】结构体定义时需要有明确的结构体名
【规则 1-12】定义变量的同时千万千万别忘了初始化。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据。
【规则 1-13】不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度数据扩展。由于表达式的运算结果的类型根据操作数的类型决定的,所以当运算表达式操作数的类型和将要赋值的目标变量类型不一致时,操作数要先强制转换为目标变量数据类型。
【规则 1-14】禁止使用八进制的常数(八进制整常数必须以0开头,即以0作为八进制数的前缀。数码取值为0~7)和八进制的转义字符。在计算机中,任何以0开头的数字都被认为是八进制格式的数,例如052是十进制42。
【规则 1-15】char有三种不同的类型:单纯的char、signed char、unsigned char。其中signed char、unsigned char这两个用来声明数值的,单纯的char是真正的字符类型,声明字符的。一元负号运算(-)不能使用在unsigned表达式内。
【规则 1-16】所有无符号型常量都应该带有字母U后缀。
【规则 1-17】使用浮点数应遵循已定义好的浮点数标准。
【规则 1-18】代码的缩进一般为4个字符,不要使用Tab键,因为不同的编译器Tab键的定义不同。
int i=0;
A),sizeof(int); B),sizeof(i); C),sizeof int; D),sizeof i;
A,B: 值为 4。
c: sizeof int 表示什么啊?int 前面加一个关键字?类型扩展?明显不正确,我们可以在 int 前加 unsigned,const 等关键字但不能加 sizeof。
D: 结果也为 4
首先计算int型所占字节数,然后再乘以p。
计算机底层只知道0、1,是如何区分正负呢:把基本数据类型的最高位腾出来,用来存储符号
缺省下,编译器默认数据为signed类型(char类型数据除外)
int main()
{
signed char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
答案:255
计算机系统,数值一律按照补码存储:
通过补码运算,可以将符号位和其他位统一处理;减法可以按照加法运算;若最高位有进位则进位被舍弃。
a[0] -1 -> 0xff 1111 1111
a[1] -2 -> 0xfe 1111 1110
a[2] -3 -> 0xfd 1111 1101
...
a[127] -128 -> 0x80 1000 0000
a[128] -129 -> 0x7f 1 0111 1111(需要九位才可以存储,去掉最高位,剩下8位)
a[128] -130 -> 0x7e 1 0111 1110(需要九位才可以存储,去掉最高位,剩下8位)
...
a[255] -256 -> 0x0 1 0000 0000(需要九位才可以存储,去掉最高位,剩下8位)
a[256] -257 -> 0xff 10 1111 1111(需要十位才可以存储,去掉最高两位,剩下8位) -> 轮回了
所以a[0]-a[254]里面值都不为0,a[255]为0,strlen碰到‘\0’结束。
FLASE的值在编译器里定义为0,但TRUE的值可以是任何非零的值,由于值不确定不能进行比较。
bool bTestFlag = FALSE
//以下两种可以
if(!bTestFlag){ }
if(bTestFlag){ }
float 和 double 类型的数据都有精度限制,直接拿来与0.0比较是不对的。“float.h” 提供了DBL_EPSILON、FLT_EPSILON、LDBL_EPSILON定义好的精度,如果一个数落在了[0.0 - EPSILON , 0.0 + EPSILON]这个闭区间,我们认为在某个精度内它的值与零值相等,否则不相等。(将EPSILON替换成上面三个则分别代表float、double、long double三种精度)
bool fTest() {
float a = 0.0;
if ((a >= -FLT_EPSILON) && (a <= FLT_EPSILON)) {
return true;
}
else
return false;
}
int main() {
printf("%d", fTest());
return 10;
}
输出:1
int * p = null; //定义指针要初始化
//以下两种可以
if(NULL == p); if(NULL != P);
这种写法有些奇怪,但是成功避免的“=” 和 “==”编译器检查不出的问题,所以以后要这样写。
else始终与同一括号内最近的未匹配的if语句结合。
if(NULL != p) ;
fun();
等价于
if(NULL != p)
{
;
}
fun();
会把分号解析成两条语句
真正使用空语句时候建议写法:
if(NULL != p) NULL;
fun();
【规则 1-19】由于if语句总是需要判断,所以先处理正常情况,再处理异常情况也就是说把正常情况的处理放在 if 后面,而不要放在 else 后面。
【规则 1-20】确保if和else子句没有弄反
【规则 1-21】赋值运算符不能使用在产生布尔值的表达式上。
if((x = y) != 0) //Boolean by context
应修改为
x = y;
if(x != 0)
{
foo;
}
【规则1-22】所有的 if - else if 结构应该由else子句结束,最后的else语句的要求是保护性编程。else语句或者要执行适当的动作,或者要包含适当的注释以说明为何没有执行动作,与switch语句中要求有一个default子句是一致的。
if(x < 0)
{
log_error(3);
x = 0;
}
else if(y < 0)
{
x = 3;
}
else //this else clause is required,even if the programer expects this will never be reached
{
//no charge in value of x
}
if、else 一般表示两个分支或者是嵌套比较少量的分支,如果分支比较多还是使用switch、case。
//基本格式
switch(variable)
{
case Valuel:
//program code
break;
case Value2:
//program code
break;
case Value3:
//program code
break;
...
default:
break;
}
【规则1-23】每个case语句的结尾绝对不要忘记加break,否则将会导致多个分支重叠。
【规则1-24】最后必须使用default分支,即使程序真的不需要也要保留下面语句:
default:
break;
【规则1-25】switch case组合中,禁止使用return语句。
【规则1-26】switch表达式不应是有效的布尔值。
case后面只能是整型或字符型的常量或者常量表达式(因为字符型数据在内存中储存的是它的ASCII码值)
【规则1-27】按照字母或者数字顺序排列各条case语句,A-B-C;1-2-3。
【规则1-28】按照正常情况放前面,异常情况放后面的顺序排列,同时都需要做好注释。
【规则1-29】按照执行频率排列。最常执行的放在前面,不常执行的放到后面。最常执行的可能是调试单步执行最多的代码,方便寻找。
【规则1-30】简化每种情况的操作,如果某个case需要多个代码执行某个操作,可以把操作写成一个或几个子程序在case里进行调用。
【规则1-31】default子句只用于检查真正的默认情况,认真地把每一种情况都用case语句完成。
C语言中循环语句有3种:while循环、do-while循环、for循环
break:中止本层循环。
continue:终止本次(本轮)循环,进入下一次循环。
循环计数器(循环变量)与循环次数控制调剂必须是相同数据类型。
循环调剂表达如果需要访问数组的元素,需要同时判断对数组的访问是否越界,否则很容易出现死循环。
char var1[MAX]
for(i = 0; i<MAX && var1[i]!=0; i++)
{
//Even if 0 are not set in the var1 array,there is no risk of accessing out-side the array range
}
在循环体内,相等操作(== , !=)不能用于循环计数变量,因为当循环计数变量不是加减1时,会导致死循环。所以使用<=,>=,<,>进行代替。
【建议1-32】多层循环中,长循环放最内层,最短的循环放最外层,减少CPU跨切循环的次数,提高效率。
【建议1-33】建议for语句的控制循环变量的取值采用“半开半闭区间的写法”,更加直观
for(n = 0; n < 10 ;n++)
【规则1-34】不能在for循环体内修改循环变量,防止循环失控。
【规则1-35】循环嵌套控制在3层内,多余的写成子函数。
【规则1-36】for语句的控制表达式不能包含任何浮点类型的对象。
主张禁用goto关键字的使用,有可能会导致跳过程序初始化的操作。
void真正发挥的作用在于:对函数返回的限定;对函数参数的限定。
void * 类型,任何类型的指针都可以直接赋值给它,无需进行强制类型转换。但不代表 void * 也可以无需进行强制类型转换赋值给其他的指针。
“空类型”可以包含“有类型”,“有类型“则不能包含”空类型“。
void *p1
int *p2
p2 = p1;
//报错,提示不能将 void * 转换为 int *
int function(void){}
【规则1-37】如果函数没有返回值,应声明其为void类型。凡不加返回值类型限定的函数默认作为返回整型值处理。在编写C程序时,任何函数都必须一个不漏的指定其类型。
【规则1-38】如果函数无参数,应将其参数声明为void,否则别的函数在调用的时候传入参数后有些编译器不报错则会出现意外情况(我的编译器报错会)。
【规则1-39】ANSI标准,不能对void指针进行算法操作。进行算法操作的指针必须是确定知道其指向数据类型大小的,也就是说必须知道内存目的地址的确切值。但是GNU不这么认为,它指定void *与char *一致。
void *pvoid
pvoid ++; //ANSI:错误 GNU正确
pvoid += 1; //ANSI:错误 GNU正确
(char *)pvoid ++; //ANSI:正确 GNU正确
(char *)pvoid += 1; //ANSI:错误 GNU正确
【规则1-40】如果函数的**参数可以是任意类型指针,应声明其参数为 void ***,任何类型的指针都可以传入其中(类似于泛型概念)。
void * memcpy(void *dest, const void *src, size_t len);
void * memset(void *buff, int c, size_t num);
//这样任何类型的指针都可以传入memcpy和memset中,这样体现了内存操作函数的意义。
*有趣的是memcpy memset的返回值也是void 类型
【规则1-41】void不能代表一个真实的变量,因为定义变量时必须分配内存空间。void体现了一种抽象,正如不能给抽象基类定义一个实例一样。T
void a; //错误
function(void a); //错误
return用来中止一个函数并返回其后面跟着的值。
return(Val); //此括号可以省略,但是一般不省略。
【规则1-42】return语句不可返回指向”栈内存“的”指针“,因为该内存在函数体结束时被自动销毁。
const是constant的缩写,是恒定不变的意思,也翻译为常量、常数等,精确来说是只读变量,值在编译的时候不能被使用。其推出的初始目的在于取代预编译指令。
const修饰的只读变量必须在定义的同时初始化,具有不可变性。
编译器通常不为普通const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,提高了效率:
#define M 3 //宏常量
const int N=5; //此时并未将N放入内存中
......
int i=N; //此时为N分配内存,以后不再分配!
int I=M; //预编译期间进行宏替换,分配内存
int j=N; //没有内存分配
int J=M; //再进行宏替换,又一次分配内存!
const定义的只读变量从汇编角度看,只是给出了对应的内存地址,所以在内存中只有一份备份(全局只读变量,存在静态区),在编译的时候确定其值,具有特定类型。
#define给出的是立即数,所以在内存中有若干备份,在预编译阶段进行替换,且没有类型。
一般变量:修饰符const可以用在类型说明符前,也可以用在类型说明符后。
int const i=2; 或 const int i=2;
数组:定义或说明一个只读数组可采用如下格式:
int const a[5]={1, 2, 3, 4, 5}; 或 const int a[5]={1, 2, 3, 4, 5};
指针:离谁近修饰谁:
const int *p; // p 可变,p指向的对象不可变 ->*p
int const *p; // p 可变,p指向的对象不可变 ->*p
int *const p; // p不可变,p指向的对象可变 ->p
const int *const p; // 指针 p 和 p 指向的对象都不可变 ->p
函数参数:当不希望这个参数值在函数体内被意外改变时使用:
//告诉编译器*p在函数体中不能改变
void Fun(const int *p);
函数返回值:修饰函数返回值,返回值不可改变。
另一链接文件引用const只读变量:
extern const int i; //正确的声明
extern const int j = 10; //错误的声明,成了定义
volatile是异变的、不稳定的意思,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其他线程等。遇到这个关键字的变量,编译器对访问该变量的代码不再进行优化。
int i=10;
int j = i;//(1)语句
int k = i;//(2)语句
//在(1)、(2)两条语句中,i没有被用作左值。在(1)语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k赋值。
volatile int i=10;
int j = i;//(3)语句
int k = i;//(4)语句
//volatile 关键字告诉编译器 i是随时可能发生变化的,因而编译器生成的汇编代码会重新从i的地址处读取数据放在k中。如果一个端口数据或者是多个线程的共享数据就不会出错。
const volatile int i = 10
:这行代码没有问题,**“volatile”的含义并非是“non-const”,volatile和const并不矛盾,只是控制的范围不一样,一个在程序本身之外,另一个是程序本身。**这个表明当前值i在当前程序的上下文中不会被修改,且可能会被外部的其余进程或系统进行修改。
const表示常量语义(运行时):被const修饰的对象在所在的作用域无法进行修改操作,编译器对于试图直接修改const对象的表达式会产生编译错误。
volatile表示“易变的”:即在运行期对象可能在当前程序上下文的控制流以外被修改(例如多线程中被其它线程修改;对象所在的存储器可能被多个硬件设备随机修改等情况)
extern可以置于变量或函数前,表明变量或函数的定义在别的文件中,不是不文定义的。链接器在其他模块中解析/绑定此标识符。
struct将一些相关联的数据打包成一个整体,方便使用。
结构体所占的内存大小是其成员所占内存之和。
typedef struct st_type_b
{
}type_b;
printf("%d\r\n",sizeof(type_b));
上述所述情况值为多少呢?答案为1,编译器认为任何一种数据类型都有其大小,非空结构体类型数据最少需要占一个字节的空间,而空结构体类型数据总不能比最小的非空结构体类型数据所占的空间大吧。编译器理所当然的认为你构造一个结构体数据类型是用来打包一些数据成员的,而最小的数据成员需要1个byte,编译器为每个结构体类型数据至少预留1个字节的空间。所以,空结构体的大小就定为1个字节。
结构体中的最后一个元素允许是位置大小的数组,这就叫做柔性数组,但结构中的柔性数组成员前面必须至少有一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。sizeof返回的这种结构大小不包含柔性数组的内存(它属于编外人员不占用结构体的编制,只是在使用的时候需要把它当作结构体的一个成员)。包含柔性数组成员的结构用malloc()函数进行内存的动态分配。
typedef struct st_type
{
int i;
int a[];
}type_a;
void main() {
type_a * p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
printf("%d\r\n", sizeof(*p));
free(p);
return ;
}
输出:4
union 维护足够的空间来置放多个数据成员中的“一种”,在union中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。
union StateMachine
{
char character;
int number;
char *str;
double exp;
};
一个union只配置一个足够大的空间以来容纳最大长度的数据成员,例如上述最大长度为double,所以StateMachine的空间大小是double类型。
大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。
union型的成员的存取都是 相对于该联合体基地址的偏移量为0处开始,也就是联合体的访问不论对哪个变量的存取都是从union的首地址位置开始。某些系统可能同时支持两种存储模式,你可以用硬件跳线或在选项中设置其存储模式。
int checkSystem( )
{
union check
{
int i;
char ch;
}c;
c.i = 1;
return (c.ch ==1);
}
变量i占4个字节,但只有一个字节的值为1,另外三个字节的值都为0。如果取出低地址上的值为0,毫无疑问,这是大端模式;如果取出低地址上的值为1,毫无疑问,这是小端模式。
【规则1-43】使用位域的时候需要提前用代码check当前系统的模式(大端或是小端),使用位域特别注意对其方式是LSB或是MSB。
MSB LSB:起始地址为最高位, 最后地址为最低位。
LSB MSB:起始地址为最低位,最后地址为最高位。
一个芯片的管脚中,对于一个多比特的信号,比如32根的地址线,从低开始按0到31编个号。MSB就是31,LSB就是0。那么如果标记为:ADDR[31:0]就是MSB first的方式,如果标记为ADDR[0:31]就是LSB first的方式。
位域主要有两种用法:
位域特点:
建议申明结构体来存放位域,在同一结构体中不要包含其他数据类型
struct message
{
signed int little: 4;
unsigned int x_set: 1;
unsigned int y_set: 1;
}message_chunk;
enum enum_type_name
{
ENUM_CONST_1,
ENUM_CONST_2,
...
ENUM_CONST_n
} enum_variable_name;
enum_type_name是自定义的一种数据类型名,enum_variable_name为enum_type_name的一个变量,也是枚举变量。enum_type_name是对一个变量取值范围的限定,如果赋给该类型变量的值不在列表中,则会报错或者警告。ENUM_CONST_1,ENUM_CONST_2…这些成员都是常量,可以给这些常量赋值,如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,它们的值从0开始依次递增1。
enum Color
{
GREEN=1,
RED,
BLUE,
GREEN_RED=10,
GREEN_BLUE
}ColorVal;
其中:
GREEN=1
RED=2
BLUE=3
GREEN_RED=10
GREEN_BLUE =11
枚举类型的成员的值必须是int类型能表述的
typedef 的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型。
在实际项目中,为了方便,可能很多数据类型(尤其是结构体之类的自定义数据类型)需要我们重新取一个适用实际情况的别名。
typedef struct student
{
//code
}Stu_st,*Stu_pst;
A),struct student stu1;和 Stu_st stu1;没有区别。
B),struct student *stu2;和 Stu_pst stu2;和 Stu_st * stu2;没有区别。
我们把“struct student { /*code*/}”看成一个整体,typedef 就是给“struct student{/*code*/}”取了个别名叫“Stu_st”;同时给“struct student{ /*code*/} *”取了个别名叫“Stu_pst”
C),const Stu_pst stu3; ->stu3指针
D),Stu_pst const stu4; ->stu4指针
const 放在类型名“int”前后都行;而 const int *p 与 int* const p 则完全不一样。我们看const修饰谁都时候完全可以将数据类型名视而不见,当它不存在。
Stu_pst是“struct student { /*code*/} *”的别名, “struct student {/*code*/} *”是一个整体。对于编译器来说,只认为 Stu_pst 是一个类型名,所以在解析的时候很自然的把“Stu_pst”这个数据类型名忽略掉。
【规则1-44】用typedef重命名基本的数据类型,以替换原始的数据类型。
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
E): #define INT32 int
unsigned INT32 i = 10;
F): typedef int int32;
unsigned int32 j = 10;
//其中E)编译不会出错因为在预编译的时候INT32被替换为int
//其中F)编译出错因为用typedef取的别名不支持这种类型扩展。
G): #define PCHAR char*
PCHAR p3,p4;
H): typedef char* pchar;
pchar p1,p2;
//两组代码编译都没有问题,但是,这里的p4却不是指针,仅仅是一个char类型的字符。
的别名, “struct student {/code/} *”是一个整体。对于编译器来说,只认为 Stu_pst 是一个类型名,所以在解析的时候很自然的把“Stu_pst”这个数据类型名忽略掉。
【规则1-44】用typedef重命名基本的数据类型,以替换原始的数据类型。
```c
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
E): #define INT32 int
unsigned INT32 i = 10;
F): typedef int int32;
unsigned int32 j = 10;
//其中E)编译不会出错因为在预编译的时候INT32被替换为int
//其中F)编译出错因为用typedef取的别名不支持这种类型扩展。
G): #define PCHAR char*
PCHAR p3,p4;
H): typedef char* pchar;
pchar p1,p2;
//两组代码编译都没有问题,但是,这里的p4却不是指针,仅仅是一个char类型的字符。