C和C是两种非常流行和强大的编程语言,它们都是由贝尔实验室的丹尼斯·里奇(Dennis Ritchie)和比雅尼·斯特劳斯特鲁普(Bjarne Stroustrup)分别在1972年和1985年开发的。C语言是一种结构化的、过程式的、通用的编程语言,它可以用来编写各种类型的程序,如操作系统、数据库、编译器等。C语言是一种在C语言基础上发展而来的、支持面向对象编程和泛型编程的编程语言,它可以用来编写更复杂和高效的程序,如图形界面、游戏、网络应用等。
C和C++有很多相同的语法和数据类型,但也有一些不同和扩展。我们来看一些基本的语法和数据类型。
注释、变量、常量、运算符、表达式等
注释是程序中用来说明代码功能或逻辑的文字,它不会被编译器执行,只是给人阅读的。C和C++都支持两种注释方式:单行注释和多行注释。单行注释以//
开头,多行注释以/*
开头,以*/
结尾。例如:
// 这是一个单行注释 /* 这是一个 多行注释 */
变量是程序中用来存储数据的标识符,它有一个名字和一个类型。变量必须先声明后使用,声明时要指定变量的类型和名字,可以同时赋初值。例如:
int a; // 声明一个整型变量a a = 10; // 给a赋值为10 double b = 3.14; // 声明一个双精度浮点型变量b,并赋值为3.14 char c = 'A'; // 声明一个字符型变量c,并赋值为'A'
常量是程序中不会改变的数据,它有一个值和一个类型。常量可以用字面量表示,如10
,3.14
,'A'
等,也可以用const
关键字定义一个常量变量,如const int MAX = 100;
。常量变量一旦定义就不能再修改。
运算符是程序中用来对数据进行操作的符号,如加减乘除等。表达式是由运算符和操作数(变量或常量)组成的式子,如a + b * c
等。运算符有不同的优先级和结合性,可以用括号改变计算顺序。例如:
int a = 10, b = 20, c = 30; int d = a + b * c; // d = 610, 因为乘法优先于加法 int e = (a + b) * c; // e = 900, 因为括号优先于乘法
基本数据类型(整型、浮点型、字符型等)和复合数据类型(数组、结构体等)
下面是C和C++语言中的所有基本数据类型的详细表格,包括它们之间的差异:
类型 | 描述 | 存储大小 | 值范围 | C支持 | C++支持 |
bool |
布尔类型,表示真或假 | 1字节 | true 或 false | 否 | 是 |
char |
通常用于存储字符型数据 | 1字节 | -128 到 127 或 0 到 255 | 是 | 是 |
unsigned char |
无符号字符型 | 1字节 | 0 到 255 | 是 | 是 |
signed char |
有符号字符型 | 1字节 | -128 到 127 | 是 | 是 |
int |
用于存储整数 | 2 或 4字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 | 是 | 是 |
unsigned int |
无符号整型 | 2 或 4字节 | 0 到 65,535 或 0 到 4,294,967,295 | 是 | 是 |
short |
用于存储短整数 | 2字节 | -32,768 到 32,767 | 是 | 是 |
unsigned short |
无符号短整型 | 2字节 | 0 到 65,535 | 是 | 是 |
long |
用于存储长整数 | 4字节 | -2,147,483,648 到 2,147,483,647 | 是 | 是 |
unsigned long |
无符号长整型 | 4字节 | 0 到 4,294,967,295 | 是 | 是 |
long long |
用于存储更大的整数 | 8字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 是(C99起) | 是 |
unsigned long long |
无符号更大的整型 | 8字节 | 0 到 18,446,744,073,709,551,615 | 是(C99起) | 是 |
float |
用于存储单精度浮点数 | 4字节 | 约 ±3.4e ±38 (7位有效数字) | 是 | 是 |
double |
用于存储双精度浮点数 | 8字节 | 约 ±1.7e ±308 (15位有效数字) | 是 | 是 |
long double |
用于存储长双精度浮点数 | 8字节 | 约 ±1.7e ±308 (15位有效数字) | 是 | 是 |
wchar_t |
宽字符类型 | 2 或 4字节 | 1宽字符 | 是(C95起) | 是 |
char16_t |
用于存储16位宽的字符 | 2字节 | 1个16位宽的字符 | 否 | 是(C++11起) |
char32_t |
用于存储32位宽的字符 | 4字节 | 1个32位宽的字符 | 否 | 是(C++11起) |
注意:存储大小和值范围可能会因编译器或计算机架构的不同而有所不同。
复合数据类型包括数组、结构体、联合体、枚举、类和模板,它们的大小和值范围取决于它们的元素和成员。C++还支持字符串类型std::string
,而C语言通常使用字符数组来表示字符串。
数据类型是程序中用来表示数据的形式和范围的分类,不同的数据类型有不同的内存大小和取值范围。C和C++都支持以下几种基本数据类型:
- 整型(
int
):用来表示整数,如10
,-5
等。整型有不同的长度,如short
(短整型),long
(长整型),long long
(更长整型)等,长度决定了取值范围。整型也可以有符号(signed
)和无符号(unsigned
)之分,符号决定了正负范围。例如:
short a = 32767; // a是一个短整型变量,最大值为32767 unsigned short b = 65535; // b是一个无符号短整型变量,最大值为65535 long c = 2147483647; // c是一个长整型变量,最大值为2147483647 unsigned long d = 4294967295; // d是一个无符号长整型变量,最大值为4294967295 long long e = 9223372036854775807; // e是一个更长整型变量,最大值为9223372036854775807 unsigned long long f = 18446744073709551615; // f是一个无符号更长整型变量,最大值为18446744073709551615
- 浮点型(
float
,double
):用来表示小数,如3.14
,-0.5
等。浮点型有不同的精度,如单精度(float
),双精度(double
),扩展精度(long double
)等,精度决定了小数位数。例如:
float a = 3.14f; // a是一个单精度浮点型变量,精度为6~7位小数 double b = 3.1415926; // b是一个双精度浮点型变量,精度为15~16位小数 long double c = 3.14159265358979323846L; // c是一个扩展精度浮点型变量,精度为19~20位小数
- 字符型(
char
):用来表示单个字符,如'A'
,'9'
等。字符型也可以有符号和无符号之分,符号决定了ASCII码范围。字符型可以用单引号表示字面量,也可以用转义字符表示特殊字符,如换行符(\n
),制表符(\t
)等。例如:
char a = 'A'; // a是一个字符型变量,值为'A' unsigned char b = 255; // b是一个无符号字符型变量,值为255对应的ASCII码字符 char c = '\n'; // c是一个字符型变量,值为换行符
- 布尔型(C中的
bool
):用来表示真假值,如真(true),假(false)。布尔型只有两个取值,在C中可以用关键字true和false表示,在C中可以用宏定义TRUE和FALSE表示。例如:
bool a = true; // a是一个布尔型变量,值为真 bool b = false; // b是一个布尔型变量,值为假 bool c = (a && b); // c是一个布尔型变量,值为a和b的逻辑与运算结果
除了基本数据类型外,C和C++还支持一些复合数据类型,如数组、结构体、枚举、联合等。复合数据类型是由基本数据类型或其他复合数据类型组合而成的数据类型,它可以表示更复杂的数据结构。我们来看一些复合数据类型的例子。
- 数组(array):数组是一种由相同类型的元素组成的有序集合,它可以用来存储多个相同类型的数据。数组在内存中占据连续的空间,每个元素都有一个索引来表示其位置,索引从0开始。数组必须在声明时指定其类型和长度,长度不能改变。例如:
int a[10]; // 声明一个长度为10的整型数组a a[0] = 1; // 给a的第一个元素赋值为1 a[9] = 10; // 给a的最后一个元素赋值为10 int b[3] = {1, 2, 3}; // 声明一个长度为3的整型数组b,并初始化为{1, 2, 3} char c[] = "Hello"; // 声明一个字符型数组c,并初始化为"Hello",长度为6(包括'\0')
- 结构体(struct):结构体是一种由不同类型的元素组成的复合数据类型,它可以用来表示一个实体的属性。结构体在内存中占据连续的空间,每个元素都有一个名字来表示其含义,可以用点运算符(
.
)来访问。结构体必须先定义后使用,定义时要指定结构体的名字和各个元素的类型和名字,可以同时初始化。例如:
struct Student { // 定义一个结构体Student int id; // 学号 char name[20]; // 姓名 double score; // 成绩 }; struct Student s1; // 声明一个Student类型的变量s1 s1.id = 1001; // 给s1的id赋值为1001 strcpy(s1.name, "人坤"); // 给s1的name赋值为"Alice" s1.score = 95.5; // 给s1的score赋值为95.5 struct Student s2 = {10028, "坤坤", 28.0}; // 声明并初始化一个Student类型的变量s2
- 枚举(enum):枚举是一种由一组有名字的常量组成的数据类型,它可以用来表示一些有限的选项或状态。枚举在内存中占据整型大小的空间,每个常量都有一个整数值来表示其顺序,可以用等号赋值运算符(
=
)来修改。枚举必须先定义后使用,定义时要指定枚举的名字和各个常量的名字,可以同时赋值。例如:
enum Color { // 定义一个枚举Color RED, // 红色,值为0 GREEN, // 绿色,值为1 BLUE // 蓝色,值为2 }; enum Color c1; // 声明一个Color类型的变量c1 c1 = RED; // 给c1赋值为RED enum Season { // 定义一个枚举Season,并赋值 SPRING = 1, // 春天,值为1 SUMMER = 4, // 夏天,值为4 AUTUMN = 7, // 秋天,值为7 WINTER = 10 // 冬天,值为10 }; enum Season s1; // 声明一个Season类型的变量s1 s1 = WINTER; // 给s1赋值为WINTER
- 联合(union):联合是一种由不同类型的元素组成的复合数据类型,它可以用来表示一个共享内存空间的变量。联合在内存中占据最大元素大小的空间,每个元素都有一个名字来表示其含义,可以用点运算符(
.
)来访问。联合必须先定义后使用,定义时要指定联合的名字和各个元素的类型和名字,可以同时初始化。联合的特点是同一时间只能存储一个元素的值,如果给一个元素赋值,会覆盖其他元素的值。例如:
union Data { // 定义一个联合Data int i; // 整型 float f; // 浮点型 char c; // 字符型 }; union Data d1; // 声明一个Data类型的变量d1 d1.i = 10; // 给d1的i赋值为10 d1.f = 3.14; // 给d1的f赋值为3.14,会覆盖i的值 d1.c = 'A'; // 给d1的c赋值为'A',会覆盖f的值 union Data d2 = {20}; // 声明并初始化一个Data类型的变量d2,只能初始化第一个元素
类型转换、强制类型转换和自动类型推断(C++)
在C和C++语言中,类型转换可以分为隐式类型转换和显式类型转换。下面是一个详细的表格:
类型转换 | 描述 | C支持 | C++支持 |
隐式类型转换 | 当两种不同的数据类型进行混合运算时,编译器会自动进行类型转换。转换规则通常是将"低级别"类型转换为"高级别"类型,以确保不会丢失数据。例如,如果一个 int 和一个 double 进行运算,int 会被转换为 double 。 |
是 | 是 |
C风格类型转换(强制类型转换) | 你可以使用强制类型转换运算符 (typename) 来显式地转换一个值的类型。例如,double x = (double)3/2; 将使得 3 被视为 double 类型,从而使得整个除法运算的结果也是 double 类型。 |
是 | 是 |
static_cast |
static_cast 是C++中最通用的类型转换方式,它可以进行大部分类型的转换,但不能转换掉类型的const、volatile、或者__unaligned属性。例如,double x = static_cast<double>(3)/2; |
否 | 是 |
dynamic_cast |
dynamic_cast 主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的;在进行下行转换时,dynamic_cast 具有类型检查,比 static_cast 更安全。 |
否 | 是 |
const_cast |
const_cast 主要用于修改类型的const或volatile属性。但是对一个定义为const的变量进行const_cast ,然后进行修改,结果是未定义的。 |
否 | 是 |
reinterpret_cast |
reinterpret_cast 用于位的简单重新解释。reinterpret_cast 是最危险的cast, 它能将任何类型的指针转换为任何其他类型的指针。例如,int* p = reinterpret_cast<int*>(new double(3.14)); |
否 | 是 |
注意:类型转换可能会导致数据丢失或溢出,所以在进行类型转换时需要特别注意。
类型转换是程序中将一个数据类型转换为另一个数据类型的过程,有时是必要的,有时是隐含的。类型转换有两种方式:强制类型转换和自动类型推断。
- 隐式类型转换:当两种不同的数据类型进行混合运算时,编译器会自动进行类型转换。转换规则通常是将"低级别"类型转换为"高级别"类型,以确保不会丢失数据。
// C/C++ int i = 10; double d = i; // 隐式类型转换,将int转换为double
- C风格类型转换(强制类型转换):你可以使用强制类型转换运算符
(typename)
来显式地转换一个值的类型。
// C/C++ int i = 3; double d = (double)i / 2; // 显式类型转换,将int转换为double
- static_cast:
static_cast
是C++中最通用的类型转换方式,它可以进行大部分类型的转换,但不能转换掉类型的const、volatile、或者__unaligned属性。
// C++ int i = 3; double d = static_cast<double>(i) / 2; // 使用static_cast进行类型转换
- dynamic_cast:
dynamic_cast
主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast
和static_cast
的效果是一样的;在进行下行转换时,dynamic_cast
具有类型检查,比static_cast
更安全。
// C++ class Base { virtual void dummy() {} }; class Derived: public Base { int a; }; Base* base = new Derived; Derived* derived = dynamic_cast<Derived*>(base); // 使用dynamic_cast进行类型转换
- const_cast:
const_cast
主要用于修改类型的const或volatile属性。但是对一个定义为const的变量进行const_cast
,然后进行修改,结果是未定义的。
// C++ const int i = 10; int* p = const_cast<int*>(&i); // 使用const_cast去掉const属性 //*p = 11; // 结果是未定义的
- reinterpret_cast:
reinterpret_cast
用于位的简单重新解释。reinterpret_cast
是最危险的cast, 它能将任何类型的指针转换为任何其他类型的指针。
// C++ double d = 3.14; int* p = reinterpret_cast<int*>(&d); // 使用reinterpret_cast进行类型转换
注意:类型转换可能会导致数据丢失或溢出,所以在进行类型转换时需要特别注意。