程序的主要目的就是对数据进行计算和处理,为了方便数据的管理,通常会对数据进行分类。就像根据容器的容量和形状不同,可以分成大杯中杯、大碗小碗等,不同的容器适合装不同的东西。
C语言根据数据的特性和用途进行了类型划分,不同类型的数据在内存中占据不同大小的空间。
一:整数类型
整数类型简称整型,就是用于存放整数值的,比如:12,30,3456等。
类型 | 存储大小 | 值范围 |
short signed short | 2 字节 | -32,768 (- 2^15)到 32,767 (2^15 -1) |
unsigned short | 2 字节 | 0 到 65,535 (2^16 - 1) |
int signed int | 16位:2 字节 32位:4 字节 | 16位:-32,768 (- 2^15 ) 到 32,767 (2^15-1) 32位:-2,147,483,648 (- 2^31) 到 2,147,483,647 (2^31 -1) |
unsigned int | 16位:2 字节 32位:4 字节 | 16位:0 到 65,535 (2^16-1) 32位:0 到4,294,967,295(2^32 -1) |
long signed long | 32位:4 字节 64位:8 字节 | 32位:-2,147,483,648 (- 2^31) 到 2,147,483,647 (2^31 - 1) 64位:9223372036854775808(-2^63) 到9223372036854775807 (2^63-1) |
unsigned long | 4 或 8 字节 | 32位:0 到 4,294,967,295 (2^32 - 1) 64位:0 ~ 18446744073709551615 (2^64 - 1) |
long long signed long long | 8字节 | 9223372036854775808(-2^63) 到9223372036854775807 (2^63-1) |
unsigned long long | 8 字节 | 0 ~ 18446744073709551615 (2^64 - 1) |
关于存储大小单位:
bit(位):计算机中的最小存储单位,表示一个二进制位。
byte(字节):计算机中基本存储单元,1byte = 8bit。
(1)整型注意事项
各类型存储大小受到操作系统、编译器、硬件平台的影响。
整型分为有符号signed和无符号unsigned两种,默认是signed。
开发中使用整型一般用int型,如果不足以表示大数,可以使用long long。
(2)字面量后缀
字面量是源代码中一个固定值的表示法,用于直接表示数据
一个整数字面量默认是int类型。
如果需要表示 long 类型字面量,需要添加后缀 l 或 L。
如果需要表示 long long 类型字面量,需要添加后缀 ll 或 LL。
如果需要表示无符号整数字面量,需要添加后缀 u 或 U, 注意, u 和 l 可以结合使用,不分先后,如 ul(无符号long类型)、ULL(无符号 long long类型),lu(无符号 long 类型) 等。
(3)格式占位符
- %d 对应 int 类型,%u 对应 unsigned int 类型。
- %hd 对应 short 类型,%hu 对应 unsigned short 类型。
- %ld 对应 long 类型,%lu 对应 unsigned long 类型。
- %lld 对应 long long 类型,%llu 对应 unsigned long long 类型。
#include <stdio.h>
int main()
{
// short 类型
short a1 = 10; // 等同于 signed short a1 = 10;
short a2 = -10; // 等同于 signed short a1 = -10;
// unsigned short a3 = -20;
unsigned short a3 = 20;
printf("a1=%d; a2=%d; a3=%d \n", a1, a2, a3); // a1=10; a2=-10; a3=20
// int 类型
int b1 = 100; // 等同于 signed int a1 = 100;
int b2 = -100; // 等同于 signed int a1 = -100;
unsigned int b3 = 200u;
unsigned b4 = 300U; // 等同于 unsigned int b4 = 300U;
printf("b1=%d; b2=%d; b3=%d; b4=%d \n", b1, b2, b3, b4); // b1=100; b2=-100; b3=200; b4=300
// long 类型
long c1 = 1000l; // 等同于 signed long c1 = 1000l;
long c2 = -1000L; // 等同于 signed long c2 = -1000L;
unsigned long c3 = 2000ul;
printf("c1=%ld; c2=%ld; c3=%ld \n", c1, c2, c3); // c1=100; c2=-1000; c3=2000
// long long 类型
long long d1 = 10000ll; // 等同于 signed long long d1 = 10000ll;
long long d2 = -10000LL; // 等同于 signed long long d2 = -10000LL;
unsigned long long d3 = 20000ULL;
printf("d1=%lld; d2=%lld; d3=%lld \n", d1, d2, d3); // d1=10000; d2=-10000; d3=20000
return 0;
}
C 语言的整数类型(short、int、long)在不同计算机上,占用的字节宽度可能是不一样的。程序员有时需要精准的字节宽度,以提高代码的可移植性,尤其是嵌入式开发中,使用精确宽度类型可以确保代码在各种平台上的一致性。
标准库的头文件 <stdint.h> 中定义了一些新的类型别名,如下:
类型名称 | 含义 |
int8_t | 8 位有符号整数 |
int16_t | 16 位有符号整数 |
int32_t | 32 位有符号整数 |
int64_t | 64 位有符号整数 |
uint8_t | 8 位无符号整数 |
uint16_t | 16 位无符号整数 |
uint32_t | 32 位无符号整数 |
uint64_t | 64 位无符号整数 |
上面这些都是类型别名,编译器会指定它们指向的底层类型。比如,某个系统中,如果 int 类型为32位, int32_t 就会指向 int ;如果 long 类型为32位, int32_t 则会指向 long 。
示例代码:
#include <stdio.h>
#include <stdint.h>
int main()
{
// 变量 x32 声明为 int32_t 类型,可以保证是32位(4个字节)的宽度。
int32_t x32 = 45933945;
printf("x32=%d\n", x32);
return 0;
}
二:浮点类型
浮点类型可以表示一个小数,比如:123.4,7.8,0.12等。
类型 | 存储大小 | 值范围 | 有效小数位数 |
float 单精度 | 4 字节 | 1.2E-38 到 3.4E+38 | 6 ~ 9 |
double 双精度 | 8 字节 | 2.3E-308 到 1.7E+308 | 15 ~18 |
long double 长双精度 | 32位:10字节 64位:16字节 | 32位:与 double 相同或更大 64位:3.4E-4932到1.2E+4932 | 18或更多 |
(1)浮点型注意事项
各类型的存储大小和精度受到操作系统、编译器、硬件平台的影响。
浮点型数据有两种表示形式:
十进制数形式:如:5.12、512.0f、.512(0.512 可以省略 0)
科学计数法形式:如:5.12e2、5.12E-2
开发中用到浮点型数字,建议使用double型,如精度要求更高可以使用long double 型。
(2)字面量后缀
- 浮点数字面量默认是double型,
- 如果需要表示float类型字面量,须加后缀 f 或 F。
- 如果需要表示long double类型字面量,需加后缀 l 或 L。
(3)格式占位符
在C语言中,占位符是一种用于格式化输出的特殊字符,通常用于 printf() 等输出函数中,用于指定输出的格式和内容。
- %f 是浮点类型的格式占位符,在printf中对应double类型(float类型会转换成double来处理);默认会保留6位小数,可以指定小数位数,如:%.2f 表示保留2位小数。
- %lf在printf中和 %f意思相同(C99标准加入),也对应double类型,默认保留6位小数,可以指定小数位数,如:%.2lf 表示保留2位小数。但需要注意的是,在scanf中 %lf和 %f含义不同:输入一个float类型数据时使用 %f;而输入double类型时必须使用 %lf。
- %Lf 对应的是long double 类型,默认保留6位小数,可以指定小数位数,如: %.2Lf 表示保留2位小数。需要注意的是,输入输出 long double 类型都必须使用 %Lf 占位符。
- %e 对应科学计数法表示的浮点数,可以指定尾数部分所保留的小数位数,如 %.2e 表示尾数部分保留两位小数
#include <stdio.h>
int main()
{
// double 类型
double a1 = 3.1415;
double a2 = .12345678;
double a3 = -2e12;
double a4 = 1.9823e2;
printf("a1=%f, a2=%.10f, a3=%.2lf, a4=%lf \n", a1, a2, a3, a4);
printf("a1=%e, a2=%.2e, a3=%e, a4=%e \n", a1, a2, a3, a4);
// float 类型
float b1 = 3.1415f;
float b2 = .123456f;
float b3 = -2e12f;
float b4 = 1.9823e2f;
printf("b1=%f, b2=%f, b3=%.0f, b4=%f \n", b1, b2, b3, b4);
printf("b1=%e, b2=%.2e, b3=%e, b4=%e \n\n", b1, b2, b3, b4);
return 0;
}
三:字符类型
字符类型 char 可以表示单个字符,如一个数字、一个字母、一个符号。
char类型的字面量是用单引号括起来的单个字符。
可以使用转义字符 \ 表示特殊含义的字符。
转义字符 | 说明 |
\b | 退格 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符 |
\” | 双引号 |
\’ | 单引号 |
\\ | 反斜杠 |
多个字符称为字符串,在C语言中使用char数组表示,数组不是基本数据类型,而是构造类型,我们后续专门讲解。
使用%c表示char类型。
- C语言中,char类型本质是一个整数,是ASCII码中对应的数字,存储长度是 1 个字节,char类型也可以进行数学运算。
- 字符型同样分为signed char(无符号)和unsigned char(有符号),其中signed char取值范围-128 ~ 127,unsigned char取值范围0 ~ 255。默认是否带符号取决于当前运行环境。
(1)ASCII 码介绍
ASCII(American Standard Code for Information Interchange)码是一种用于表示文本字符的字符编码标准,一共规定了128个字符的编码,比如空格“SPACE” 是32(二进制00100000),大写的字母A是65(二进制01000001)。
#include <stdio.h>
int main()
{
// char 类型字面量需要使用单引号包裹
char a1 = 'A';
char a2 = '9';
char a3 = '\t';
printf("c1=%c, c3=%c, c2=%c \n", a1, a3, a2);
// char 类型本质上整数可以进行运算
char b1 = 'b';
char b2 = 101;
printf("%c->%d \n", b1, b1);
printf("%c->%d \n", b2, b2);
printf("%c+%c=%d \n", b1, b2, b1 + b2);
// char 类型取值范围
unsigned char c1 = 200; // 无符号char取值范围 0 ~255
signed char c2 = 200; // 有符号char取值范围 -128~127,c2会超出范围
char c3 = 200; // 当前系统,char 默认是 signed char
printf("c1=%d, c2=%d, c3=%d", c1, c2, c3);
return 0;
}
四:布尔类型
布尔值用于表示真、假两种状态,通常用于逻辑运算和条件判断。
C89标准没有定义布尔类型,判断真假时以0为假,非0为真 ,但这种做法不直观,我们一般需要借助C语言的宏定义。
#include <stdio.h>
// 宏定义
#define BOOL int
#define TURE 1
#define FALSE 0
int main()
{
// 使用整型表示真假两种状态
// int isPass = 0;
// int isOk = 1;
// 借助于宏定义
BOOL isPass = FALSE;
BOOL isOk = TURE;
printf("isPass=%d, isOk=%d \n", isPass, isOk);
if (isPass)
{
printf("Pass");
}
if (isOk)
{
printf("Ok");
}
return 0;
}
C99标准提供了_Bool 型,_Bool仍是整数类型,但与一般整型不同的是,_Bool变量只能赋值为0或1,非0的值都会被存储为1。
#include <stdio.h>
int main()
{
// 使用 _BOOL 类型
_Bool isPass = 0;
//_Bool isOk = 1;
_Bool isOk = -4;
printf("isPass=%d, isOk=%d \n", isPass, isOk);
if (isPass)
{
printf("Pass");
}
if (isOk)
{
printf("Ok");
}
return 0;
}
C99标准还提供了一个头文件 <stdbool.h> 定义了bool代表_Bool,true代表1,false代表0。
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool isPass = false;
bool isOk = true;
printf("isPass=%d, isOk=%d \n", isPass, isOk);
if (isPass)
{
printf("Pass");
}
if (isOk)
{
printf("Ok");
}
return 0;
}
五:获取数据的存储大小
使用sizeof 可以获取数据类型或变量、字面量的存储大小,单位是字节。sizeof返回一个size_t类型的无符号整数值,格式占位符是 %zu。
size_t 通常是 unsigned int 或 unsigned long 的别名,具体是哪个类型的别名,由系统和编译器决定。
示例代码:
#include <stdio.h>
int main()
{
// 计算数据类型的大小, 必须使用括号将数据类型关键字包裹起来
printf("char:%zu \n", sizeof(char)); // char:1
printf("short:%zu \n", sizeof(short)); // short:2
printf("int:%zu \n", sizeof(int)); // int:4
printf("long:%zu \n", sizeof(long)); // long:4
printf("long long:%zu \n", sizeof(long long)); // long long:8
printf("float:%zu \n", sizeof(float)); // float:4
printf("double:%zu \n", sizeof(double)); // double:8
printf("long double:%zu \n", sizeof(long double)); // long double:16
printf("\n");
// 计算字面量数据的大小,字面量可以省略括号
printf("%zu \n", sizeof('a')); // 4
printf("%zu \n", sizeof(431)); // 4
printf("%zu \n", sizeof 4.31); // 8
printf("\n");
// 计算变量的大小,变量可以省略括号
char a = 'A';
int b = 90;
long long c = 100;
double d = 10.8;
printf("a: %zu \n", sizeof(a)); // a: 1
printf("b: %zu \n", sizeof b); // b: 4
printf("c: %zu \n", sizeof(c)); // c: 8
printf("d: %zu \n", sizeof(d)); // d: 8
return 0;
}
六:数据类型转换
(1)自动类型转换(隐式转换)
不同类型的数据进行混合运算,会发生数据类型转换,窄类型会自动转为宽类型,这样不会造成精度损失。
- 不同类型整数进行运算,窄类型整数自动转换为宽类型整数。
- 不同类型浮点数进行运算,精度小的类型自动转换为精度大的类型。
- 整数与浮点数进行运算,整数自动转换为浮点数。
#include <stdio.h>
int main()
{
// 整型提升
short s1 = 10;
int n1 = 40000;
// 运算过程中,变量 s1 是 short 类型,会自动转为 int 类型
printf("%d \n", s1 + n1);
// 有符号整数自动转为无符号整数
// int n2 = 100;
int n2 = -100;
unsigned int n3 = 20;
// 负数转为无符号整数,两者绝对值的和是无符号整数的最大值再加 1
printf("%u \n", n2 + n3);
// 不同类型的浮点数运算,精度低的转诶精度高的
float f1 = 1.25f;
double d2 = 4.58667435;
// printf("%f \t", f1);
printf("%.10f\n", f1 + d2);
// 整型与浮点型运算,整型转为浮点型
int n4 = 10;
double d3 = 1.67;
printf("%f", n4 + d3);
return 0;
}
(2)赋值时的自动类型转换
在赋值运算中,赋值号两边量的数据类型不同时,等号右边的类型将转换为左边的类型。 如果窄类型赋值给宽类型,不会造成精度损失;如果宽类型赋值给窄类型,会造成精度损失。
#include <stdio.h>
int main()
{
// 赋值 窄类型赋值给宽类型
int a1 = 10;
double a2 = a1;
printf("%f \n", a2);
// 赋值 宽类型赋值给窄类型
double b1 = 1.2;
int b2 = b1;
printf("%d", b2);
return 0;
}
(3)强制类型转换(显式转换)
隐式类型转换中的宽类型赋值给窄类型,编译器是会产生警告的,提示程序存在潜在的隐患,如果非常明确地希望转换数据类型,就需要用到强制(或显式)类型转换。
(类型名)变量、常量或表达式
#include <stdio.h>
int main()
{
double d1 = 1.934;
double d2 = 4.2;
int num1 = (int)d1 + (int)d2; // d1转为1,d2转为4,结果是5
int num2 = (int)(d1 + d2); // d1+d2=6.134,6.134转为6
int num3 = (int)(3.5 * 10 + 6 * 1.5); // 35.0 + 9.0 = 44.0 -> int = 44
printf("num1=%d \n", num1);
printf("num2=%d \n", num2);
printf("num3=%d \n", num3);
return 0;
}
本章的内容就到这里。
关注我一起成为嵌入式大佬。