1 基本类型
1.1 数字类型
1.1.1 整型
1.1.2 短整型 short
短整型 short 是 C 语言中定义的一种整型变量。短整型的二进制位长是 16 位。这意味着它可以表示的整数范围较小。短整型所能表示的整数的值域为 -32768~32767。
1.1.3 整型 int
int 是基本的数据类型,可以满足物我们处理一般数据的需求。整型的二进制位长是 32 位。取值范围为 -2147483648 ~ 21474836471 。
1.1.4 长整型 long long
长整型同理,只不过比整型表示的范围更多,长整型的二进制位长是 64 位。取值范围为 -9223372036854775808 ~ 92233720368547758071 。
1.1.5 无符号关键字 unsigned
unsigned 为无符号的修饰关键字,可以加在上述三个整形上面,加了这个关键字之后,表示该变量只能存储无符号整数,也即是说,不能表负数。
例如:unsigned short
表示的范围为 0 ~ 655351 ,因为它不能表示负数,所以它的二进制位全部用来表示正数,因此它表示的正数的范围会扩大。
上面三个整型都与此相同,如果某个变量不需要存储负数,可以用这个关键词扩大存储正数的范围。
1.2 浮点型
当我们需要储存的数据存在小数的时候,就需要用到浮点型变量才存储。
1.2.1 单精度型 float
float 是单精度浮点数,用于表示带有小数部分的实数。它在内存中占用 4 个字节,精度为 6~7 位,绝对能保证的为 6 位 ,也就是说,float 能保证小数点后 6 位小数的精确性。它的取值范围为 3.4E-38 ~ 3.4E38 或者 -(3.4E-38 ~ 3.4E38) 。
在科学计数法中,E 表示 10 的指数。例如,1E5
表示 1×10^5 ,即 100000 。
1.2.2 双精度型 double
double 表示双精度浮点型。它占用 8 个字节(64位)的内存空间。其数值范围为 1.7E-308~1.7E+308,双精度完全保证的有效数字是 15 位,16 位只是部分数值有保证。
1.3 字符类型 char
char 表示字符型。它用来存储字符,它只占用 1 个字节,但是计算机用数字编码存储字符,如美国用 ASCII 码,所以字符和整数均可以表示 char
型。
字符可以用字母表示,也可以用整数表示。例如,在 ASCII 码中,65 代表大写字母 A。因此,char grade = 'A'
和 char grade = 65
是等价的。
所以字符型在其本质上就是整形。
总结:
数据类型 | 占内存字节数 | 表示范围 | 格式说明符 |
---|---|---|---|
char | 1 | -128~127 | %c |
short int | 2 | -32,768~32,767 | %hd |
unsigned short | 2 | 0~65,535 | %hu |
int | 4 | -2,147,483,648~2,147,483,647(-2^31 ~ 2^31 -1) | %d |
unsigned int | 4 | 0~4,294,967,295 | %u |
long long int | 8 | -9,223,372,036,854,775,808~9,223,372,036,854,775,807(-2^63 ~ 2^63 -1) | %lld |
unsigned long long | 8 | 0~18,446,744,073,709,551,615 | %llu |
float | 4 | -3.4x10^-38 ~ 3.4x10^38 | %f |
double | 8 | -1.7x10^-308 ~ 1.7x10^308 | %lf |
注意:int占多少个字节是由编译器决定的,ANSI标准定义int是占2个字节。TC是按ANSI标准的,它的int是占2个字节的。在TC里面,printf (“%d”,sizeof (int));结果是2;但是在VC里,一个int是占4个字节的,printf (“%d”,sizeof (int));cout<<sizeof (int);结果都是4。不同的编译器,规定也不一样。float,double也是一样的,在不同的编译器里,占的字节是不一样的。
2 构造类型
2.1 数组
将许多相同数据类型的变量按照顺序存储在内存,我们可以使用数组。
比如我需要存储10个整形变量,就可以使用数组 int a[10]
一次性连续声明十个整形变量。极大的提高了写代码的效率。
当获取元素的时候,下表是从 0 开始,即第一个元素为 a[0]
,那么最后一个元素就是 a[9]
。
2.2 结构体 struct
struct 是一个构造数据类型
它将不同类型的数据组合成一个整体,也就是说可以自义定数据类型。
例如声明一个结构体 people :
struct structA
{
int age;
char name[10];
double height;
};
在这里定义了一个人的结构体,包含 年龄、名字、身高 三个数据类型。
当一个结构体被创建的时候,结构体变量所占内存长度是各成员占的内存长度的总和。
结构体的数据使用 .
来点出。
2.3 共用体 union
union 是一个特殊的类类型
它在一个时刻只能保有其一个非静态数据成员。也就是说它可以使得几个变量一起占据一段内存空间,可以将一个内存位置用于多种用途。
例如声明一个共用体 data :
union data
{
int age;
char name[10];
double height;
};
在这里定义了一个存放数据的共用体,它可以在一个内存中存储三个数据,但是这三个数据每次只能存放其中一种数据,当新的数据存放的时候,原来的数据就失去了作用。
例如:
#include <iostream>
using namespace std;
union data{
int age;
char name[10];
double height;
}Data;
int main()
{
Data.age=21;
cout<<Data.age<<endl;
Data.height=1.65;
cout<<Data.age<<' '<<Data.height<<endl;
return 0;
}
它使用共用体来存储数据。共用体是一种特殊的数据类型,允许在同一内存位置存储不同的数据类型。在这个程序中,联合 data
包含三个成员:一个 int
成员 age
,一个 char
数组 name
和一个 double
成员 height
。程序初始化联合的 age
成员为 21 并输出它。然后它用 1.65 初始化 height
成员并输出 age
和 height
成员。但是,由于联合一次只能保存一个值,所以当初始化 height
时,age
的值将被覆盖。
这个程序的输出为:
21
0 1.65
需要注意的是:当一个共用体被创建的时候,其大小为最长的成员占的内存大小。比如上面的data的内存大小为10个字节。
2.4 枚举类型 enum
枚举类型不是很常用,很少看见。
枚举类型(enum)是C语言中的一种特殊类型,它允许程序员定义一个有限的、可枚举的数据集。枚举类型描述的是一组整型值的集合,它是预处理指令#define的替代。枚举和宏其实非常类似,宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。
例如,定义一个表示星期的枚举类型:
enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
在这个例子中,定义了一个名为Weekday的枚举类型,它包含了7个枚举成员,分别表示一周中的7天。此时 Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
均为常量。
定义了枚举类型后,我们可以像使用其他数据类型一样使用它。例如,我们可以定义一个Weekday类型的变量,并给它赋值:
enum Weekday today;
today = Wednesday;
在这个例子中,我们定义了一个名为today的Weekday类型变量,并将其值设置为Wednesday。
3 指针类型
指针本质上是一个值为地址的变量,它存储的是某个地址。
指针是C语言中的一个重要概念。它是一种特殊的变量,用于存储内存地址。通过指针,不仅可以访问数据本身,还可以操作存储数据的变量地址。
指针的作用非常广泛。它可以使不同区域的代码轻松共享内存数据,从而使程序更快速高效。此外,C语言中一些复杂的数据结构,如链表和二叉树,往往需要使用指针来构建。
定义指针变量的语法如下:
type *pointer_name;
其中,type
是指针所指向变量的数据类型, *
表示这是一个指针变量,pointer_name
是指针变量的名称。
例如:定义一个指向整型变量的指针:
int *p;
在这个例子中,定义了一个名为 p
的指针变量,它可以指向一个整型变量。
定义了指针变量后,需要给它赋值,即让它指向一个实际的内存地址。这可以通过使用取地址运算符 &
来实现。例如:
int x = 10;
int *p = &x;
在这个例子中,定义了一个整型变量x
并将其初始化为10。然后定义了一个指向整型变量的指针p
并将其初始化为x
的地址。这样,p
就指向了x
所在的内存地址。
有了指针后,就可以通过它来访问和修改它所指向的内存地址中存储的数据。例如:
int x = 10;
int *p = &x;
*p = 20;
在这个例子中,首先定义了一个整型变量 x
并将其初始化为10。然后定义了一个指向整型变量的指针 p
并将其初始化为 x
的地址。接着使用解引用运算符 *
来访问 p
所指向的内存地址中存储的数据,并将其修改为20。这样,变量 x
的值就被修改为了20。
这仅仅是指针的初步用法,后面还有双指针,指针数组,数组指针等等用法。
4 空类型 void & void*
4.1 void
在C语言中,void 关键字表示空类型。它常用于定义函数的参数类型、返回值和函数中指针类型的声明。
void 类型可以表示一种未知类型。它不能代表一个真实的变量,而是用来声明一个函数没有返回值或没有参数,或者声明一个无类型指针。
例如,定义一个没有返回值的函数:
void printHello() {
printf("Hello, world!\n");
}
在这个例子中,它的返回类型为void
,表示该函数没有返回值。
也可以定义一个没有参数的函数:
void printHello(void) {
printf("Hello, world!\n");
}
在这个例子中,它的参数列表为void
,表示该函数没有参数。
4.2 void*
可以使用void
关键字来定义一个无类型指针:
void *p;
在这个例子中,我们定义了一个名为p
的指针变量,它的类型为 void *
,表示它是一个无类型指针。
void* 是一种无类型指针,它可以指向任何类型的数据,也可以接受任何类型指针的赋值,但是它不能直接解引用或进行算术运算,必须先转换为具体的类型指针才能操作。
void* 是一种特殊的指针类型,也称为空指针、空类型指针或无类型指针。它可以指向任何类型的数据,但是不能直接进行解引用操作。
例如:
#include <stdio.h>
int main() {
int x = 10;
double y = 3.14;
char z = 'A';
void *p;
p = &x;
printf("Value of x: %d\n", *(int*)p);
p = &y;
printf("Value of y: %f\n", *(double*)p);
p = &z;
printf("Value of z: %c\n", *(char*)p);
return 0;
}
在这个例子中,定义了三个变量 x
、y
和 z
,分别为整型、双精度浮点型和字符型。然后定义了一个 void*
类型的指针 p
。
接下来,将 p
分别指向这三个变量的地址。由于 void*
指针不能直接进行解引用操作,所以我们需要先将它强制转换为相应的类型,然后再进行解引用。
当我们想要访问 x
的值时,我们需要先将 p
强制转换为 int*
类型,然后再进行解引用操作。同理,当我们想要访问 y
和 z
的值时,也需要先将 p
分别强制转换为 double*
和 char*
类型。
运行上面的代码,输出结果如下:
Value of x: 10
Value of y: 3.140000
Value of z: A
void 和 void* 的使用和派生的意义在于提供了一种通用和灵活的方式来处理不同类型的数据:
- void* 可以用来实现泛型编程,即编写可以处理多种数据类型的函数或数据结构,例如库函数 qsort 和 malloc 就使用了 void* 。
- void* 可以用来实现动态内存分配,即根据运行时的需要申请和释放内存空间,例如 malloc 函数返回一个 void* 指针,用户可以根据自己的需求将其转换为合适的类型。
- void* 可以用来实现低级别的内存操作,即直接对内存中的字节进行读写,而不考虑其数据类型,例如库函数 memcpy 和 memset 就使用了 void*。
c语言的储存模型
参考自:C语言的6种存储模型
c语言的内存分配模型包括以下几个区域:
在C语言中,内存可分用五个部分:
-
BSS段(Block Started by Symbol):用来存放程序中未初始化的全局变量的内存区域。
-
数据段(data segment): 用来存放程序中已初始化的全局变量的内存区域。
-
代码段(text segment):用来存放程序执行代码的内存区域。
-
堆(heap):用来存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc分配内存时,新分配的内存就被动态添加到堆上,当进程调用free释放内存时,会从堆中剔除。
-
栈(stack):存放程序中的局部变量(但不包括static声明的变量,static变量放在数据段中)。同时,在函数被调用时,栈用来传递参数和返回值。由于栈先进先出特点。所以栈特别方便用来保存/恢复调用现场。
参考自:C语言内存模型详解