在 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;
}
/*
1
2
3
*/
在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 存储最大字节数 : 4
float 最小值: 1.175494E-38
float 最大值: 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上对应的值是 99
int 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 值是 99
float 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.000000
mean2 = (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.50
printf("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.600000
50
*/
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~+127
short int 2 -32768~+32767
int 4 -2147483648~+2147483647
long int 4 -2147483648~+2141483647
long 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 00000001
2、得反码: 11111111 11111111 11111111 11111110
3、得补码: 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, 字符 = A
ASCII 值 = 66, 字符 = B
ASCII 值 = 67, 字符 = C
ASCII 值 = 68, 字符 = D
ASCII 值 = 69, 字符 = E
ASCII 值 = 70, 字符 = F
ASCII 值 = 71, 字符 = G
ASCII 值 = 72, 字符 = H
ASCII 值 = 73, 字符 = I
ASCII 值 = 74, 字符 = J
ASCII 值 = 75, 字符 = K
ASCII 值 = 76, 字符 = L
ASCII 值 = 77, 字符 = M
ASCII 值 = 78, 字符 = N
ASCII 值 = 79, 字符 = O
ASCII 值 = 80, 字符 = P
ASCII 值 = 81, 字符 = Q
ASCII 值 = 82, 字符 = R
ASCII 值 = 83, 字符 = S
ASCII 值 = 84, 字符 = T
ASCII 值 = 85, 字符 = U
ASCII 值 = 86, 字符 = V
ASCII 值 = 87, 字符 = W
ASCII 值 = 88, 字符 = X
ASCII 值 = 89, 字符 = Y
ASCII 值 = 90, 字符 = Z
ASCII 值 = 91, 字符 = [
ASCII 值 = 92, 字符 = \
ASCII 值 = 93, 字符 = ]
ASCII 值 = 94, 字符 = ^
ASCII 值 = 95, 字符 = _
ASCII 值 = 96, 字符 = `
ASCII 值 = 97, 字符 = a
ASCII 值 = 98, 字符 = b
ASCII 值 = 99, 字符 = c
ASCII 值 = 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
*/