文章目录
- 一、编程语言中的数据类型
- 1.1 整型(Integer)
- 1.2 浮点型(Floating-Point)
- 1.3 字符型(Character)
- 1.4 布尔型(Boolean)
- 1.5 数组(Array)
- 1.6 字符串(String)
- 1.7 结构体(Struct)和类(Class)
- 1.8 枚举(Enumeration)
- 二、C++ 数据类型
- 2.1 基本的内置类型
- 2.2 代码实战
- 2.2.1 \n 和 \t 的区别
- 2.2.2 typedef 声明
- 2.3 枚举类型
- 2.4 类型转换
- 2.4.1 静态转换(Static Cast)
- 2.4.2 动态转换(Dynamic Cast)
- 2.4.2.1 向上转型
- 2.4.2.2 向下转型
- 2.4.3 常量转换(Const Cast)
- 2.4.4 重新解释转换(Reinterpret Cast)
一、编程语言中的数据类型
编程语言中的数据类型是用于表示数据的特定类别或分类。不同的编程语言提供了各种数据类型,用于存储不同类型的数据。以下是一些常见的数据类型,涵盖多种编程语言:
1.1 整型(Integer)
- 在C/C++中,使用 int、short、long、long long等表示整数类型。
- 在Java中,使用 int、short、long等表示整数类型。
- 在Python中,使用 int表示整数类型。
1.2 浮点型(Floating-Point)
- 在C/C++中,使用 float、double表示浮点数类型。
- 在Java中,使用 float、double表示浮点数类型。
- 在Python中,使用 float表示浮点数类型。
1.3 字符型(Character)
- 在C/C++中,使用 char表示单个字符类型。
- 在Java中,使用 char表示单个字符类型。
- 在Python中,使用 str表示字符串类型,但可以通过索引访问单个字符。
1.4 布尔型(Boolean)
- 在C/C++中,使用 bool表示布尔类型,取值为 true或 false。
- 在Java中,使用 boolean表示布尔类型,取值为 true或 false。
- 在Python中,使用 bool表示布尔类型,取值为 True或 False。
1.5 数组(Array)
- 在C/C++中,使用数组来存储一组相同类型的元素。
- 在Java中,使用数组或者使用集合类如 ArrayList 来存储一组相同类型的元素。
- 在Python中,使用列表(List)或者使用NumPy库提供的数组来存储一组元素。
1.6 字符串(String)
- 在C/C++中,可以使用字符数组来表示字符串,也可以使用C++标准库提供的 string 类型。
- 在Java中,使用 String 类型表示字符串。
- 在Python中,使用 str 类型表示字符串。
1.7 结构体(Struct)和类(Class)
- 在C/C++中,可以使用结构体来自定义复合数据类型,类似于面向对象编程中的类。
- 在Java中,使用类来定义自定义数据类型。
- 在Python中,使用类来定义自定义数据类型,支持面向对象编程。
1.8 枚举(Enumeration)
- 在C/C++中,使用 enum 关键字定义枚举类型,用于列举一组命名的常量值。
- 在Java中,使用 enum 关键字定义枚举类型,用于列举一组命名的常量值。
- 在Python中,没有内置的枚举类型,但可以使用类或者常量定义来实现类似的功能。
这些是一些常见的数据类型,不同的编程语言可能提供了其他特定的数据类型或变体。根据所选择的编程语言,可以使用相应的数据类型来处理和操作数据。
二、C++ 数据类型
使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。
您可能需要存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等)的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么。
2.1 基本的内置类型
C++ 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的 C++ 数据类型:
其实 wchar_t 是这样来的:
typedef short int wchar_t;
是C编程语言中的typedef声明。它将一个名为wchar_t的新类型定义为短整数(short int)。
在C语言中,wchar_t通常用于表示宽字符,即需要多个字节来存储的字符。wchar_t的确切大小取决于实现,可能在不同的系统上有所不同。但是,按照惯例,它通常被定义为16位或32位的整数类型。
通过使用typedef,你可以为现有类型创建一个别名。在这种情况下,typedef为short int创建了一个名为wchar_t的别名。这样,你可以在代码中使用wchar_t作为short int的简写,从而提高可读性,并在处理宽字符时使代码更加易于理解。
所以 wchar_t 实际上的空间是和 short int 一样。
一些基本类型可以使用一个或多个类型修饰符进行修饰:
- signed
- unsigned
- short
- long
下表显示了各种变量类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值。
注意:不同系统会有所差异,一字节为 8 位。
注意:默认情况下,int、short、long都是带符号的,即 signed。
注意:long int 8 个字节,int 都是 4 个字节,早期的 C 编译器定义了 long int 占用 4 个字节,int 占用 2 个字节,新版的 C/C++ 标准兼容了早期的这一设定。
拓展知识:带符号该怎么理解?
在C语言中,默认情况下,整数类型(如int、short、long)都是带符号的(signed),这意味着它们可以表示正数、负数和零。
带符号的整数使用最高位(最左边的位)来表示符号位。如果符号位为0,则表示该数为正数;如果符号位为1,则表示该数为负数。
例如,对于一个有符号的8位整数类型(signed char),范围是从-128到127。其中,如果二进制表示的最高位为0,如01000001,则表示整数值65;如果最高位为1,如11000001,则表示负数值-63。
需要注意的是,当进行算术运算或比较操作时,带符号的整数类型会遵循符号扩展规则,确保运算结果和比较行为符合预期。符号扩展是指在将较小的整数类型(如char或short)提升为较大的整数类型(如int)时,将符号位进行复制以保持符号一致。
如果你希望使用无符号的整数类型,可以使用unsigned关键字进行显式声明,如unsigned int、unsigned short、unsigned long。无符号整数类型只能表示非负数和零,因此它们的取值范围都是正数。
拓展知识:一个宽字符,该怎么理解?
宽字符(Wide Character)是在编程中用于表示与单个字节不同大小的字符的概念。它是为了支持更广泛的字符集,如Unicode字符集,而引入的。
在传统的C编程中,字符通常被表示为一个字节(8位)。这种表示方式适用于ASCII字符集,其中包含标准的英文字符和一些特殊字符,但无法表示其他语言的字符或特殊符号。
为了支持更广泛的字符集,引入了宽字符的概念。宽字符使用多个字节来表示一个字符,以容纳更多的字符集。在C语言中,宽字符通常使用wchar_t类型表示。
宽字符的大小是实现相关的,可以是16位或32位,具体取决于编译器和操作系统。宽字符编码方案最常用的是UTF-16和UTF-32。
使用宽字符类型wchar_t,可以表示更多的字符,包括各种语言的字符、特殊符号、表情符号等。这对于国际化和本地化应用程序以及需要处理多语言文本的场景非常有用。
需要注意的是,在处理宽字符时,需要使用特定的宽字符函数和库来确保正确处理多字节字符。例如,在C语言中,宽字符操作通常使用w前缀的函数(如wprintf、wscanf)以及与宽字符相关的库函数。
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。
以下列出了32位系统与64位系统的存储大小的差别(windows 相同):
从上表可得知,变量的大小会根据编译器和所使用的电脑而有所不同。
2.2 代码实战
#include<iostream>
#include <limits>using namespace std; int main()
{ cout << "type: \t\t" << "************size**************"<< endl; cout << "bool: \t\t" << "所占字节数:" << sizeof(bool); cout << "\t最大值:" << (numeric_limits<bool>::max)(); cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl; cout << "char: \t\t" << "所占字节数:" << sizeof(char); cout << "\t最大值:" << (numeric_limits<char>::max)(); cout << "\t\t最小值:" << (numeric_limits<char>::min)() << endl; cout << "signed char: \t" << "所占字节数:" << sizeof(signed char); cout << "\t最大值:" << (numeric_limits<signed char>::max)(); cout << "\t\t最小值:" << (numeric_limits<signed char>::min)() << endl; cout << "unsigned char: \t" << "所占字节数:" << sizeof(unsigned char); cout << "\t最大值:" << (numeric_limits<unsigned char>::max)(); cout << "\t\t最小值:" << (numeric_limits<unsigned char>::min)() << endl; cout << "wchar_t: \t" << "所占字节数:" << sizeof(wchar_t); cout << "\t最大值:" << (numeric_limits<wchar_t>::max)(); cout << "\t\t最小值:" << (numeric_limits<wchar_t>::min)() << endl; cout << "short: \t\t" << "所占字节数:" << sizeof(short); cout << "\t最大值:" << (numeric_limits<short>::max)(); cout << "\t\t最小值:" << (numeric_limits<short>::min)() << endl; cout << "int: \t\t" << "所占字节数:" << sizeof(int); cout << "\t最大值:" << (numeric_limits<int>::max)(); cout << "\t最小值:" << (numeric_limits<int>::min)() << endl; cout << "unsigned: \t" << "所占字节数:" << sizeof(unsigned); cout << "\t最大值:" << (numeric_limits<unsigned>::max)(); cout << "\t最小值:" << (numeric_limits<unsigned>::min)() << endl; cout << "long: \t\t" << "所占字节数:" << sizeof(long); cout << "\t最大值:" << (numeric_limits<long>::max)(); cout << "\t最小值:" << (numeric_limits<long>::min)() << endl; cout << "unsigned long: \t" << "所占字节数:" << sizeof(unsigned long); cout << "\t最大值:" << (numeric_limits<unsigned long>::max)(); cout << "\t最小值:" << (numeric_limits<unsigned long>::min)() << endl; cout << "double: \t" << "所占字节数:" << sizeof(double); cout << "\t最大值:" << (numeric_limits<double>::max)(); cout << "\t最小值:" << (numeric_limits<double>::min)() << endl; cout << "long double: \t" << "所占字节数:" << sizeof(long double); cout << "\t最大值:" << (numeric_limits<long double>::max)(); cout << "\t最小值:" << (numeric_limits<long double>::min)() << endl; cout << "float: \t\t" << "所占字节数:" << sizeof(float); cout << "\t最大值:" << (numeric_limits<float>::max)(); cout << "\t最小值:" << (numeric_limits<float>::min)() << endl; cout << "size_t: \t" << "所占字节数:" << sizeof(size_t); cout << "\t最大值:" << (numeric_limits<size_t>::max)(); cout << "\t最小值:" << (numeric_limits<size_t>::min)() << endl; cout << "string: \t" << "所占字节数:" << sizeof(string) << endl; // << "\t最大值:" << (numeric_limits<string>::max)() << "\t最小值:" << (numeric_limits<string>::min)() << endl; cout << "type: \t\t" << "************size**************"<< endl; return 0;
}
这段代码是用C++编写的,它输出不同数据类型在内存中所占的字节数以及它们的最大值和最小值。
以下是代码的解读:
#include<iostream>
:包含了iostream头文件,用于输入输出操作。#include <limits>
:包含了limits头文件,用于获取各种数据类型的最大值和最小值。- using namespace std;:使用标准命名空间,简化后续代码中的标准库函数的调用。
- int main():主函数的开始。
- cout << “type: \t\t” << “size**” << endl;:输出表头。
- 之后的一系列cout语句按顺序输出各个数据类型的信息:
- sizeof(type):使用sizeof运算符获取该数据类型在内存中所占的字节数。
- (numeric_limits::max)():使用numeric_limits模板类获取该数据类型的最大值。
- (numeric_limits::min)():使用numeric_limits模板类获取该数据类型的最小值。
- cout << “type: \t\t” << “size**” << endl;:输出表尾。
- return 0;:主函数的结束。
这段代码通过输出各种数据类型的字节数和取值范围,帮助你了解不同类型在内存中的存储情况和可表示的数据范围。
当上面的代码被编译和执行时,它会产生以下的结果,结果会根据所使用的计算机而有所不同:
type: ************size**************
bool: 所占字节数:1 最大值:1 最小值:0
char: 所占字节数:1 最大值: 最小值:€
signed char: 所占字节数:1 最大值: 最小值:€
unsigned char: 所占字节数:1 最大值: 最小值:
wchar_t: 所占字节数:2 最大值:65535 最小值:0
short: 所占字节数:2 最大值:32767 最小值:-32768
int: 所占字节数:4 最大值:2147483647 最小值:-2147483648
unsigned: 所占字节数:4 最大值:4294967295 最小值:0
long: 所占字节数:4 最大值:2147483647 最小值:-2147483648
unsigned long: 所占字节数:4 最大值:4294967295 最小值:0
double: 所占字节数:8 最大值:1.79769e+308 最小值:2.22507e-308
long double: 所占字节数:8 最大值:1.79769e+308 最小值:2.22507e-308
float: 所占字节数:4 最大值:3.40282e+38 最小值:1.17549e-38
size_t: 所占字节数:8 最大值:18446744073709551615 最小值:0
string: 所占字节数:40
type: ************size**************D:\C++CodeProject\Project2\x64\Debug\Project2.exe (进程 54608)已退出,代码为 0。
按任意键关闭此窗口. . .
2.2.1 \n 和 \t 的区别
\n和\t是C语言和C++语言中的转义字符,用于表示特殊的控制字符。
- \n:表示换行符。在输出文本时,\n会导致光标移动到下一行的开头位置,从而实现换行效果。常用于在控制台或文本文件中创建新的行。
- \t:表示制表符。在输出文本时,\t会导致光标移动到下一个制表符停靠位置,从而实现对齐的效果。常用于创建表格或对齐输出。
例如,在下面的代码中:
cout << "Hello, world!\n";
cout << "Name:\tJohn Doe\n";
cout << "Age:\t25\n";
Hello, world!
Name: John Doe
Age: 25
第一行输出了字符串"Hello, world!",并在末尾使用\n进行换行。
第二行输出了字符串"Name:“,后面是一个制表符\t,然后是"John Doe”,再使用\n进行换行。
第三行输出了字符串"Age:“,后面是一个制表符\t,然后是"25”,再使用\n进行换行。
总结:
- \n表示换行符,用于在输出中创建新的行。
- \t表示制表符,用于在输出中实现对齐效果。
2.2.2 typedef 声明
typedef是C语言中的关键字,用于创建类型别名(type alias)。通过typedef,可以为现有的数据类型或自定义的结构体、联合体等定义一个新的名称,以提高代码的可读性和可维护性。
typedef语法如下:
typedef existing_type new_type_name;
其中,existing_type是已存在的数据类型,可以是基本数据类型(如int、float)、指针类型、数组类型,也可以是自定义的结构体、联合体等类型。
new_type_name是为现有类型定义的新名称,可以是任何合法的标识符,用于在代码中代替原始类型。
以下是一些typedef的示例:
typedef int MyInt; // 定义 MyInt 作为 int 的别名
typedef float Temperature; // 定义 Temperature 作为 float 的别名
typedef int* IntPtr; // 定义 IntPtr 作为 int 指针的别名
typedef struct {int x;int y;
} Point; // 定义 Point 作为结构体的别名
上述typedef声明将创建了新的类型别名,可以在代码中使用这些别名来声明变量,代替使用原始的类型名称,从而增加代码的可读性和可维护性。
例如,在使用typedef之后,可以这样声明变量:
MyInt num = 10;
Temperature currentTemp = 25.5;
IntPtr p = NULL;
Point pt;
pt.x = 3;
pt.y = 5;
这样,代码中的变量声明就更加清晰和易于理解了。
2.3 枚举类型
枚举类型(Enumeration Type)是C++中的一种用户定义的数据类型,用于定义一组命名的常量。枚举类型提供了一种更具可读性和可维护性的方式来表示一组相关的离散值。
在C++中,定义枚举类型使用enum关键字,语法如下:
enum enum_name {constant1,constant2,constant3,// ...
};
其中,enum_name是枚举类型的名称,可以是任何有效的标识符。
constant1, constant2, constant3等是枚举常量,代表枚举类型的可能取值,可以是整数、字符或其他枚举常量。
下面是一个示例:
enum Color {RED,GREEN,BLUE
};
在这个示例中,Color是一个枚举类型的名称,它定义了三个枚举常量:RED、GREEN和BLUE。这些常量分别代表红色、绿色和蓝色。
枚举类型的常用操作包括:
- 声明枚举变量:
Color myColor;
- 为枚举变量赋值:
myColor = RED;
- 使用枚举常量进行比较:
if (myColor == GREEN) {// 执行相应的操作
}
- 使用枚举常量进行 switch 语句:
switch (myColor) {case RED:// 执行红色相关操作break;case GREEN:// 执行绿色相关操作break;case BLUE:// 执行蓝色相关操作break;default:// 处理未知颜色break;
}
枚举类型的优点包括:
- 提供了一种更具可读性的方式来表示离散的取值。
- 常量名称在作用域内是唯一的,避免了常量名称冲突。
- 可以进行类型检查,编译器可以检查枚举变量是否被正确地赋值。
需要注意的是,默认情况下,枚举常量会被赋予整数值,从0开始递增。但是,也可以显式为枚举常量指定特定的值:
enum Color {RED = 1,GREEN = 5,BLUE = 10
};
此外,C++11引入了强类型枚举(enum class),它提供了更严格的类型检查和更强的作用域限定。使用强类型枚举时,枚举常量在其所属的枚举类型作用域内是唯一的。
这是枚举类型在C++中的基本介绍,它可以用于表示一组相关的常量值,并提供了更好的可读性和可维护性。
如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始。
例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。
enum color { red, green, blue } c;
c = blue;
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。
enum color { red, green=5, blue };
在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。
2.4 类型转换
类型转换是将一个数据类型的值转换为另一种数据类型的值。
C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
2.4.1 静态转换(Static Cast)
静态转换是将一种数据类型的值强制转换为另一种数据类型的值。
静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型。
静态转换不进行任何运行时类型检查,因此可能会导致运行时错误。
int i = 10;
float f = static_cast<float>(i); // 静态将int类型转换为float类型
在给变量f赋值时,使用了static_cast<float>(i)
进行类型转换。这里的static_cast<float>
表示将变量i的值转换为float类型。因此,变量f的类型是float。
我们可以检查一下f的类型:
#include<iostream>
int main() {int i = 10;float f = static_cast<float>(i);std::cout << "f的类型为: " << typeid(f).name() << std::endl;return 0;
}
输出结果为:
f的类型为: floatD:\C++CodeProject\Project2\x64\Debug\Project2.exe (进程 52376)已退出,代码为 0。
按任意键关闭此窗口. . .
2.4.2 动态转换(Dynamic Cast)
动态转换通常用于将一个基类指针或引用转换为派生类指针或引用。动态转换在运行时进行类型检查,如果不能进行转换则返回空指针或引发异常。
动态转换(dynamic_cast)是C++中用于类层次结构中的安全类型转换的操作符。它在运行时检查类型信息,并允许进行向上转型(upcasting)和向下转型(downcasting)。下面是动态转换的一些常见案例:
2.4.2.1 向上转型
假设有一个基类Animal和一个派生类Dog,可以将Dog类型的对象指针或引用转换为指向基类Animal类型的指针或引用。
#include<iostream>
class Animal {
public:virtual void speak() {std::cout << "Animal speaks" << std::endl;}
};class Dog : public Animal {
public:void speak() override {std::cout << "Dog barks" << std::endl;}
};int main() {Dog dog;Animal* animalPtr = dynamic_cast<Animal*>(&dog);if (animalPtr != nullptr) {animalPtr->speak(); // 输出: Dog barks}return 0;
}
上述代码是一个简单的示例,演示了动态转换(dynamic_cast)的使用。
- 首先,定义了一个基类Animal和一个派生类Dog。其中有一个虚拟函数speak()。在这里,speak()函数输出"Animal speaks"。
- 定义了一个派生类Dog,它继承自基类Animal。Dog类重写了speak()函数,输出"Dog barks"。
- 主函数开始。创建了一个Dog对象dog。然后使用dynamic_cast将dog的地址转换为Animal类型的指针,并将结果赋给animalPtr。
- 使用if语句检查animalPtr是否为非空指针。如果不为空,则调用animalPtr指向的对象的speak()函数。由于dog对象是Dog类的实例,而animalPtr指向同一个对象,因此调用的是Dog类中重写的speak()函数。所以输出的是"Dog barks"。
- 程序结束,返回0表示成功执行。
Animal* animalPtr = dynamic_cast<Animal*>(&dog);
这行代码的作用是将一个指向Dog类型对象的指针&dog,通过dynamic_cast进行类型转换,将其转换为指向Animal类型的指针,并将结果赋值给animalPtr。
具体解释如下:
- &dog:取得了dog对象的地址,即指向Dog类型对象的指针。
- dynamic_cast<Animal*>(&dog):使用dynamic_cast进行类型转换,将指向Dog类型对象的指针转换为指向Animal类型对象的指针。
- Animal* animalPtr:声明一个指向Animal类型的指针变量animalPtr。
- =:赋值操作符,将右侧的转换结果赋值给左侧的变量。
- dynamic_cast<Animal*>(&dog)的结果将被赋给animalPtr,即将指向Dog类型的指针转换为指向Animal类型的指针,并存储在animalPtr中。
这样,animalPtr就指向了dog对象,但以Animal类型的视角来使用。通过这种类型转换,可以在处理多态对象时使用基类指针来调用相应的成员函数,实现了多态的效果。
输出结果为:
Dog barksD:\C++CodeProject\Project2\x64\Debug\Project2.exe (进程 77232)已退出,代码为 0。
按任意键关闭此窗口. . .
2.4.2.2 向下转型
假设有一个基类Animal和一个派生类Dog,可以将指向基类Animal类型的指针或引用转换为指向派生类Dog类型的指针或引用。需要注意的是,向下转型需要在运行时检查类型,并且仅在类型匹配时才成功。
#include<iostream>
class Animal {
public:virtual void speak() {std::cout << "Animal speaks" << std::endl;}
};class Dog : public Animal {
public:void speak() override {std::cout << "Dog barks" << std::endl;}void fetch() {std::cout << "Dog fetches" << std::endl;}
};int main() {Animal* animalPtr = new Dog();Dog* dogPtr = dynamic_cast<Dog*>(animalPtr);if (dogPtr != nullptr) {dogPtr->fetch(); // 输出: Dog fetches}delete animalPtr;return 0;
}
在上述示例中,通过将Animal类型的指针转换为Dog类型的指针,可以调用派生类中特有的成员函数。在进行向下转型时,应该先使用dynamic_cast进行类型检查,以确保转换的安全性。
输出结果为:
Dog fetchesD:\C++CodeProject\Project2\x64\Debug\Project2.exe (进程 111540)已退出,代码为 0。
按任意键关闭此窗口. . .
请注意,在进行动态转换时,如果转换不合法(例如,将一个基类对象转换为与之无关的派生类类型),dynamic_cast将返回空指针或抛出std::bad_cast异常(如果转换引用类型)。因此,在进行动态转换时,应始终检查转换结果是否为非空指针,并处理可能的转换失败情况。
2.4.3 常量转换(Const Cast)
常量转换用于将 const 类型的对象转换为非 const 类型的对象。
常量转换只能用于转换掉 const 属性,不能改变对象的类型。
const int i = 10;
int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
2.4.4 重新解释转换(Reinterpret Cast)
重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
重新解释转换不进行任何类型检查,因此可能会导致未定义的行为。
int i = 10;
float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型