编程语言分2种:强类型语言和弱类型语言。强类型语言中所有的变量都有自己固定的类型,这个类型有固定的内存占用,有固定的解析方法;弱类型语言中没有类型的概念,所有变量全都是一个类型(一般都是字符串的),程序在用的时候再根据需要来处理变量。
C语言就是典型的强类型语言,C语言中所有的变量都有明确的类型。因为C语言中的一个变量都要对应内存中的一段内存,编译器需要这个变量的类型来确定这个变量占用内存的字节数和这一段内存的解析方法。数据类型是程序设计语言预先规定好的,每种类型处理一类数据(比如整型数、实型数的数据)。
为什么要区分数据类型?
实际上对计算机系统和硬件而言来说没有数据类型这个概念,那我们为什么在高级语言当中要区分数据类型呢?实际上,数据类型决定了4个方面:
1、系统要给该数据分配多大的内存;
2、该数据的存储方式和读取方式是什么样的;
3、取值范围是不同的;
4、可参与的运算类型是不一样的。
C语言中的数据类型有:
整数类型:
类型 存储大小 值范围 char 1 字节 -128 到 127 或 0 到 255 unsigned char 1 字节 0 到 255 signed char 1 字节 -128 到 127 int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295 short 2 字节 -32,768 到 32,767 unsigned short 2 字节 0 到 65,535 long 4 字节 -2,147,483,648 到 2,147,483,647 unsigned long 4 字节 0 到 4,294,967,295 2的8次方256要记住;2的16次方65536要记住;2的32次方记住42亿多就行了。
另外记住2的10次方是1024。
int的字节大小和系统的位数有关,在16位系统中是2字节,在32位系统以及64位系统中都是4字节。
不要对变量所占的内存空间字节数想当然,为了得到某个类型或某个变量在特定平台上的准确大小,您可以使用 sizeof 运算符。表达式 sizeof(type) 得到对象或类型的存储字节大小。
整型的常量在C语言当中可以有三种表示方法:十进制、八进制和十六进制。
浮点类型:
C语言中,输出double类型(双 精度)和float(单精度)时, 默认输出的是6位小数,不足6位,以0补齐,超过6位按四舍五入截断。
类型 存储大小 值范围 精度 float 4 字节 1.2E-38 到 3.4E+38 6 位有效位 double 8 字节 2.3E-308 到 1.7E+308 15 位有效位 long double 16 字节 3.4E-4932 到 1.1E+4932 19 位有效位
void类型
序号 类型与描述 1 函数返回为空
C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);2 函数参数为空
C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);3 指针指向 void
类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
附上一道题。
数值后面加“L”和“l”(小写的l)的意义是该数值是long型。
详细说如下:
5L的数据类型为long int。
5.12L的数据类型为long double。
数值后面加“U”和“u”的意义是该数值是unsigned型。
数值后面加“”H“、“h”的意义是该数值是用16进制表示的。
数值后面加“”B“、“b”的意义是该数值是用2进制表示的。
后面什么也不加,代表10进制。
栗子:
11111111B = FFH = 255
数值前面加“0”的意义是该数值是八进制。
数值前面加“0x”的意义是该数值是十六进制。c语言不支持二进制常量的表示法;支持8进制前面加0;16进制加0x;还有默认的10进制。
在单片机中,我们经常要用到32位的地址,通常我们都是直接写的,比如0x33333333,但是,我在HAL源码中看到很多地址后面都带了UL,以显式表示其是一个无符号的长整型数。加或者不加UL有何区别?
32位的设备,int的范围是-2147483648~2147483647,unsigned int范围是0~4294967295
一年的 SECONDS = 60* 60* 24*365 = 31,536,000
但是一百年的秒数就是100*SECONDS = 3,153,600,000,已经超过了int的上限,溢出了。
所以需要SECOND为unsigned long(这里隐式转换了),在运算的时候保证运算的结果是对的。
此外,有些设备,例如一些16位的嵌入式设备,int 是16位的,而long 是32位。
所以加个UL是为了在不同设备上,都能保证运算过程不会因为int的位数不同而导致溢出。
C语言标准是这样规定的:int最少16位(2字节),long不能比int短,short不能比int长,具体位长由编译器开发商根据各种情况自己决定。在32位x86处理器上,short、int、long普遍的长度是2字节、4字节、4字节。
16位系统:long是4字节,int是2字节
32位系统:long是4字节,int是4字节
64位系统:long是4字节,int是4字节
数学中数是有符号的,有整数和负数之分。所以计算机中的数据类型也有符号,分为有符号数和无符号数。
PS:具体是怎么实现的,涉及到原码、反码和补码等知识,需要了解的话自行查找相关资料,此处不赘述,对编程来说也没那么重要。
有符号数(默认):
整形:signed int(简写为 int)
signed long,也写作signed long int,(简写为long)
signed short,也写作signed short int(简写为short)
signed(表示signed int)
浮点型:
signed float(简写为float)
signed double(简写为double)
字符型:
signed char(简写为char)
无符号数:
整形:整形有无符号数,用来表示一些编码编号之类的东西。譬如身份证号,房间号
unsigned int(没有简写)
unsigned long int(简写unsigned long)
unsigned short int(简写为unsigned short)
浮点数:没有无符号浮点数。也就是说,小数一般只用在数学概念中,都是有符号的。
字符型:字符型有无符号数
unsigned char(没有简写)注意:对于整形和字符型来说,有符号数和无符号数表示的范围是不同的。
譬如字符型,有符号数范围是-128~127,无符号数的范围是0~255
字符就是整数
字符和整数没有本质的区别。可以给 char变量一个字符,也可以给它一个整数;反过来,可以给 int变量一个整数,也可以给它一个字符。char 变量在内存中存储的是字符对应的 ASCII 码值。如果以 %c 输出,会根据 ASCII码表转换成对应的字符,如果以 %d 输出,那么还是整数。
int 变量在内存中存储的是整数本身,如果以 %c 输出时,也会根据 ASCII码表转换成对应的字符。
也就是说,ASCII 码表将整数和字符关联起来了。
重合范围
char类型占内存一个字节,signed char取值范围是-128-127,unsigned char取值范围是0-255。
如果整数大于255,那么整数还是字符吗?
描述再准确一些,在char的取值范围内(0-255),字符和整数没有本质区别。
字符肯定是整数,0-255范围内的整数是字符,大于255的整数不是字符。
示例
/* * 程序名:book68.c,此程序演示字符与整数的关系 * 作者:C语言技术网(www.freecplus.net) 日期:20190525 */ #include
int main() { char a = 'E'; char b = 70; int c = 71; int d = 'H'; printf("a=%c, a=%d\n", a, a); printf("b=%c, b=%d\n", b, b); printf("c=%c, c=%d\n", c, c); printf("d=%c, d=%d\n", d, d); } 运行效果
在ASCII码表中,E、F、G、H 的值分别是 69、70、71、72。
既然char的本质是整数,那C语言中为什么还需要char类型呢?因为字符的个数不多,而char型变量占用的存储空间比int型变量小,所以用char型变量表示字符,为编程带来了方便。
注意:如果给一个char类型数据赋予超过范围的值,会循环回来,比如给char变量赋予256,那么此时这个变量的值会变成0,赋予257就会变成1,以此类推。
看一道题,有大坑:
这里的循环,我总是认为到了最后一次s[6]的时候,结果为0,就会退出循环。
其实是个大坑;这里的0,是字符'0',对应的十进制是48,所以依然为真!!!
对于float和double这种浮点类型的数,它在内存中的存储方式和整形数不一样。所以float和
int相比,虽然都是4字节,但是在内存中存储的方式完全不同。所以同一个4字节的内存,如果存储时是按照int存放的,取的时候一定要按照int型方式去取。如果存的时候和取的时候理解的方式不同,那数据就完全错了。
存取方式上主要有两种,一种是整形一种是浮点型,这两种存取方式完全不同,没有任何关联,所以是绝对不能随意改变一个变量的存取方式。在整形和浮点型之内,譬如说4种整形char、short、int、long只是范围大小不同而已,存储方式是一模一样的。float和double存储原理是相同的,方式上有差异,导致了能表示的浮点型的范围和精度不同。具体怎么实现的,自行查阅资料。
所有的类型的数据存储在内存中,都是按照二进制格式存储的。所以内存中只知道有0和1,不知道是int的、还是float的还是其他类型。
int、char、short等属于整形,他们的存储方式(数转换成二进制往内存中放的方式)是相同的,只是内存格子大小不同(所以这几种整形就彼此叫二进制兼容格式);而float和double的存储方式彼此不同,和整形更不同。
int a = 5;时,编译器给a分配4字节空间,并且将5按照int类型的存储方式转成二进制存到a所对应的内存空间中去(a做左值的);我们printf去打印a的时候(a此时做右值),printf内部的vsprintf函数会按照格式化字符串(就是printf传参的第一个字符串参数中的%d之类的东西)所代表的类型去解析a所对应的内存空间,解析出的值用来输出。也就是说,存进去时是按照这个变量本身的数据类型来存储的(譬如本例中a为int所以按照int格式来存储);但是取出来时是按照printf中%d之类的格式化字符串的格式来提取的。此时虽然a所代表的内存空间中的10101序列并没有变(内存是没被修改的)但是怎么理解(怎么把这些1010转成数字)就不一定了。譬如我们用%d来解析,那么还是按照int格式解析则值自然还是5;但是如果用%f来解析,则printf就以为a对应的内存空间中存储的是一个float类型的数,会按照float类型来解析,值自然是很奇怪的一个数字了。
总结:C语言中的数据类型的本质,就是决定了这个数在内存中怎么存储的问题,也就是决定了这个数如何转成二进制的问题。一定要记住的一点是内存只是存储1010的序列,而不管这些1010怎么解析。所以要求我们平时数据类型不能瞎胡乱搞。
分析以下几种情况:
* 按照int类型存却按照float类型取 一定会出错
* 按照int类型存却按照char类型取 有可能出错也有可能不出错
* 按照short类型存却按照int类型取 有可能出错也有可能不出错
* 按照float类型存却按照double取 一定会出错
数据类型的本质就决定变量的内存占用数,和内存的解析方法。
所以得出结论:c语言中变量必须有确定的数据类型,如果一个变量没有确定的类型(就是所谓的无类型)会导致编译器无法给这个变量分配内存,也无法解析这个变量对应的内存。因此得出结论不可能有没有类型的变量。
但是C语言中可以有没有类型的内存。在内存还没有和具体的变量相绑定之前,内存就可以没有类型。实际上纯粹的内存就是没有类型的,内存只是因为和具体的变量相关联后才有了确定的类型(其实内存自己本身是不知道的,而编译器知道,我们程序在使用这个内存时知道类型所以会按照类型的含义去进行内存的读和写)。
void类型的正确的含义是:不知道类型,不确定类型,还没确定类型。
void a;定义了一个void类型的变量,含义就是说a是一个变量,而且a肯定有确定的类型,只是目前我还不知道a的类型,还不确定,所以标记为void。什么情况下需要void类型?
其实就是在描述一段还没有具体使用的内存时需要使用void类型。
void的一个典型应用案例就是malloc的返回值。void *malloc(size_t size)
我们知道malloc函数向系统堆管理器申请一段内存给当前程序使用,malloc返回的是一个指针,这个指针指向申请的那段内存。malloc刚申请的这段内存尚未用来存储数据,malloc函数也无法预知这段内存将来被存放什么类型的数据,所以malloc无法返回具体类型的指针,解决方法就是返回一个void *类型,告诉外部我返回的是一段干净的内存空间,尚未确定类型。所以我们在malloc之后可以给这段内存读写任意类型的数据。
void *类型的指针指向的内存是尚未确定类型的,因此我们后续可以使用强制类型转换强行将其转为各种类型。这就是void类型的最终归宿,就是被强制类型转换成一个具体类型。
void类型使用时一般都是用void *,而不是仅仅使用void。总结来说就是:
C语言中的void类型,代表任意类型,而不是空的意思。任意类型的意思不是说想变成谁就变成谁,而是说它的类型是未知的,是还没指定的。
void * 是void类型的指针。void类型的指针的含义是:这是一个指针变量,该指针指向一个
void类型的数。void类型的数就是说这个数有可能是int,也有可能是float,也有可能是个结构体,哪种类型都有可能,只是我当前不知道。void型指针的作用就是,程序不知道那个变量的类型,但是程序员自己心里知道。程序员如何知道?当时给这个变量赋值的时候是什么类型,现在取的时候就还是什么类型。这些类型对不对,能否兼容,完全由程序员自己负责。编译器看到void就没办法帮你做类型检查了。
一个函数形参列表为void:表示这个函数调用时不需要给它传参。
一个函数返回值类型是void:表示这个函数不会返回一个有意义的返回值。所以调用者也不要想着去使用该返回值。
和java、c++等高级语言不同,C语言中原生类型没有bool。在C语言中如果需要使用bool类型,可以用int来代替。
很多代码体系中,用以下宏定义来定义真和假
#define TRUE 1
#define FALSE 0注意:除了0之外,其他全是真,包括负数。
很多高级语言像java、C#等就有字符串类型,有个String来表示字符串,用法和int这些很像,可以String s1 = "linux";来定义字符串类型的变量。
C语言没有原生String类型,C语言中的字符串是通过字符指针来间接实现的。
C语言中定义字符串方法:char *p = "linux"。此时p就叫做字符串,但是实际上p只是一个字符指针(本质上就是一个指针变量,只是p指向了一个字符串的起始地址而已)。C语言中字符串的本质:指针指向头、固定尾部的地址相连的一段内存
- 字符串就是一串字符。字符反映在现实中就是文字、符号、数字等人用来表达的字符,反映在编程中字符就是字符类型的变量。C语言中使用ASCII编码对字符进行编程,编码后可以用char型变量来表示一个字符。字符串就是多个字符打包在一起共同组成的。
- 字符串在内存中其实就是多个字节连续分布构成的(类似于数组,字符串和字符数组非常像)
- C语言中字符串有3个核心要点:第一是用一个指针指向字符串头;第二是固定尾部(字符串总是以'\0'来结尾);第三是组成字符串的各字符彼此地址相连。
- '\0'是一个ASCII字符,其实就是编码为0的那个字符(真正的0,和数字0是不同的,数字0有它自己的ASCII编码)。要注意区分'\0'和'0'和0.(0等于'\0','0'等于48)
- '\0'作为一个特殊的数字被字符串定义为(幸运的选为)结尾标志。产生的副作用就是:字符串中无法包含'\0'这个字符。(C语言中不可能存在一个包含'\0'字符的字符串),这种思路就叫“魔数”(魔数就是选出来的一个特殊的数字,这个数字表示一个特殊的含义,你的正式内容中不能包含这个魔数作为内容)。
注意:指向字符串的指针和字符串本身是分开的两个东西。
char *p = "linux",在这段代码中,p本质上是一个字符指针,占4字节;"linux"分配在代码段,占6个字节;实际上总共耗费了10个字节,这10个字节中:4字节的指针p叫做字符串指针(用来指向字符串的,理解为字符串的引子,但是它本身不是字符串),5字节的用来存linux这5个字符的内存才是真正的字符串,最后一个用来存'\0'的内存是字符串结尾标志(本质上也不属于字符串)。另一种构造字符串的方式就是字符数组:char a[] = "linux",底层也是用指针来实现的。
对比:字符数组和字符串有本质差别。字符数组本身是数组,数组自身自带内存空间,可以用来存东西(所以数组类似于容器);而字符串本身是指针,本身永远只占4字节,而且这4个字节还不能用来存有效数据,所以只能把有效数据存到别的地方,然后把地址存在p中。
也就是说字符数组自己存那些字符;字符串一定需要额外的内存来存那些字符,字符串本身只存真正的那些字符所在的内存空间的首地址。--------------------------------------------------------------更多内容在字符串专题篇中详解。
隐式转换:
隐式转换就是自动转换,是C语言默认会进行的,不用程序员干涉。
C语言的理念:隐式类型转换默认朝精度更高、范围更大的方向转换。强制类型转换:
C语言默认不会这么做,但是程序员我想这么做,所以我强制这么做了。转换是临时的,并不会永久改变数据的原有类型。
详细内容如下:
数据类型转换就是将数据(变量、数值、表达式的结果等)从一种类型转换为另一种类型。
自动类型转换
自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。1 . 将一种类型的数据赋值给另外一种类型的变量时就会发生自动类型转换,例如:
float f = 150;
150 是 int 类型的数据,需要先转换为 float 类型才能赋值给变量 f。再如:
int n = f;
f 是 float 类型的数据,需要先转换为 int 类型才能赋值给变量 n。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。
2 . 在不同类型的混合运算中,编译器也会自动地转换数据类型,将参与运算的所有数据先转换为同一种类型,然后再进行计算。转换的规则如下:
- 转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算。
- 所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型,也要先转换为 double 类型,才能进行运算。
- char 和 short 参与运算时,必须先转换成 int 类型。
下图对这种转换规则进行了更加形象地描述:
unsigned 也即 unsigned int,此时可以省略 int,只写 unsigned。
自动类型转换示例:#include
int main(){ float PI = 3.14159; int s1, r = 5; double s2; s1 = r * r * PI; s2 = r * r * PI; printf("s1=%d, s2=%f\n", s1, s2); return 0; } 运行结果:
s1=78, s2=78.539749
在计算表达式rrPI时,r 和 PI 都被转换成 double 类型,表达式的结果也是 double 类型。但由于 s1 为整型,所以赋值运算的结果仍为整型,舍去了小数部分,导致数据失真。强制类型转换
自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。自动类型转换是编译器默默地、隐式地进行的一种类型转换,不需要在代码中体现出来;强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。
强制类型转换的格式为:
(type_name) expression
type_name为新类型名称,expression为表达式。例如:
(float) a; //将变量 a 转换为 float 类型 (int)(x+y); //把表达式 x+y 的结果转换为 int 整型 (float) 100; //将数值 100(默认为int类型)转换为 float 类型
强制类型转换示例:
#include
int main(){ int sum = 103; //总数 int count = 7; //数目 double average; //平均数 average = (double) sum / count; printf("Average is %lf!\n", average); return 0; } 运行结果:
Average is 14.714286!
sum 和 count 都是 int 类型,如果不进行干预,那么sum / count的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。既然 average 是 double 类型,为何不充分利用,尽量提高运算结果的精度呢?为了达到这个目标,我们只要将 sum 或者 count 其中之一转换为 double 类型即可。上面的代码中,我们将 sum 强制转换为 double 类型,这样sum / count的结果也将变成 double 类型,就可以保留小数部分了,average 接收到的值也会更加精确。
在这段代码中,有两点需要注意:
1 . 对于除法运算,如果除数和被除数都是整数,那么运算结果也是整数,小数部分将被直接丢弃;如果除数和被除数其中有一个是小数,那么运算结果也是小数。2 . ( )的优先级高于/,对于表达式(double) sum / count,会先执行(double) sum,将 sum 转换为 double 类型,然后再进行除法运算,这样运算结果也是 double 类型,能够保留小数部分。注意不要写作(double) (sum / count),这样写运算结果将是 3.000000,仍然不能保留小数部分。
类型转换只是临时性的
无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。请看下面的例子:#include
int main(){ double total = 400.8; //总价 int count = 5; //数目 double unit; //单价 int total_int = (int)total; unit = total / count; printf("total=%lf, total_int=%d, unit=%lf\n", total, total_int, unit); return 0; } 运行结果:
total=400.800000, total_int=400, unit=80.160000
注意看第 6 行代码,total 变量被转换成了 int 类型才赋值给 total_int 变量,而这种转换并未影响 total 变量本身的类型和值。如果 total 的值变了,那么 total 的输出结果将变为 400.000000;如果 total 的类型变了,那么 unit 的输出结果将变为 80.000000。自动类型转换 VS 强制类型转换
在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如 void * 到 int *,int 到 char * 等。可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。
可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。
什么是大小端模式?
大端模式(big endian)和小端模式(little endian)。最早是小说中出现的词,和计算机本来没关系的。后来计算机通信发展起来后,遇到一个问题就是:在串口等串行通信中,一次只能发送1个字节。这时候我要发送一个int类型的数就遇到一个问题。int类型有4个字节,我是按照:byte0 byte1 byte2 byte3这样的顺序发送,还是按照byte3 byte2 byte1 byte0这样的顺序发送。规则就是发送方和接收方必须按照同样的字节顺序来通信,否则就会出现错误。这就叫通信系统中的大小端模式。这是大小端这个词和计算机挂钩的最早问题。
现在我们讲的这个大小端模式,更多是指计算机存储系统的大小端。在计算机内存/硬盘/Nnad中。因为存储系统是32位的,但是数据仍然是按照字节为单位的。于是乎一个32位的二进制在内存中存储时有2种分布方式:高字节对应高地址(小端模式)、高字节对应低地址(大端模式)
比如将int数据0x44332211存入地址0x00000001、0x00000002、0x00000003、0x00000004这四个直接地址中,有以下两种存储方式:
大端模式和小端模式本身没有对错,没有优劣,理论上按照大端或小端都可以,但是要求必须存储时和读取时按照同样的大小端模式来进行,否则会出错。
现实的情况就是:有些CPU公司用大端(譬如C51单片机);有些CPU用小端(譬如ARM)。(大部分是用小端模式,大端模式的不算多)。于是乎我们写代码时,当不知道当前环境是用大端模式还是小端模式时就需要用代码来检测当前系统的大小端。注意:大小端是针对字节来说的,而不是针对位来说的。
用C语言写一个函数来测试当前机器的大小端模式。
用union来测试机器的大小端模式;
指针方式来测试机器的大小端。
通信系统中的大小端
譬如要通过串口发送一个0x12345678给接收方,但是因为串口本身限制,只能以字节为单位来发送,所以需要发4次;接收方分4次接收,内容分别是:0x12、0x34、0x56、0x78.接收方接收到这4个字节之后需要去重组得到0x12345678(而不是得到0x78563412).
所以在通信双方需要有一个默契,就是:先发/先接的是高位还是低位?这就是通信中的大小端问题。
一般来说是:先发低字节叫小端;先发高字节就叫大端。(我不能确定)实际操作中,在通信协议里面会去定义大小端,明确告诉你先发的是低字节还是高字节。
在通信协议中,大小端是非常重要的,大家使用别人定义的通信协议还是自己要去定义通信协议,一定都要注意标明通信协议中大小端的问题。
typedef是C语言中一个关键字,作用是用来定义或者叫重命名某种数据类型。
注意,是作用于类型,而不是变量。
用typedef只是将已存在的类型用一个新的名称代替,而不是增加新类型。
使用typedef便于程序的通用。
C语言中的类型一共有2种:一种是编译器定义的原生类型(基础数据类型,如int、double之类的);第二种是用户自定义类型,不是语言自带的是程序员自己定义的(譬如数组类型、结构体类型、函数类型·····)。
有时候自定义类型太长了,用起来不方便,所以用typedef给它重命名一个短点的名字。我的理解是,通常是用来取结构体、共用体这两类数据结构的别名,其他数据类型的符号本身也很简短。比如:
typedef int intName; //注意后面有分号 typedef struct { int a; //注意结构体内部定义时有分号 float b; } student; //结束时有分号
要注意typedef和宏定义的区别:
typedef int * intName; #define intStar int * //注意宏定义后面没有分号 intName a, b; //这种a和b都是int类型的指针 intStar c, d; //这种c是int类型的指针,而d只是一个普通int类型 /* typedef只是重命名,并不改变类型本质; 宏定义只是原封不动地替换; */
使用typedef的重要意义:
(1)简化类型的描述。
(2)很多编程体系下,人们倾向于不使用int、double等C语言内建类型,因为这些类型本身和平台是相关的(譬如int在16位机器上是16位的,在32位机器上就是32位的)。为了解决这个问题,很多程序使用自定义的中间类型来做缓冲。譬如linux内核中大量使用了这种技术:
内核中先定义:typedef int size_t; 然后在特定的编码需要下用size_t来替代int(譬如可能还有typedef int len_t),STM32的库中全部使用了自定义类型,譬如typedef volatile unsigned int vu32。
bool类型
C99 还提供了一个头文件
定义了 bool 代表 _Bool,true 代表 1,false 代表 0。只要导入 stdbool.h ,就能非常方便的操作布尔类型了。 C 语言的布尔类型(true 与 false) | 菜鸟教程
无符号数和0的比较