C语言:数据类型与强制类型转换
补充知识:
bit (位):最小存储单位,可以存储0或1,不能再分割;1bit 等于一个二进制位;01011110 = 表示一个8位(bit)的二进制数
byte (字节):1个字节等于8个二进制位 (bit),可以表达$2^8$种组合。
二进制:0 | 1
16进制:0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A| B | C | D | E | F
1个16进制数 = 4位二进制位
2个16进制数= 一个字节(8位)
在线进制转换工具:链接
进制数的前缀
- 二进制:0b
- 八进制:0;输出格式化%o,显示进制数的前缀%#o
- 十进制:无任何前缀;输出格式化%d,显示进制数的前缀%#d
- 十六进制:0x或0X;输出格式化%x,显示进制数的前缀%#x 或 %#X
基本类型
- 整型
- 字符型
- 浮点型
- 单精度浮点型
- 双精度浮点型
- 枚举类型
构造类型
- 数组类型 []
- 结构体类型 struct
- 共用体类型 union
指针类型 *
空类型
基本数据类型
数据类型占用的字节大小和编译器的位数有很大关系。见:链接。下面以64位编译器为例
类型 | 字节 | 值范围 | 格式化输出 | |
字符型 | char | 1 | C字符集,-128 到 127 | %c |
整型 | int | 4 | -2,147,483,648 到 2,147,483,647 | %d |
无符号整型 | unsigned int | 4 | 0 到 4,294,967,296 | %u |
短整型 | short | 2 | -32,768 到 32,767 | %h |
长整型 | long | 4 | -2,147,483,648 到 2,147,483,647 | %l |
长长整型 | long long | 8 | 0~18,446,744,073,709,551,616 | %ll |
无符号长整型 | unsigned long | 4 | 0 到 4,294,967,295 | %ul |
单精度型 | float | 4 | $1.2e^{−38}$ 到 $3.4e^{38}$;6位小数 | %f |
双精度型 | double | 8 | $2.3e^{−308}$ 到 $1.7e^{308}$;15位小数 |
我们还可以再l(长整型)或d(整型)后面使用x(十六进制)或o(八进制),
- %lx:以十六进制格式打印long类型整数
- %lo:以八进制格式打印long类型整数
补充:我们可以用 sizeof 查看指定类型的大小。
整数溢出
在超过最大值时,unsigned int类型的变量从0开始;而int类型的变量则从−2147483648开始。注意,当超出其相应类型的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。
#include <stdio.h> int main(void) { int i = 2147483647; unsigned int j = 4294967295; printf("%d %d %d\n", i, i + 1, i + 2); // 2147483647 -2147483648 -2147483647 printf("%u %u %u\n", j, j + 1, j + 2); // 4294967295 0 1 }
字符型 (char)
char类型用于存储字符(如,字母或标点符号),但是从技术层面看,char是整数类型,实际上存储的是整数而不是字符。参见 ASCII码对照表。
注意:字符变量就是一个字符,并且只能由单引号 ‘ ’ 或者ASCII码中的数字定义。不能是双引号哈,双引号是字符串。
关于有符号还是无符号:有些C编译器把char实现为有符号类型,这意味着char可表示的范围是-128~127。而有些C编译器把char实现为无符号类型,那么char可表示的范围是0~255,可以查阅limits.h头文件得知。或者关键字char前面使用 signed char 或 unsigned char 。
浮点型
C语言中的浮点类型有float、double和long double类型。有符号的数字(包括小数点),或者后面紧跟e或E,表示10的指数。例如: 3.1415 、 .2 、 4e16 、 .8E-5 、 100. 。
小知识:默认情况下浮点型常量是double类型的精度
float i; // float类型 变量声明 i = 4.0 * 2.0;
4.0和2.0默认是double类型,但是i是float类型,这样计算的结果会被截断到float类型的宽度。这样做虽然计算精度更高,但是会减慢程序的运行速度。
i = 4.0f * 2.0f;
在浮点数后面加上f或F后缀可覆盖默认设置,编译器会将浮点型常量看作float类型,如 2.3f 和 9.11E9F 。使用l或L后缀使得数字成为long double类型,如 54.3l 和 4.32L 。注意,建议使用L后缀,因为字母l和数字1很容易混淆。
空类型(void)
通常用于以下三种情况
- 函数返回空:函数不返回值,或者返回空,例如 void exit (int status);
- 函数参数为空:函数不接受任何参数,不带参数的函数可以接受一个 void。例如 int rand(void);
- 指针指向void:类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
#include<stdio.h> #include<cfloat> int main() { printf("int占%d字节\n",sizeof(int)); // 4 printf("unsigned int占%d字节\n", sizeof(unsigned int)); // 4 printf("short int占%d字节\n", sizeof(short int)); //2 printf("long int占%d字节\n", sizeof(long int)); // 4 printf("float占%d字节\n", sizeof(float)); // 4 printf("double占%d字节\n", sizeof(double)); // 8 printf("浮点数的最大值:%E\n", FLT_MIN); // 1.175494E-38 printf("浮点数的最小值:%E\n", FLT_MAX); // 3.402823E+38 }
可移植类型:stdint.h和inttypes.h
在不同的系统(32位机、64位机)中,相同的数据类型代表的存储大小不一样,因此C99中的头文件stdint.h,使用 typedef 创建了很多的类型名,以确保C语言的类型在各系统中的功能相同。
比如 uint8_t 、 int32_t 等。其中u代表 unsigned char ,_t 代表 typedef 。
- uint8_t:无符号8位整型
- int32_t:有符号32位的整型,就是int的别名
最小整数类型,例如:int_least8_t是可容纳8位有符号整数值的类型中宽度最小的类型的一个别名。
最大有符号整数类型,可存储任何有效的有符号整数值,uintmax_t表示最大的无符号整数类型。这些类型有可能比long long和unsigned long类型更大,
最快类型集合,这组类型集合被称为最快最小宽度类型,例如:int_fast8_t被定义为系统中对8位有符号值而言运算最快的整数类型的别名。
所以uint8_t,uint16_t,uint32_t等都不是什么新的数据类型,它们只是使用typedef给类型起的别名,新瓶装老酒的把戏。不过,不要小看了typedef,它对于你代码的维护会有很好的作用。比如C中没有bool,于是在一个软件中,一些程序员使用int,一些程序员使用short,会比较混乱,最好就是用一个typedef来定义,如: typedef char bool;
附:C99标准中stdint.h的内容
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#ifndef __INTTYPES_H_ #define __INTTYPES_H_ /* Use [u]intN_t if you need exactly N bits */ typedef signed char int8_t; typedef unsigned char uint8_t; typedef int int16_t; typedef unsigned int uint16_t; typedef long int32_t; typedef unsigned long uint32_t; typedef long long int64_t; typedef unsigned long long uint64_t; typedef int16_t intptr_t; typedef uint16_t uintptr_t; #endif
C语言中的正负数
在C语言中 short、int、long都是带有正负号的,C语言规定,把内存的最高位作为符号位,在符号位中,用 0 表示正数,用 1 表示负数。
以 int 为例,它占用 32 位的内存,0~30 位表示数值,31 位表示正负号。如下图所示:
int a = 0b00000000000000000000000000000001; int b = 0b10000000000000000000000000000001; printf("a: %d\n",a); // a: 1 printf("b: %d\n",b); // b: -2147483647
如果不希望设置符号位,可以在数据类型前面加上 unsigned 关键字,这样,short、int、long 中就没有符号位了,所有的位都用来表示数值,正数的取值范围更大了。这也意味着,使用了 unsigned 后只能表示正数,不能再表示负数了。
枚举类型 enum
被用来定义在程序中只能赋予其一定的离散整数值的变量。
声明枚举类型
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 枚举方式(更加简洁):
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
一般情况下第一个枚举成员的默认值为 0,后续枚举成员的值在前一个成员上加 1。在这个实例中我们把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
当然我们也可以在定义枚举类型时改变枚举元素的值 enum season {spring, summer=3, autumn, winter};
定义枚举变量
我们可以通过以下三种方式来定义枚举变量
1、先声明枚举类型,再定义枚举变量
// 声明枚举类型 enum 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,SUM }; int main() { enum DAY day; // 定义枚举变量 day = WED; // 将WED复制给day printf("结果%d", WED); // 3 return 0; }
如果枚举类型是连续的则可以实现有条件的遍历。不连续的枚举类型无法遍历。
#include<stdio.h> // 声明枚举类型,定义枚举变量 enum DAY { MON=1,TUE,WED,THU,FRI,SAT,SUM } day; int main() { for (day = MON; day < SUM; day++) { printf("枚举类型:%d\n", day); } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//枚举类型:1 //枚举类型:2 //枚举类型:3 //枚举类型:4 //枚举类型:5 //枚举类型:6
枚举在 switch 中的使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> int main() { enum color { red = 1, green, blue }; // 枚举类型声明 enum color favorite_color; // 定义枚举变量 /* 用户输入数字来选择颜色 */ printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): "); scanf_s("%u", &favorite_color); /* 输出结果 */ switch (favorite_color) { case red: printf("你喜欢的颜色是红色"); break; case green: printf("你喜欢的颜色是绿色"); break; case blue: printf("你喜欢的颜色是蓝色"); break; default: printf("你没有选择你喜欢的颜色"); } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
请输入你喜欢的颜色: (1. red, 2. green, 3. blue): 1 你喜欢的颜色是红色
类型转换
强制类型转换(显式)是把变量从一种类型转换为另一种数据类型。
(类型名) 变量名
案例:
#include <stdio.h> int main() { int sum = 17, count = 5; double mean; mean = (double)sum / count; // 将sum强制类型转换为 double printf("Value of mean : %f\n", mean); }
强制类型转换运算符的优先级大于除法,因此 sum 的值首先被转换为 double 型,然后除以 count,得到一个类型为 double 的值。
类型转换可以是隐式的,由编译器自动执行,也可以是显式的,通过使用强制类型转换运算符来指定。
整数提升
整数提升是指把小于 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); //116 }
在这里,sum 的值为 116,因为编译器进行了整数提升,在执行实际加法运算时,把 'c' 的值转换为对应的 ascii 值。
常用的算术转换
常用的算术转换 是隐式地把值强制转换为相同的类型。编译器首先执行整数提升,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型:
优先级:long double --> double --> float --> unsigned long long --> long long --> unsigned long --> long --> unsigned int --> int
#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); // 116.000000 }
C 首先被转换为整数,但是由于最后的值是 float 型的,所以会应用常用的算术转换,编译器会把 i 和 c 转换为浮点型,并把它们相加得到一个浮点数。
整数转换枚举
#include<stdio.h> int main() { // 枚举类型的声明和定义 enum day { saturday, sunday, monday, tuesday, wednesday, thursday, friday } weekend; int a = 1; //变量的声明和初始化 weekend = (enum day) a; // 类型转换 //weekend = a; //错误 printf("weekend:%d", weekend); //1 return 0; }
参考
相关文章
- ?面向对象编程必备-UML类图能看的懂吗?
- 桌面端开发(Tauri)开启第一篇
- Java-什么是面向对象
- 苹果MacOS_虚拟机安装详细过程
- 21条最佳实践,全面保障 GitHub 使用安全
- 字节后端实习一面凉经
- 面向接口编程的好处-介绍API数据接口的好处!
- C语言大学生考试全面题库
- 数据治理很抽象吗
- Elasticsearch互联网主流分布式全文检索框架实战-ElasticStack(上)v7.14.0
- 分布式全局ID生成器原理剖析及非常齐全开源方案应用示例
- ElasticJob分布式任务调度应用v2.5.2
- 云原生概念你了解多少
- 主流微服务一站式解决方案Spring Cloud Alibaba入门看这篇就足够了-开篇v2.2.1.RELEASE
- Pulsar云原生分布式消息和流平台v2.8.0
- 回顾2022,展望2023,一个普通98年程序员的自述和分享
- SpringBoot+Mybatis-plus整合easyExcel批量导入Excel到数据库+导出Excel
- Springboot整合策略模式概念->使用场景->优缺点->企业级实战
- 三台服务器使用docker搭建redis一主二从三哨兵,概念-搭建-整合springboot
- IDEA中给源码添加自己注释——private-notes插件安装使用