在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式
C 中的类型可分为以下几种:
| 类型 | 描述 |
|---|---|
| void 类型 | 类型说明符 void 表明没有可用的值 |
| 基本类型 | 它们是算术类型,包括两种类型:整数类型和浮点类型 |
| 枚举类型 | 它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量 |
| 派生类型 | 它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型 |
数组类型和结构类型统称为聚合类型。函数的类型指的是函数返回值的类型
void 类型
void 类型指定没有可用的值。它通常用于以下三种情况下:
| 类型 | 描述 |
|---|---|
| 函数返回为空 | C 中有各种函数都不返回值,不返回值的函数的返回类型为空 例如 void exit (int status); |
| 函数参数为空 | C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。 例如 int rand(void); |
| 指针指向 void | 类型为 void 的指针代表对象的地址,而不是类型 例如内存分配函数 **void malloc( size_t size );** 返回指向 void 的指针,可以转换为任何数据类型 |
如果现在您还是无法完全理解 void 类型,不用太担心,在后续的章节中我们将会详细讲解这些概念
整数类型
关于标准整数类型的存储大小和值范围的细节:
| 类型 | 存储大小 | 值范围 |
|---|---|---|
| char | 1 字节 | -128 到 127 或 0 到 255 |
| unsigned char | 1 字节 | 0 到 255 |
| signed char | 1 字节 | -128 到 127 |
| short | 2 字节 | -32,768 到 32,767 |
| unsigned short | 2 字节 | 0 到 65,535 |
| 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 |
| long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
| unsigned long | 4 字节 | 0 到 4,294,967,295 |
| long long | 8 字节 |
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主 以下列出了32位系统与64位系统的存储大小的差别(windows 相同):

