0.1. 类型安全
- C以后的语言向两个方向发展
- C++/Java 更强调类型,对类型的检查更严格
- JavaScript、Python、PHP不看重类型,甚至不需要事先定义
- 支持强类型的观点认为明确的类型有助于尽早发现程序中的简单错误
- 反对强类型的观点认为过于强调类型迫使程序员面对底层、实现而非事务逻辑
- 总的来说,早期语言强调类型,面向底层的语言强调类型
- C语言需要类型,但是对类型的安全检查并不足够
1. 基本数据类型
1.1. 整型
下表列出了关于标准整数类型的存储大小和值范围的细节:
类型 | 存储大小 | 值范围 |
---|---|---|
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 |
-
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。以下列出了32位系统与64位系统的存储大小的差别(windows 相同):
-
为了得到某个类型或某个变量在特定平台上的准确大小,您可以使用
sizeof
运算符。
#include <stdio.h>
#include <limits.h>
int main()
{
printf("int 存储大小 : %lu \n", sizeof(int));
return 0;
}
1.2. 字符型
- ASCII 表
- A - 65
- a - 97
1.3. 浮点型
- 两个整数的运算的结果只能是整数
- 10/3*3=9
- 10.0/3=3.333333
- 10和10.0在C中是完全不同的数
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
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;
}
- %E 为以指数形式输出单、双精度实数
2. 枚举类型
-
每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从
0
开始递增 -
接下来我们举个例子,比如:一星期有 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
};
- 可以在定义枚举类型时改变枚举元素的值:
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1。也就说spring
的值为 0,summer
的值为 3,autumn
的值为 4,winter
的值为 5
2.1. 枚举变量的定义
- 先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
- 定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
- 省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
2.2. 实例
#include <stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day;
day = WED;
printf("%d",day);
return 0;
}
3
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
};
2.4. 枚举在 switch
中的使用
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color {
red=1, green, blue
};
enum color favorite_color;
/* 用户输入数字来选择颜色 */
printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
scanf("%u", &favorite_color);
/* 输出结果 */
switch (favorite_color)
{
case red:
printf("你喜欢的颜色是红色");
break;
case green:
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}
请输入你喜欢的颜色: (1. red, 2. green, 3. blue): 1
你喜欢的颜色是红色
2.5. 将整数转换为枚举
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum day
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
};
int a = 1;
enum day weekend;
weekend = ( enum day ) a; //类型转换
//weekend = a; //错误
printf("weekend:%d",weekend);
return 0;
}
weekend:1
3. void
类型
- 它通常用于以下三种情况下
- 函数返回为空
- 函数参数为空
- 指针指向
void
4. _Bool
类型
- C99 提供了
_Bool
型,但与一般整型不同的是,_Bool
变量只能赋值为 0 或 1,非 0 的值都会被存储为 1 - C99还提供了一个头文件 <stdbool.h> 定义了 bool 代表
_Bool
,true 代表 1,false 代表 0。
5. 派生类型
5.1. 数组类型
5.2. 指针类型
5.3. 结构体类型
5.4. 共用体
- 共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
5.4.1. 定义共用体
- 为了定义共用体,您必须使用
union
语句,方式与定义结构类似。union
语句定义了一个新的数据类型,带有多个成员。union
语句的格式如下
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
- union tag 是可选的
- member definition 是标准的变量定义,或者其他有效的变量定义
- 在共用体定义的末尾,最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的
- 共用体意味着一个变量(相同的内存位置)可以存储多个多种类型的数据
- 共用体占用的内存应足够存储共用体中最大的成员
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}
Memory size occupied by data : 20
5.4.2. 访问共用体成员
- 访问共用体的成员,我们使用成员访问运算符
.
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
6. 类型转换
6.1. 算术转换
- 常用的算术转换是隐式地把值强制转换为相同的类型。编译器首先执行整数提升,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型
int -> unsigned int -> long -> unsigned long -> long long -> unsigned long long -> float -> double -> long double
- 常用的算术转换不适用于赋值运算符、逻辑运算符
&&
和||
#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 );
}
Value of sum : 116.000000
6.2. 强制类型转换
(type_name) expression
#include <stdio.h>
int main()
{
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
//这里要注意的是强制类型转换运算符的优先级大于除法
printf("Value of mean : %f\n", mean );
}
Value of mean : 3.400000
6.3. 整数提升
- 整数提升是指把小于
int
或unsigned int
的整数类型转换为int
或unsigned int
的过程
#include <stdio.h>
int main()
{
int i = 17;
char c = 'c'; /* ascii 值是 99 */
int sum;
sum = i + c;//在执行实际加法运算时,把 'c' 的值转换为对应的 ASCII 值
printf("Value of sum : %d\n", sum );
}
Value of sum : 116