目录
一、基本数据类型
(一)字符类型
(二)整数类型
1、短整型 — short
2、整型 — int
3、长整型 — long
4、长长整型 — long long
(三)布尔类型
(四)浮点类型
1、单精度浮点型 — float
2、双精度浮点型 — double
3、长双精度浮点型 — long double
(五)本质探讨(重点!原理!核心!)
二、派生数据类型
(一)数组
(二)指针
(三)结构体
(四)联合体
(五)枚举
三、变量的定义与创建方式
(一)变量的定义
(二)变量的创建与初始化
(三)变量的分类
2、局部变量
四、数据类型知识点补充
(一)各种数据类型长度的计算(字节数)
1、sizeof 操作符
2、sizeof 操作的的计算
(二)signed 与 unsigned
(三)数据类型的极值表示
在 C 语言中,数据类型是一个非常基础且关键的概念。它本质是“在内存中开辟一块空间用于存储数据”,并且规定了数据的取值范围、存储方式以及可以对这些数据进行的操作。
合理使用数据类型不仅能提高程序的效率,还能确保程序的正确性和稳定性。
一、基本数据类型
(一)字符类型
字符类型只有一种,那就是char类型。
char 类型在内存中开辟 1 个字节(8 位)的存储空间。它既可以用来表示字符,也可以表示小整数。因为在 ASCII 编码中,每个字符都对应一个整数值,所以它本质上是整型类型,我们可以把 char 类型变量当作整数来使用。例如:
#include <stdio.h>int main()
{char ch = 'A';printf("字符: %c,对应的ASCII码: %d\n", ch, ch);return 0;
}
char 类型可以是有符号的(signed char),也可以是无符号的(unsigned char)。有符号 char 的取值范围是 -128 到 127,无符号 char 的取值范围是 0 到 255。C语言规定 char 类型默认是否带有正负号,由当前系统决定。
(二)整数类型
整数类型用于表示没有小数部分的数值。C 语言提供了多种整数类型,它们的主要区别在于占用的存储空间和表示的数值范围不同。
1、短整型 — short
short 类型在内存中开辟 2 个字节(16 位)的存储空间。
它同样有有符号和无符号之分。short 类型可以是有符号的(signed short),也可以是无符号的(unsigned short)。有符号 short 的取值范围是 -32768 到 32767,无符号 short 的取值范围是 0 到 65535。一般来说如果写成short类型,默认都是有符号的。示例代码如下:
#include <stdio.h>int main()
{short num = -100;printf("短整数: %d\n", num);return 0;
}
2、整型 — int
int 是最常用的整数类型,会在内存中开辟 4 个字节(32 位)的存储空间。
它同样有有符号和无符号之分。int 类型可以是有符号的(signed int),也可以是无符号的(unsigned int)。有符号 int 的取值范围是 -2147483648 到 2147483647,无符号 int 的取值范围是 0 到 4294967295。一般来说如果写成int类型,默认都是有符号的。示例代码如下:
#include <stdio.h>int main()
{int number = 12345;printf("整数: %d\n", number);return 0;
}
3、长整型 — long
long 类型在内存中开辟 4 个字节或 8 个字节(取决于编译器和系统),标准 C 语言只规定了 long 类型的长度至少和 int 一样长。
它同样有有符号和无符号之分。long 类型可以是有符号的(signed long),也可以是无符号的(unsigned long)。有符号 long 最小的取值范围是 -2147483648 到 2147483647 ,无符号 long 最小的取值范围是 0 到 4294967295。一般来说如果写成long类型,默认都是有符号的。
当给 long 类型变量赋值的整数字面量,超出了 int 类型所能表示的范围时,就必须添加 L 后缀,以此来明确告知编译器这个字面量是 long 类型。不然编译器可能会给出警告或者错误。
如果没有超过这个范围,可以不添加 L 后缀,编译器会自动进行类型转换,将int类型转换成 long 类型。
示例代码如下:
#include <stdio.h>int main()
{long num = 100; // 100 在 int 范围内,可自动转换为 long 类型printf("%ld\n", num);long large_num = 2147483648L; // int 最大值为 2147483647,所以这里需要 L 后缀printf("%ld\n", large_num); // 但是在VS2022中,long类型最大是4个字节,所以不会正确输出这个数字return 0;
}
4、长长整型 — long long
long long 类型在内存中开辟 8 个字节。它们用于表示更大范围的整数。
它同样有有符号和无符号之分。long 类型可以是有符号的(signed long long),也可以是无符号的(unsigned long long)。有符号 long long 的取值范围是 -2⁶³ 到 2⁶³ - 1,无符号 long long 取值范围是 0 到 2⁶⁴ - 1 。一般来说如果写成long long 类型,默认都是有符号的。
当给 long long 类型变量赋值的整数字面量超出了 int 或者 long 类型所能表示的范围时,就必须添加 LL 后缀,以此来明确告知编译器这个字面量是 long long 类型。
如果没有超过这个范围,可以不添加 LL 后缀,编译器会自动进行类型转换。
示例代码如下:
#include <stdio.h>int main()
{long long num = 100;// 100 在 int 范围内,先为 int 类型,再隐式转为 long longprintf("%lld\n", num);long long largeNum = 2147483648LL;// 明确使用 long long 类型,增强代码可移植性printf("%lld\n", largeNum);return 0;
}
(三)布尔类型
在 C99 标准之前,C 语言没有内置的布尔类型。而是使用整数 0 表示假,非 0 值表示真。
C99 标准引入了 _Bool 类型,是用来专门表示真假的。我们通常使用 true 和 false 作为布尔常量赋值给布尔类型的变量,但是使用布尔类型,必须包含 <stdbool.h> 头文件。
在 <stdbool.h> 头文件中,我们进行了三个重命名的宏定义,也就是起了一个别名。
#define bool _Bool 把 bool 定义成了 _Bool 的别名,这样在后续代码里就可以使用 bool 来声明布尔类型的变量。
但是注意不是说取了别名bool,_Bool这个名字就失效了,依旧是可以使用的。
#define true 1 和 #define false 0 分别把 true 和 false 定义为 1 和 0 的别名,方便在代码中使用布尔值。
其实布尔类型本质上也是在内存中开辟了 1 个字节(8 位)的存储空间。只不过说它的用途是接收true或者false两个布尔常量,用来专门表示真假。
示例代码如下:
#include <stdio.h>
#include <stdbool.h>int main()
{bool is_true = true;bool is_false = false;printf("布尔值: %d, %d\n", is_true, is_false);return 0;
}
(四)浮点类型
浮点类型用于表示带有小数部分的数值。
1、单精度浮点型 — float
float 类型在内存中开辟 4 个字节(32 位)的存储空间,它可以表示大约 6 到 7 位的有效数字,即整数位+小数位的合为6 ~ 7位。示例:
#include <stdio.h>int main()
{float f = 3.14159f;printf("单精度浮点数: %f\n", f);return 0;
}
当你在小数后面加上 f (例如 3.14159f),就表明这个小数是 float 类型的常量。
在将小数赋值给 float 类型的变量时,如果不使用 f,编译器会先把小数当作 double 类型,然后再将其转换为 float 类型。
虽然这种隐式转换通常不会出问题,但可能会造成不必要的性能开销。加上 f 之后,编译器就知道这是 float 类型的常量,能直接赋值给 float 类型的变量,避免了额外的转换操作。
2、双精度浮点型 — double
double 类型在内存中开辟 8 个字节(64 位)的存储空间,它可以表示大约 15 到 16 位的有效数字,即整数位+小数位的合为15 ~ 16位,精度比 float 更高。示例:
#include <stdio.h>int main()
{double d = 3.14159265358979;printf("双精度浮点数: %lf\n", d);return 0;
}
3、长双精度浮点型 — long double
long double 类型提供了比 double 更高的精度,在内存中开辟的存储空间通常大于 8 个字节,但具体大小取决于编译器和系统。示例:
#include <stdio.h>int main()
{long double ld = 3.14159265358979323846L;printf("长双精度浮点数: %Lf\n", ld);return 0;
}
当你要把一个小数赋值给 long double 类型的变量时,若不使用 L,编译器会把这个小数当作 double 类型,然后进行隐式类型转换。添加 L 能让编译器知晓这是一个 long double 类型的常量,从而直接进行赋值操作,避免额外的类型转换。
(五)本质探讨(重点!原理!核心!)
我们在开始的时候说了,数据类型仅仅是在内存中开辟了一块空间用于存储数据,并规定了在这块空间内数据的取值范围、存储方式。
我们只有知道了数据在这块内存空间中是怎么存储的,才可以根据内存空间的大小得到取值范围。
也就是先知道数据在内存中的存储方式,再根据内存空间得到取值范围。
前面提及的字符类型、整数类型、布尔类型,都可以化为对应的整数,它们在内存中的存储方式是将其整数值转化为二进制数字进行存储。不同点仅仅是开辟的内存空间大小不同,所以取值范围也不同。
我们可以说:因为它们的存储方式相同,所以这三种数据类型本质上就是整型。不同点仅仅是内存空间不同导致的取值范围不同。
所以即便是我们使用 int 类型创建的变量,也可以存储字符,只是会耗费更多的空间罢了,因为它们在内存中的存储方式是一样的。但是绝对不建议这样去写,写程序还是要规范。
而浮点型数据在内存中的存储,就不是简单得将数组转化为二进制形式了,它有自己的另一套规则,它的存储方式不同。
所以,在我看来,根据存储方式的划分,基本数据类型只有两类:整数类型与浮点类型。而前面之所以把整数类型又细分为三类,是为了在具体用途上做一个区分。
二、派生数据类型
(一)数组
数组是一组相同数据类型的元素的集合,这些元素在内存中连续存储。数组的大小在定义时必须确定。示例:
#include <stdio.h>int main()
{int arr[5] = {1, 2, 3, 4, 5};for (int i = 0; i < 5; i++){printf("数组元素: %d\n", arr[i]);}return 0;
}
(二)指针
指针是一种特殊的数据类型,它存储的是内存地址。通过指针可以直接访问和操作内存中的数据。示例:
#include <stdio.h>int main()
{int num = 10;int *ptr = #printf("变量的值: %d,指针指向的值: %d\n", num, *ptr);return 0;
}
(三)结构体
结构体是一种用户自定义的数据类型,它可以将不同类型的数据组合在一起。示例:
#include <stdio.h>// 定义结构体
struct Person
{char name[20];int age;
};int main()
{struct Person p = {"张三", 20};printf("姓名: %s,年龄: %d\n", p.name, p.age);return 0;
}
(四)联合体
联合体也是一种用户自定义的数据类型,它允许在相同的内存位置存储不同类型的数据,但同一时间只能使用其中一个成员。示例:
#include <stdio.h>// 定义联合体
union Data
{int i;float f;char str[20];
};int main()
{union Data data;data.i = 10;printf("整数: %d\n", data.i);data.f = 3.14f;printf("浮点数: %f\n", data.f);return 0;
}
(五)枚举
枚举是一种用户自定义的数据类型,它用于定义一组命名的整数常量。示例:
#include <stdio.h>// 定义枚举
enum Color { RED, GREEN, BLUE };int main()
{enum Color my_color = GREEN;printf("颜色的枚举值: %d\n", my_color);return 0;
}
上面的五个派生数据类型,就在此作一个了解,在后面的文章中会详细介绍。
三、变量的定义与创建方式
(一)变量的定义
前面我们说到,数据类型就是在内存中开辟了一块内存空间,用于存储数据。
变量就像是一个标签,为分配的内存空间赋予了一个名称,这样在程序中就可以方便地引用和操作这块内存空间中的数据。
可以说,我们对内存空间中数据的操作与引用都是通过变量名。通过变量名,我们可以对内存中的数据进行读取、写入、修改等操作。
那为何称之为变量,就是因为这块内存空间中存储的数据可变,是一个可变的量。而与之对应不变的量,称之为常量。
(二)变量的创建与初始化
变量的创建方式:“数据类型 变量;”
int age; //整型变量
char ch; //字符变量
double weight; //浮点型变量
如果我们在变量创建的时候就给它一个初始值,就叫做初始化;已经初始化了,再给一个值就叫作赋值。未初始化的变量不能使用,里面是一个随机值。
//初始化
int age = 18;
char ch = 'w';
int age1 = age + 1;
//赋值
age = 20;
ch = 'a';
上面代码中的 18 与 'w' 就是字面量。字面量指的是在代码中直接给出的固定值。它是一种能够直接表示数据类型值的方式,不需要经过计算或者转换就能使用。
而第三个则不是字面量,它需要将age转化为上面的18之后,再 +1 ,才可以使用。
最后两个则是在初始后之后的赋值。
(三)变量的分类
1、全局变量
在大括号外部定义的变量就是全局变量。全局变量的使用范围更广,整个工程中想使用,都是有办法使用的。(整个工程都能使用)
2、局部变量
在大括号内部定义的变量就是局部变量。局部变量的使用范围是比较局限,只能在自己所在的局部范围内使用的。(只能在自己的大括号内使用)
#include <stdio.h>int global = 2023;//全局变量int main()
{int local = 2018;//局部变量printf("%d\n", local);printf("%d\n", global);return 0;
}
3、当局部变量与全局变量同名时,局部变量优先使用。
#include <stdio.h>int n = 1000;int main()
{int n = 10;printf("%d\n" n);//打印的结果是10return 0;
}
4、全局变量与局部变量在内存中的存储位置
一般我们在学习C/C++语言的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。
① 局部变量是放在内存中的栈区;
② 全局变量是放在内存中的静态区;
③ 堆区是用来动态管理内存的,后面的文章会作详细介绍。
四、数据类型知识点补充
(一)各种数据类型长度的计算(字节数)
1、sizeof 操作符
① sizeof 是⼀个关键字,也是操作符,是专门是用来计算sizeof的操作数的类型长度的,
② 单位是字节。sizeof 操作符的操作数可以是类型,也可是变量或者表达式。
① sizeof( 类型 )
② sizeof( 表达式 )
③ sizeof 的操作数如果不是类型,是表达式的时候,可以省略掉后边的括号的。
④ sizeof 后边的表达式是不真实参与运算的,根据表达式的类型来得出大小。
⑤ sizeof 的计算结果是 size_t 类型的,对应的占位符为"%zd"。
因为 sizeof 运算符的返回值,C语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定, sizeof 到底返回什么类型。
不同的系统中,返回值的类型有可能是unsigned int,也有可能是 unsigned long ,甚至是 unsigned long long ,对应的 printf( ) 占位符分别是 %u、%lu 和 %llu 。这样不利于程序的可移植性。
C语言提供了一个解决方法,创造了一个类型别名 size_t,用来统一表示 sizeof 的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int ,也可能是unsigned long 等。
2、sizeof 操作的的计算
#include <stdio.h>
int main()
{int a = 10;short b = 20;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof a); //a是变量的名字,可以省略掉sizeof后边的() printf("%zd\n", sizeof(int)); //计算数据类型的字节数printf("%zd\n", sizeof(b = a + 10)); //表达式不计算,以被赋值变量为准,计算字节数return 0;
}
(二)signed 与 unsigned
C语言使用 signed 和 unsigned 关键字修饰字符类型和整型类型的。
signed 关键字,表示⼀个类型带有正负号,包含负值;unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。
对于这些数据类型,整数类型默认带有正负号的,字符类型默认是否带有正负号,由当前系统决定。
也就是说 int 等同于 signed int 。由于这是默认情况,关键字 signed 一般都省略不写,但是写了也不算错。但是 char 不等同于 signed char,它有可能是 signed char ,也有可能是unsigned char。
整数变量声明为 unsigned 的好处是,同样长度的内存能够表示的最大整数值,增大了一倍。
例如,16位的 signed short 的取值范围是:-32768~32767 ,最大值为32767;而 unsigned short 的取值范围是:0~65535,最大值增大到了65,535。
整型类型的最值在头文件 limits.h 中都有重命名宏定义。如果我们需要使用这些最值,直接书写它的别名即可。
(三)数据类型的极值表示
我们知道在不同的编译器中,某些数据类型的取值范围可能不同,虽然说我们普遍使用的集成开发环境是VS2022,但是不排除程序要在其他其地方运行的可能。
在 limits.h 文件中说明了整型类型的取值范围;在 float.h 这个文件中说明了浮点型类型的取值范围。当我们不确定某些数据类型的取值范围时,可以查看这两个文件。
它们在文件中,对其数据类型的极值进行了重命名:
如果需要使用这些常量,需要先引用上面的两个头文件。我们为了代码的可移植性,需要使用某种整数类型的极限值时,应该尽量使用这些常量。
以上即为C语言数据类型与变量的全部内容,整理时长近七个小时,麻烦三连支持一下呗~