为了得到某个类型或某个变量在特定平台上的准确大小,您可以使用 sizeof(type) 获取对象或类型的存储字节大小
下面的实例演示了获取 int 类型的大小:
#include <stdio.h>#include <limits.h>int main(){printf("int 存储大小 : %lu \n", sizeof(int));return 0;}/*int 存储大小 : 4*/
%lu 为 32 位无符号整数,详细说明查看 C库函数 - printf()
C语言中的进制表示
以0b开头为2进制 0b11101101以0开头为8进制 045,021默认为10进制 10 ,20以0x开头为16进制 0x21458adf
enum(枚举)
枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读
枚举语法定义格式为:
enum 枚举名 {枚举元素1,枚举元素2,……};
接下来我们举个例子,比如:一星期有 7 天,如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名:
#define MON 1#define TUE 2#define WED 3#define THU 4#define FRI 5#define SAT 6#define SUN 7
这个看起来代码量就比较多,接下来我们看看使用枚举的方式:
enum DAY{MON=1, TUE, WED, THU, FRI, SAT, SUN};
这样看起来是不是更简洁了。
⚠️注意: 第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
可以在定义枚举类型时改变枚举元素的值:
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5
枚举变量的定义
前面我们只是声明了枚举类型,接下来我们看看如何定义枚举变量。
我们可以通过以下三种方式来定义枚举变量
1、先定义枚举类型,再定义枚举变量
num DAY{MON=1, TUE, WED, THU, FRI, SAT, SUN};enum DAY day;
2、定义枚举类型的同时定义枚举变量
enum DAY{MON=1, TUE, WED, THU, FRI, SAT, SUN} day;
3、省略枚举名称,直接定义枚举变量
enum{MON=1, TUE, WED, THU, FRI, SAT, SUN} day;
#include <stdio.h>enum DAY{MON = 1,TUE,WED,THU,FRI,SAT,SUN};int main(){enum DAY day1;enum DAY day2;enum DAY day3 = WED;day1 = MON;day2 = TUE;printf("%d\n", day1);printf("%d\n", day2);printf("%d\n", day3);return 0;}/*123*/
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。
不过在一些特殊的情况下,枚举类型必须连续是可以实现有条件的遍历。
以下实例使用 for 来遍历枚举的元素:
#include <stdio.h>enum DAY{MON=1, TUE, WED, THU, FRI, SAT, SUN} day;int main(){// 遍历枚举元素for (day = MON; day <= SUN; day++) {printf("枚举元素:%d \n", day);}}/*枚举元素:1枚举元素:2枚举元素:3枚举元素:4枚举元素:5枚举元素:6枚举元素:7*/
以下枚举类型不连续,这种枚举无法遍历
enum{ENUM_0,ENUM_10 = 10,ENUM_11};
枚举在 switch 中的使用:
#include <stdio.h>#include <stdlib.h>int main(){int a;enum color { red=1, green, blue } favorite_color;printf("%u,%u,%u,%u,\n",red,green,blue,favorite_color); //用户输入数字来选择颜色printf("请输入你喜欢的颜色: (1.red, 2.green, 3.blue): "); //用户输入数字来选择颜色// scanf("%u", &a); //这个也可以scanf("%u", &favorite_color);// 输出结果switch (favorite_color){case red:printf("你喜欢的颜色是红色\n");break;case green:printf("你喜欢的颜色是绿色\n");break;case blue:printf("你喜欢的颜色是蓝色\n");break;default:printf("你没有选择你喜欢的颜色\n");}return 0;}/*1,2,3,32766,请输入你喜欢的颜色: (1.red, 2.green, 3.blue): 3你喜欢的颜色是蓝色*/
将整数转换为枚举
以下实例将整数转换为枚举:
#include <stdio.h>#include <stdlib.h>int main(){enum day{saturday,sunday,monday,tuesday,wednesday,thursday,friday} workday;int a = 1;enum day weekend;weekend = ( enum day ) a; //类型转换//weekend = a; //错误printf("weekend:%d",weekend);return 0;}/*weekend:1*/
浮点类型
关于标准浮点类型的存储大小、值范围和精度的细节:
| 类型 | 存储大小 | 值范围 | 精度 |
|---|---|---|---|
| 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 位小数 |
头文件 float.h 定义了宏,在程序中可以使用这些值和其他有关实数二进制表示的细节。下面的实例将输出浮点类型占用的存储空间以及它的范围值:
#include <stdio.h>#include <float.h>int main(){printf("float 存储最大字节数 : %lu \n", sizeof(float));printf("float 最小值: %E\n", FLT_MIN);printf("float 最大值: %E\n", FLT_MAX);printf("精度值: %d\n", FLT_DIG);return 0;}/*float 存储最大字节数 : 4float 最小值: 1.175494E-38float 最大值: 3.402823E+38精度值: 6*/
%E 为以指数形式输出单、双精度实数,详细说明查看 C 库函数 - printf()
FLT_MAX FLT_DIG C标准库
单精度常量:2.3f双精度常量:2.3,默认为双精度
类型转化
1、数据类型转换:C 语言中如果一个表达式中含有不同类型的常量和变量,在计算时,会将它们自动转换为同一种类型;在 C 语言中也可以对数据类型进行强制转换;
2、自动转换规则:
- a)浮点数赋给整型,该浮点数小数被舍去;
- b)整数赋给浮点型,数值不变,但是被存储到相应的浮点型变量中;
3、强制类型转换形式: (类型说明符)(表达式)
整数提升
整数提升是指把小于 int 或 unsigned int 的整数类型转换为 int 或 unsigned int 的过程。请看下面的实例,在 int 中添加一个字符:
#include <stdio.h>int main(){int i = 17;char c = 'c'; // ASCII上对应的值是 99int sum;sum = i + c;printf("value of sum : %d\n", sum);}/*value of sum : 116*/
在这里,sum 的值为 116,因为编译器进行了整数提升,在执行实际加法运算时,把 ‘c’ 的值转换为对应的 ascii 值
常用的算术转换
常用的算术转换是隐式地把值强制转换为相同的类型。编译器首先执行 整数提升,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型:
如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如右边图所示。
常用的算术转换不适用于赋值运算符、逻辑运算符 && 和 ||。让我们看看下面的实例来理解这个概念:
#include <stdio.h>int main(){int i = 17;char c = 'c'; // ascii 值是 99float sum;sum = i + c;printf("value of sum : %f\n", sum);}/*在这里,c 首先被转换为整数,但是由于最后的值是 float 型的,所以会应用常用的算术转换,编译器会把 i 和 c 转换为浮点型,并把它们相加得到一个浮点数。value of sum : 116.000000*/
⚠️注意:C 语言中 printf 输出 double 和 float 都可以用 %f 占位符 可以混用
- 而 double 可以额外用 %lf
- 而 scanf 输入情况下 double 必须用 %lf
- float 必须用 %f 不能混用
强制类型转换
强制类型转换是把变量从一种类型转换为另一种数据类型。例如,如果您想存储一个 long 类型的值到一个简单的整型中,您需要把 long 类型强制转换为 int 类型。您可以使用 强制类型转换运算符 来把值显式地从一种类型转换为另一种类型,如下所示:
(type_name) expression
请看下面的实例,使用强制类型转换运算符把一个整数变量除以另一个整数变量,得到一个浮点数:
#include <stdio.h>int main(){int sum = 17, count = 5;double mean1;double mean2;mean1 = sum / count; //自动转化 3->3.000000,所以 value of mean1 : 3.000000mean2 = (double)sum / count; //强制转化 17.0/5,所以 value of mean2 : 3.400000// mean2 = ((double)sum) / ((double)count);printf("value of mean1 : %f\n", mean1);printf("value of mean2 : %f\n", mean2);}
这里要注意的是强制类型转换运算符的优先级大于除法,因此sum的值首先被转换为double型,然后除以 count,得到一个类型为 double 的值。
类型转换可以是隐式的,由编译器自动执行,也可以是显式的,通过使用 强制类型转换运算符 来指定。在编程时,有需要类型转换的时候都用上强制类型转换运算符,是一种良好的编程习惯。
#include <stdio.h>#include <float.h>int main(){int i = 4, a, b;float f, x = 3.6, y = 5.2;a = x + y;b = (int)(x + y);f = 10 / i;// f = 10 / (float)i; //如果强制转化后 f=2.50printf("a=%d,b=%d,f=%.2f,x=%f\n", a, b, f, x);short s = 10;s += 20;s = s + 20;printf("%d\n", s);}/*例中先计算 x + y 值为 8.8,然后赋值给 a,因为a为整型,所以自取整数部分8,a = 8;接下来 b 把 x + y 强制转换为整型;最后 10 / i 是两个整数相除,结果仍为整数 2,把 2 赋给浮点数 f;x 为浮点型直接输出输出结果为:a=8,b=8,f=2.00,x=3.60000050*/
C语言的整型溢出问题 int、long、long long取值范围 最大最小值
溢出和取值范围
《C和指针》中写过:long与int:标准只规定long不小于int的长度,int不小于short的长度。
double与int类型的存储机制不同,long int的8个字节全部都是数据位,而double是以尾数,底数,指数的形式表示的,类似科学计数法,因此double比int能表示的数据范围更广。
long long在win32中是确实存在,长度为8个字节;定义为LONG64。 为什么会出现long int呢?在win32现在系统中,长度为4;在历史上,或者其他某些系统中,int长度为2,是short int。
即便是long long,在TI的有款平台中,长度为5也就是说,每种类型长度,需要sizeof才知道,如果可能,最好用union看看里面的数据,可以消除一些类型的假象长度。
类型名称 字节数 取值范围signed char 1 -128~+127short int 2 -32768~+32767int 4 -2147483648~+2147483647long int 4 -2147483648~+2141483647long long long int 8 -9223372036854775808~+9223372036854775807
unsigned int (unsigned long)4字节8位可表达位数:2^32=42 9496 7296范围:0 ~ 42 9496 7295 (42*10^8)int (long)4字节8位可表达位数:2^32=42 9496 7296范围:-21 4748 3648 ~ 21 4748 3647 (21*10^8)long long (__int64)8字节8位可表达位数:2^64=1844 6744 0737 0960 0000范围:-922 3372 0368 5477 5808 ~ 922 3372 0368 5477 5807 (922*10^16)unsigned long (unsigned __int64)8字节8位可表达位数:2^64=1844 6744 0737 0960 0000范围:0 ~ 1844 6744 0737 0955 1615 (1844*10^16)
最大值、最小值
在#include<climits>存有各个类型的最大值和最小值CHAR_MIN char的最小值SCHAR_MAX signed char 最大值SCHAR_MIN signed char 最小值UCHAR_MAX unsigned char 最大值SHRT_MAX short 最大值SHRT_MIN short 最小值USHRT_MAX unsigned short 最大值INT_MAX int 最大值INT_MIN int 最小值UINT_MAX unsigned int 最大值UINT_MIN unsigned int 最小值LONG_MAX long最大值LONG_MIN long最小值ULONG_MAX unsigned long 最大值FLT_MANT_DIG float 类型的尾数FLT_DIG float 类型的最少有效数字位数FLT_MIN_10_EXP 带有全部有效数的float类型的负指数的最小值(以10为底)FLT_MAX_10_EXP float类型的正指数的最大值(以10为底)FLT_MIN 保留全部精度的float类型正数最小值FLT_MAX float类型正数最大值
自己推算:
以int类型为例:int为4字节32位,其中首位用0表示正数,用1表示为负数。因此对于最大正数可以表示为:0x7fff ffff(7的二进制为0111,f二进制为1111)最大负数(-1)可以表示为:0xffff ffff最小负数可以表示为:0x8000 0000(8的二进制为1000)负数为正数的源码取反码再取补码,过程如下:1、-1的原码:10000000 00000000 00000000 000000012、得反码: 11111111 11111111 11111111 111111103、得补码: 11111111 11111111 11111111 11111111最小负数没有并没有原码和反码表示,最高位为1,其余位为0。就是最小负数。
转义字符
| 转义字符 | 意义 | ASCII码值(十进制) |
|---|---|---|
| \a | 响铃(BEL) | 007 |
| \b | 退格(BS) ,将当前位置移到前一列 | 008 |
| \f | 换页(FF),将当前位置移到下页开头 | 012 |
| \n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
| \r | 回车(CR) ,将当前位置移到本行开头 | 013 |
| \t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
| \v | 垂直制表(VT) | 011 |
| \\ | 代表一个反斜线字符’’\‘ | 092 |
| \‘ | 代表一个单引号(撇号)字符 | 039 |
| \“ | 代表一个双引号字符 | 034 |
| \? | 代表一个问号 | 063 |
| \0 | 空字符(NUL) | 000 |
| \ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
| \xhh | 十六进制所代表的任意字符 | 十六进制 |
printf() 格式输出
C 库函数 int printf(const char *format, …) 发送格式化输出到标准输出 stdout
printf()函数的调用格式为:
printf("<格式化字符串>", <参量表>);
printf() 函数的声明
int printf(const char *format, ...)
format 这是字符串,包含了要被写入到标准输出 stdout 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化
附加参数 根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同
返回值 如果成功,则返回写入的字符总数,否则返回一个负数
format 标签属性
format 标签属性是 %[flags][width][.precision][length]specifier,具体讲解如下
| 格式字符 | 意义 |
|---|---|
| %d | 以十进制形式输出带符号整数(正数不输出符号) |
| %u | 以十进制形式输出无符号整数 |
| %o | 以八进制形式输出无符号整数(不输出前缀0) |
| %x, %X | 以十六进制形式输出无符号整数(不输出前缀Ox) |
| %f | 以小数形式输出单、双精度实数 |
| %e, %E | 以指数形式输出单、双精度实数 |
| %g, %G | 以%f或%e中较短的输出宽度输出单、双精度实数 |
| %c | 输出单个字符 |
| %s | 输出字符串 |
| %p | 输出指针地址 |
| %lu | 32位无符号整数 |
| %llu | 64位无符号整数 |
| flags(标识) | 描述 |
|---|---|
| - | 在给定的字段宽度内左对齐,默认是右对齐(参见 width 子说明符) |
| + | 强制在结果之前显示加号或减号(+ 或 -),即正数前面会显示 + 号。默认情况下,只有负数前面会显示一个 - 号 |
| 空格(space) | 如果没有写入任何符号,则在该值前面插入一个空格 |
| # | 与 o、x 或 X 说明符一起使用时,非零值前面会分别显示 0、0x 或 0X |
与 e、E 和 f 一起使用时,会强制输出包含一个小数点,即使后边没有数字时也会显示小数点。默认情况下,如果后边没有数字时候,不会显示显示小数点
与 g 或 G 一起使用时,结果与使用 e 或 E 时相同,但是尾部的零不会被移除 | | 0 | 在指定填充 padding 的数字左边放置零(0),而不是空格(参见 width 子说明符) |
| width(宽度) | 描述 |
|---|---|
| (number) | 要输出的字符的最小数目。如果输出的值短于该数,结果会用空格填充。如果输出的值长于该数,结果不会被截断 |
| * | 宽度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前。 |
| .precision(精度) | 描述 |
|---|---|
| .number | 对于整数说明符(d、i、o、u、x、X):precision 指定了要写入的数字的最小位数。如果写入的值短于该数,结果会用前导零来填充。如果写入的值长于该数,结果不会被截断。精度为 0 意味着不写入任何字符。 |
对于 e、E 和 f 说明符:要在小数点后输出的小数位数。
对于 g 和 G 说明符:要输出的最大有效位数。
对于 s: 要输出的最大字符数。默认情况下,所有字符都会被输出,直到遇到末尾的空字符。
对于 c 类型:没有任何影响。
当未指定任何精度时,默认为 1。如果指定时不带有一个显式值,则假定为 0。 | | .* | 精度在 format 字符串中未指定,但是会作为附加整数值参数放置于要被格式化的参数之前 |
| length(长度) | 描述 |
|---|---|
| h | 参数被解释为短整型或无符号短整型(仅适用于整数说明符:i、d、o、u、x 和 X) |
| l | 参数被解释为长整型或无符号长整型,适用于整数说明符(i、d、o、u、x 和 X)及说明符 c(表示一个宽字符)和 s(表示宽字符字符串) |
| L | 参数被解释为长双精度型(仅适用于浮点数说明符:e、E、f、g 和 G) |
⚠️注意: C 语言中 printf 输出 double 和 float 都可以用 %f 占位符 可以混用,而 double 可以额外用 %lf。
而 scanf 输入情况下 double 必须用 %lf,float 必须用 %f 不能混用。
printf()案例
#include <stdio.h>int main(){int ch = 65;for (; ch <= 100; ch++){printf("ASCII 值 = %d, 字符 = %c\n", ch, ch);}return (0);}/*ASCII 值 = 65, 字符 = AASCII 值 = 66, 字符 = BASCII 值 = 67, 字符 = CASCII 值 = 68, 字符 = DASCII 值 = 69, 字符 = EASCII 值 = 70, 字符 = FASCII 值 = 71, 字符 = GASCII 值 = 72, 字符 = HASCII 值 = 73, 字符 = IASCII 值 = 74, 字符 = JASCII 值 = 75, 字符 = KASCII 值 = 76, 字符 = LASCII 值 = 77, 字符 = MASCII 值 = 78, 字符 = NASCII 值 = 79, 字符 = OASCII 值 = 80, 字符 = PASCII 值 = 81, 字符 = QASCII 值 = 82, 字符 = RASCII 值 = 83, 字符 = SASCII 值 = 84, 字符 = TASCII 值 = 85, 字符 = UASCII 值 = 86, 字符 = VASCII 值 = 87, 字符 = WASCII 值 = 88, 字符 = XASCII 值 = 89, 字符 = YASCII 值 = 90, 字符 = ZASCII 值 = 91, 字符 = [ASCII 值 = 92, 字符 = \ASCII 值 = 93, 字符 = ]ASCII 值 = 94, 字符 = ^ASCII 值 = 95, 字符 = _ASCII 值 = 96, 字符 = `ASCII 值 = 97, 字符 = aASCII 值 = 98, 字符 = bASCII 值 = 99, 字符 = cASCII 值 = 100, 字符 = d*/
#include <stdio.h>int main(){char ch = 'A';char str[20] = "www.baidu.com";float flt = 10.234;int no = 150;double dbl = 20.123456;printf("字符为 %c \n", ch);printf("字符串为 %s \n", str);printf("浮点数为 %f \n", flt);printf("整数为 %d\n", no);printf("双精度值为 %lf \n", dbl);printf("八进制值为 %o \n", no);printf("十六进制值为 %x \n", no);return 0;}/*字符为 A字符串为 www.baidu.com浮点数为 10.234000整数为 150双精度值为 20.123456八进制值为 226十六进制值为 96*/
