C语言从入门到放弃
1. 介绍
C语言是一种广泛使用的高级编程语言,由Dennis M. Ritchie(丹尼斯·里奇,C语言之父)在20世纪70年代初于贝尔实验室开发。它最初是为设计UNIX操作系统而创建的,但后来因其高效性、灵活性和跨平台特性而成为世界上最流行的编程语言之一。
1.1 特点
- 简洁且强大:C语言提供了一套精简的关键字集,并允许程序员直接操作内存(通过指针),这使得它可以非常高效地执行任务。
- 移植性强:C 程序可以在不同平台上编译运行,只需做少量甚至不做修改。这种跨平台的能力使C成为了编写操作系统、嵌入式系统和其他底层软件的理想选择。
- 丰富的库函数:标准C库提供了大量的预定义函数,用于文件 I/O、字符串处理、数学运算等常用功能,简化了开发过程。
- 支持结构化编程:它鼓励使用函数来组织代码,增强了程序的模块化和可读性。
- 指针支持:C中的指针允许直接访问和操纵内存地址,这对于实现复杂的数据结构(如链表、树)以及优化性能至关重要。
- 编译型语言:C是一种编译型语言,这意味着源代码在执行前需要被编译成机器码。编译后的程序通常比解释型语言更快。
1.2 历史与发展
- 起源:C 语言源于 B 语言,后者又是 BCPL 的后继者。Ritchie 在 B 语言的基础上添加了类型系统和其他改进,从而创造了 C 语言
- 标准化:ANSI 和 ISO 分别于1989年和1990年发布了 C 语言的标准版本(即 C89 或 ANSI C)。随后有多个修订版发布,包括 C99、C11 和最新的 C18。
1.3 应用领域
- 系统编程:由于其低级别的特性和对硬件的良好控制,C 被广泛应用于操作系统内核、驱动程序和网络协议栈等领域。
- 嵌入式系统:从微控制器到复杂的实时控制系统,C 都是非常流行的选择,因为它可以生成高效的代码并且占用较少资源。
- 游戏开发:许多游戏引擎和框架都是用 C 或 C++ 编写的,因为它们能够提供必要的性能优势。
- 数据库管理系统:像 MySQL 这样的数据库也是用 C 实现的,以确保高性能的数据管理和查询处理能力。
2. 安装
2.1 编译器安装
MinGW(Minimalist GNU for Windows)是一个用于 Windows 操作系统的开源编译器工具链,它基于 GNU 工具集(包括 GCC 编译器、GDB 调试器等),但专门为 Windows 环境进行了优化和定制。
2.2 编辑器安装
Visual Studio Code(简称 VS Code)是一款由微软开发的源代码编辑器,支持多种编程语言,并且可以通过插件扩展其功能。
安装扩展:Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code、C/C++、Code Runner
3. 第一个程序
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
1. 包含头文件
#include <stdio.h>
:这行预处理指令告诉编译器包含标准输入输出库(Standard Input Output library)的声明。stdio.h
提供了诸如printf
和scanf
等函数的原型声明,使得我们可以在程序中使用这些函数。
2. 主函数定义
int main()
:这是程序的入口点——主函数。根据 C 标准,main
函数应该返回一个整数类型 (int
),通常用来向操作系统报告程序的执行状态。返回值0
表示程序成功结束。
3. 打印语句
printf("Hello World\n");
:这行代码调用printf
函数来输出字符串"Hello World"
到标准输出(通常是控制台)。\n
是换行符,表示在输出完文本后移动光标到下一行的开始位置。
4. 返回值
return 0;
:这条语句明确地指定了当main
函数结束时返回给操作系统的值为0
,这通常被解释为程序成功完成。如果程序遇到错误,可以返回非零值(例如1
),以指示发生了某种问题。
4. 基础语法
4.1 注释
注释用于向代码中添加解释性文本,这些文本不会被编译器编译为机器码,因此不会影响程序的执行。注释的主要目的是提高代码的可读性和可维护性,帮助开发者理解代码的功能和逻辑。
4.1.1 单行注释
单行注释是从 //
开始直到该行结束的所有内容都被视为注释。
#include <stdio.h>
int main()
{
// 控制台输出:Hello World
printf("Hello World\n");
return 0;
}
4.1.2 多行注释
多行注释是用 /*
开始,并以 */
结束。在这两个标记之间的所有内容都被视为注释,可以跨越多行。多行注释适合用于较长的解释或暂时禁用一段代码。
#include <stdio.h>
/*
C语言入口程序
main函数
*/
int main()
{
printf("Hello World\n");
return 0;
}
4.2 关键字
关键字是具有特殊含义和用途的保留字,它们不能用作标识符(如变量名、函数名等)。这些关键字定义了 C 语言的基本结构和语法。
4.2.1 C语言标准
序号 | 标准名称 | 发布时间 |
---|---|---|
1 | C89/C90 标准 | 1990 |
2 | C99 标准 | 1999 |
3 | C11 标准 | 2011 |
4 | C17 标准 | 2018 |
5 | C23 标准 | 2024 |
4.2.2 C89/C90关键字(32)
1. 数据类型关键字(12个)
这些关键字用于声明变量或函数的数据类型。
关键字 | 说明 |
---|---|
char |
声明字符型变量或函数返回值类型。 |
short |
声明短整型变量或函数返回值类型。 |
int |
声明整型变量或函数返回值类型。 |
long |
声明长整型变量或函数返回值类型。 |
signed |
明确声明有符号数类型(如 signed int )。 |
unsigned |
定义无符号数类型(如 unsigned int )。 |
float |
声明单精度浮点型变量或函数返回值类型。 |
double |
声明双精度浮点型变量或函数返回值类型。 |
struct |
定义结构体类型。 |
union |
定义共用体类型,允许不同类型的成员共享同一块内存。 |
enum |
定义枚举类型。 |
void |
表示没有类型;用于定义函数返回值为空或指针指向未知类型的数据。 |
2. 控制语句关键字(12个)
这些关键字用于控制程序流程,包括循环、条件分支等。
-
循环控制(5个)
for
:定义for
循环。do
:与while
组合使用,形成do-while
循环。while
:定义while
循环。break
:终止循环或switch
语句。continue
:结束当前循环迭代并继续下一次迭代。
-
条件语句(3个)
if
:条件判断语句。else
:与if
结合使用,形成条件分支。goto
:无条件跳转到指定标签处,不推荐频繁使用。
-
开关语句(3个)
switch
:控制多分支选择语句。case
:在switch
语句中定义一个分支。default
:在switch
语句中定义默认分支。
-
返回语句(1个)
return
:返回从函数调用的结果给调用者。
3. 存储类型关键字(5个)
这些关键字用于指定变量的存储类型和作用域。
关键字 | 说明 |
---|---|
auto |
声明自动变量,默认情况下局部变量即为自动变量,很少使用。 |
extern |
声明外部变量或函数,表明其定义位于其他文件中。 |
register |
建议编译器将变量存储在寄存器中以加快访问速度,现代编译器通常忽略此建议。 |
static |
定义静态变量或函数,限制其作用域和生命周期。 |
typedef |
创建新的类型名作为现有类型的别名。 |
4. 其他关键字(3个)
这些关键字用于特定目的,如声明常量、查询类型大小以及防止优化。
关键字 | 说明 |
---|---|
const |
定义常量,表示该值在程序运行期间不可改变。 |
sizeof |
运算符,返回数据类型的大小(以字节为单位)。 |
volatile |
告诉编译器不要对变量进行优化,因为它的值可能会被外部因素改变。 |
4.2.3 C99 新增关键字
关键字 | 说明 |
---|---|
_Bool |
定义布尔类型(true 或 false )。通常通过 <stdbool.h> 头文件中的宏定义为 bool 类型。 |
complex |
用于复数类型(需要包含 <complex.h> )。 |
imaginary |
用于虚数类型(需要包含 <complex.h> ),但较少使用。 |
inline |
提示编译器将函数内联展开以提高性能。 |
restrict |
用于指针声明,表示该指针是访问对象的唯一方式,帮助编译器优化代码。 |
4.2.4 C11 新增关键字
关键字 | 说明 |
---|---|
_Alignas |
指定数据类型的对齐方式。 |
_Alignof |
查询类型的对齐要求(类似于 sizeof ,但用于对齐属性)。 |
_Atomic |
定义原子类型,确保操作不会被打断,常用于多线程编程中。 |
_Bool |
虽然 _Bool 最初是在 C99 中引入的,但在 C11 中继续保留。 |
_Complex |
同样是从 C99 继承而来,用于复数类型。 |
_Generic |
支持泛型选择表达式,允许根据参数类型选择不同的实现。 |
_Imaginary |
从 C99 继承而来,用于虚数类型,但较少使用。 |
_Noreturn |
标记函数不会返回(例如 exit 函数),帮助编译器优化代码。 |
_Static_assert |
编译时断言,如果条件不满足则产生编译错误,增强代码健壮性。 |
_Thread_local |
定义线程局部存储变量,即每个线程拥有该变量的一个独立副本。 |
4.2.5 C17移除关键字
_Imaginary
:在C17中,_Imaginary
关键字被标记为过时,并且不再推荐使用。复数类型现在主要通过_Complex
来表示。
4.2.6 C23新增关键字
关键字 | 说明 |
---|---|
_Alignof |
查询类型的对齐要求(虽然不是新引入,但得到了进一步的支持和澄清)。 |
_Atomic |
定义原子类型,确保操作不会被打断,常用于多线程编程中(得到进一步支持)。 |
_BitInt |
支持固定宽度的整数类型,允许定义特定位数的整数类型。 |
_Decimal32 |
支持 IEEE 754-2008 规范中的 32 位十进制浮点数类型。 |
_Decimal64 |
支持 IEEE 754-2008 规范中的 64 位十进制浮点数类型。 |
_Decimal128 |
支持 IEEE 754-2008 规范中的 128 位十进制浮点数类型。 |
_Generic |
支持泛型选择表达式(得到进一步支持和扩展)。 |
_Imaginary |
虽然在 C17 中被标记为过时,但在 C23 中重新考虑其使用情况。 |
_Noreturn |
标记函数不会返回(例如 exit 函数),帮助编译器优化代码(得到进一步支持)。 |
_Static_assert |
编译时断言,如果条件不满足则产生编译错误(得到进一步支持)。 |
_Thread_local |
定义线程局部存储变量,即每个线程拥有该变量的一个独立副本(得到进一步支持)。 |
bool |
定义布尔类型(通过 <stdbool.h> 头文件引入,得到进一步支持)。 |
char8_t |
表示 UTF-8 编码的字符类型,增强了对 Unicode 的支持。 |
char16_t |
表示 UTF-16 编码的字符类型,增强了对 Unicode 的支持。 |
char32_t |
表示 UTF-32 编码的字符类型,增强了对 Unicode 的支持。 |
4.3 常量
常量是指在程序执行期间其值不能被修改的量。C语言提供了多种定义常量的方式,包括使用预处理器指令、关键字 const
以及枚举类型。
1. 使用预处理器指令 #define
这是最传统的定义常量的方法,通过预处理器指令 #define
来创建宏定义。
示例
#define PI 3.141592653589793
#define MAX_SIZE 100
优点:
- 简单直接,适用于所有数据类型。
- 可以定义复杂的表达式或字符串。
缺点:
- 没有类型检查,容易导致错误。
- 不是真正意义上的变量,调试时可能难以跟踪。
2. 使用 const
关键字
从 C99 开始,推荐使用 const
关键字来定义常量。这种方式更安全,因为编译器可以进行类型检查,并且可以在运行时分配内存。
示例
const double PI = 3.141592653589793;
const int MAX_SIZE = 100;
优点:
- 提供类型安全和更好的调试支持。
- 可以用于局部和全局作用域。
- 支持数组和其他复杂类型的初始化。
缺点:
- 对于一些非常量表达式(如函数返回值),不能直接用作数组维度等编译时常量。
3. 枚举类型 enum
枚举类型提供了一种定义整型常量集合的方法,通常用于表示一组相关的符号名称。
示例
enum Weekday {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
优点:
- 清晰地表达了相关常量之间的关系。
- 编译器自动为每个枚举成员分配唯一的整数值。
- 改善代码的可读性和维护性。
缺点:
- 只能用于整数类型。
4.4 变量
变量是程序中用于存储数据的命名位置。每个变量都有一个特定的类型,这决定了它所占用的内存大小和可以进行的操作。
1. 变量的基本概念
- 标识符:变量名必须是一个有效的标识符。标识符是由字母(a-z, A-Z)、数字(0-9)和下划线(_)组成,并且不能以数字开头。
- 作用域:变量的作用域决定了它可以在代码的哪些部分访问。常见的作用域有全局作用域(文件级别)和局部作用域(函数内部或块内)。
- 生命周期:变量的生命周期指的是它在程序运行期间存在的时间段。静态变量和全局变量在整个程序运行期间都存在,而自动变量仅在其定义的作用域内有效。
2. 变量的声明与定义
声明
声明告诉编译器变量的名称和类型,但不一定分配内存空间。全局变量的声明通常出现在所有函数之外。
extern int globalVar; // 全局变量的外部声明
定义
定义不仅声明了变量的类型和名称,还会为该变量分配内存空间。定义通常伴随着初始值的赋值。
int globalVar = 10; // 全局变量的定义并初始化
局部变量
局部变量是在函数或代码块内部声明的,其作用域仅限于该函数或代码块。
void myFunction() {
int localVar = 5; // 局部变量
}
3. 数据类型
C语言基本数据类型:
- 整型 (
int
,short
,long
,long long
):用于表示整数。 - 浮点型 (
float
,double
,long double
):用于表示实数。 - 字符型 (
char
):用于表示单个字符。 - 布尔型 (
_Bool
或bool
):用于表示逻辑值(从 C99 开始引入,通常通过<stdbool.h>
头文件中的宏定义)。 - 指针 (
*
):用于存储地址。 - 枚举 (
enum
):用于定义一组命名的整数值。 - 结构体 (
struct
) 和 联合体 (union
):用于组合不同类型的数据成员。
4. 初始化
变量可以在定义时进行初始化,也可以稍后通过赋值语句来设置初始值。
int x = 10; // 定义并初始化
char ch = 'A'; // 定义并初始化
double d = 3.14; // 定义并初始化
如果不显式地初始化变量,那么它的值将是未定义的(对于自动变量),或者为零(对于静态变量和全局变量)。
5. 存储类别
C语言提供了几种不同的存储类别来控制变量的存储方式和生命周期:
- 自动 (
auto
):默认情况下,局部变量都是自动变量,它们在函数调用时创建,在函数返回时销毁。 - 静态 (
static
):静态变量存在于整个程序的执行过程中,即使定义它们的函数已经返回。静态局部变量只初始化一次,而静态全局变量只能在定义它们的文件中访问。 - 寄存器 (
register
):建议编译器将变量存储在寄存器中以加快访问速度,但这只是一个提示,现代编译器可能会忽略此关键字。 - 外部 (
extern
):用于引用其他地方定义的变量,特别是在多文件项目中共享全局变量。
6. 变量的作用域和可见性
- 全局变量:在所有函数之外声明的变量具有全局作用域,可以在整个文件甚至多个文件之间访问(如果正确声明的话)。
- 局部变量:在函数或代码块内部声明的变量仅在该函数或代码块内可见。
- 块作用域:由大括号
{}
包围的代码区域内的变量只能在该区域内访问。
示例代码
#include <stdio.h>
// 全局变量声明
int globalVar = 10;
void myFunction() {
// 局部变量声明
static int staticVar = 0; // 静态局部变量
int autoVar = 5; // 自动局部变量
staticVar++;
printf("Static Variable: %d\n", staticVar);
printf("Auto Variable: %d\n", autoVar);
}
int main() {
// 局部变量声明
int localMainVar = 20;
printf("Global Variable: %d\n", globalVar);
myFunction();
myFunction(); // 注意静态变量的行为
return 0;
}
4.5 字面量
字面量(literals)是指直接出现在代码中的固定值,它们不需要通过变量名来引用。字面量可以是整数、浮点数、字符或字符串。
1. 整数字面量(Integer Literals)
整数字面量用于表示整数值。它们可以使用十进制、八进制或十六进制表示法。
- 十进制:默认情况下,整数是十进制的。
- 八进制:以
0
开头(例如017
表示八进制的 15)。 - 十六进制:以
0x
或0X
开头(例如0x1A
表示十六进制的 26)。 - 后缀:可以添加
u
或U
表示无符号类型,l
或L
表示长整型(long
),ll
或LL
表示长长整型(long long
)。这些后缀可以组合使用,例如100uL
。
示例
int a = 10; // 十进制
int b = 012; // 八进制 (等同于十进制 10)
int c = 0xA; // 十六进制 (等同于十进制 10)
unsigned long d = 100uL;
2. 浮点数字面量(Floating-point Literals)
浮点数字面量用于表示实数。它们可以包含小数点和指数部分,并且可以用科学计数法表示。
- 小数形式:包括一个小数点(例如
3.14
)。 - 指数形式:使用
e
或E
来表示指数(例如6.022e23
表示 (6.022 \times 10^{23}))。 - 后缀:可以添加
f
或F
表示float
类型,l
或L
表示long double
类型。
示例
double pi = 3.14159;
float f = 1.234f;
long double ld = 1.234L;
3. 字符字面量(Character Literals)
字符字面量用于表示单个字符,用单引号括起来。
- 普通字符:例如
'A'
。 - 转义序列:用于表示特殊字符,如换行
\n
、制表符\t
等。 - 多字节字符:可以使用 Unicode 编码表示非ASCII字符(依赖于编译器支持)。
示例
char letter = 'A';
char newline = '\n';
char copyright = '\u00A9'; // Unicode for ©
4. 字符串字面量(String Literals)
字符串字面量用于表示一系列字符,用双引号括起来。它们实际上是字符数组,并以空字符 \0
结尾。
- 普通字符串:例如
"Hello, World!"
。 - 拼接字符串:多个相邻的字符串字面量会自动拼接成一个字符串。
- 宽字符字符串:使用前缀
L
表示宽字符字符串(wchar_t
类型),适用于多字节字符集。
示例
const char* greeting = "Hello, ";
const char* name = "World!";
const char* message = "Hello, " "World!"; // 拼接字符串
const wchar_t* wideString = L"你好,世界!";
5. 布尔字面量(Boolean Literals)
从 C99 开始,C语言支持布尔类型 _Bool
(通常通过 <stdbool.h>
头文件中的宏定义为 bool
),其字面量为 true
和 false
。
示例
#include <stdbool.h>
bool isTrue = true;
bool isFalse = false;
4.6 格式化输出
格式化输出主要用于控制输出数据的格式,使得输出结果更加整齐和易于理解。最常用的格式化输出函数是 printf
和 fprintf
。其中,printf
用于向标准输出(通常是屏幕)打印格式化的字符串,而 fprintf
可以将格式化的字符串输出到指定的文件流中。
格式化输出的基本语法
格式化输出函数的基本语法如下:
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
format
:是一个格式字符串,包含普通字符和格式说明符。...
:代表一个或多个额外的参数,这些参数与格式说明符一一对应。
常见的格式说明符
格式说明符以百分号 %
开头,并跟随一个或多个字符来指定如何格式化相应的参数。以下是一些常见的格式说明符及其用法:
类型 | 说明符 | 示例 |
---|---|---|
整数 | %d 或 %i |
十进制整数 (int ) |
%u |
无符号十进制整数 (unsigned int ) |
|
%o |
八进制整数 | |
%x 或 %X |
小写/大写的十六进制整数 | |
浮点数 | %f |
浮点数 (float , double ) |
%e 或 %E |
科学计数法表示的浮点数 | |
%g 或 %G |
根据值自动选择 %f 或 %e /%E |
|
字符 | %c |
单个字符 (char ) |
字符串 | %s |
字符串 (const char* ) |
指针 | %p |
指针地址 |
百分号 | %% |
输出百分号本身 |
格式修饰符
除了基本的格式说明符外,还可以使用修饰符来进一步控制输出格式:
- 宽度:指定输出字段的最小宽度。如果实际内容短于指定宽度,则会用空格填充。可以在宽度前加
0
来用零填充。 - 精度:对于浮点数和字符串,可以指定小数点后的位数或最大长度。
- 标志:如
-
表示左对齐,+
强制显示正负号,空格(
示例
printf("%10d\n", 42); // 输出右对齐的整数,占10个字符宽
printf("%05d\n", 25); // 输出左补零的整数,占5个字符宽
printf("%.2f\n", 3.14159); // 输出保留两位小数的浮点数
printf("%-10s\n", "hello"); // 输出左对齐的字符串,占10个字符宽
格式化输出示例
下面是一些具体的例子,展示了如何使用 printf
进行格式化输出:
#include <stdio.h>
int main() {
int age = 25;
double gpa = 3.876;
char grade = 'A';
// 简单输出
printf("Age: %d\n", age);
printf("GPA: %.2f\n", gpa);
printf("Grade: %c\n", grade);
// 使用宽度和精度
printf("Formatted Age: %5d\n", age); // 右对齐,宽度为5
printf("Formatted GPA: %6.2f\n", gpa); // 右对齐,宽度为6,精度为2
printf("Formatted Grade: %3c\n", grade); // 右对齐,宽度为3
// 使用标志
printf("Signed Age: %+d\n", age); // 强制显示正负号
printf("Left-aligned GPA: %-6.2f\n", gpa); // 左对齐,宽度为6,精度为2
// 组合使用
printf("Student Info: Age=%03d, GPA=%.1f, Grade=%c\n", age, gpa, grade);
return 0;
}
文件输出
要将格式化的输出发送到文件而不是标准输出,可以使用 fprintf
函数。以下是 fprintf
的一个简单例子:
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
int age = 25;
double gpa = 3.876;
char grade = 'A';
fprintf(file, "Age: %d\n", age);
fprintf(file, "GPA: %.2f\n", gpa);
fprintf(file, "Grade: %c\n", grade);
fclose(file);
return 0;
}
4.7 ASCII
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是一种字符编码标准,最初设计用于在计算机和通信设备之间传输文本。ASCII码为每个英文字母、数字、标点符号以及一些控制字符分配了一个唯一的7位二进制数。
ASCII 码的基本概念
- 7位编码:标准ASCII码使用7位来表示字符,这意味着它可以表示 (2^7 = 128) 种不同的字符。
- 扩展ASCII:为了支持更多的字符(如国际字符),出现了各种扩展ASCII码,它们使用8位,可以表示 (2^8 = 256) 种字符。
标准ASCII码表(0-127)
扩展ASCII码(128-255)
扩展ASCII码增加了额外的字符,包括非英语字符和其他符号。不同系统可能有不同的扩展ASCII实现。
ASCII 码的应用
-
字符串处理:在编程中,ASCII码常用于字符串处理和字符比较。例如,在C语言中,字符实际上是用其对应的ASCII码值来表示的。
-
文件格式:许多简单的文本文件格式(如
.txt
文件)都是基于ASCII码的,确保了跨平台的兼容性。 -
网络协议:早期的网络协议(如HTTP、SMTP等)也广泛使用ASCII码进行数据传输。
C语言中的ASCII码
在C语言中,字符和整数可以互换使用,因为字符实际上是用其对应的ASCII码值来表示的。例如:
#include <stdio.h>
int main() {
char ch = 'A'; // 定义一个字符变量
printf("Character: %c, ASCII Value: %d\n", ch, ch); // 输出字符及其ASCII码值
int asciiValue = 65; // 直接使用ASCII码值
printf("ASCII Value: %d, Character: %c\n", asciiValue, asciiValue); // 输出ASCII码值及其对应的字符
return 0;
}
4.8 转义序列
转义序列(escape sequences)用于表示那些不能直接通过键盘输入或不容易用普通字符表示的特殊字符。这些序列以反斜杠 \
开头,后面跟着一个或多个字符,用来指示编译器将它们解释为特殊的控制字符或非打印字符。
常见的转义序列
序列 | 说明 | ASCII 码 (十进制) |
---|---|---|
\a |
警报(响铃) | 7 |
\b |
退格符(删除前一个字符) | 8 |
\f |
换页符 | 12 |
\n |
换行符(newline) | 10 |
\r |
回车符(将光标移回到当前行的开头) | 13 |
\t |
水平制表符(tab) | 9 |
\v |
垂直制表符 | 11 |
\\ |
反斜杠字符 \ |
92 |
\' |
单引号 ' |
39 |
\" |
双引号 " |
34 |
\? |
问号 ? |
63 |
八进制和十六进制转义序列
除了上述标准转义序列外,C语言还支持使用八进制和十六进制值来表示任意字符。
- 八进制:以
\
开头,后跟1到3位八进制数字(0-7)。例如,\101
表示字母A
(ASCII码为65)。 - 十六进制:以
\x
开头,后跟任意数量的十六进制数字(0-9, a-f, A-F)。例如,\x41
同样表示字母A
。
示例
char ch1 = '\101'; // 使用八进制表示 'A'
char ch2 = '\x41'; // 使用十六进制表示 'A'
Unicode 转义序列(宽字符)
对于多字节字符集或Unicode字符,可以使用以下格式:
- 通用字符名:如
\u
后跟四位十六进制数,或\U
后跟八位十六进制数。例如,\u00A9
表示版权符号 ©。
示例
wchar_t wc = L'\u00A9'; // 定义宽字符版权符号
使用示例
以下是一些如何在代码中使用转义序列的例子:
#include <stdio.h>
int main() {
printf("Hello\tWorld!\n"); // 输出 "Hello" 和 "World!" 之间有一个制表符,并换行
printf("Backspace\bexample\n"); // 输出 "Backspaceexample",因为 \b 删除了前一个空格
printf("Path: C:\\Windows\\System32\n"); // 输出路径字符串,其中 \\ 表示单个反斜杠
printf("Quote: \"Hello, World!\"\n"); // 输出带双引号的字符串
printf("Alert: \a\n"); // 发出警报声(可能在某些终端不起作用)
// 使用八进制和十六进制转义序列
printf("Octal: %c\n", '\101'); // 输出 'A'
printf("Hexadecimal: %c\n", '\x41'); // 输出 'A'
// 使用 Unicode 转义序列
wprintf(L"Copyright symbol: %lc\n", L'\u00A9');
return 0;
}
注意事项
- 转义序列的作用范围:转义序列通常只在一个字符常量或字符串字面量内有效。如果需要在其他上下文中使用这些特殊字符,则需要使用相应的函数或方法。
- 跨平台兼容性:虽然大多数转义序列在不同平台上都有一致的行为,但某些特定于操作系统的控制字符(如
\r\n
在 Windows 中表示换行)可能会有所不同。编写跨平台代码时应尽量使用标准转义序列。 - 避免混淆:确保正确使用转义序列,尤其是在处理文件路径、URL 或其他包含反斜杠的字符串时,以免引起意外的解析错误。
4.9 sizeof
sizeof
是一个运算符,用于查询数据类型、变量或表达式在内存中所占的字节数。它可以了解不同类型的数据占用多少空间,这对于优化内存使用和理解程序行为非常重要。
语法
sizeof(type)
sizeof(variable)
sizeof(expression)
type
:可以是任何有效的C数据类型,如int
、float
、char
等。variable
:可以是任何已声明的变量。expression
:可以是任意合法的C表达式。
返回值
sizeof
返回一个 size_t
类型的值,表示对象或类型的大小(以字节为单位)。size_t
是一种无符号整数类型,通常定义在 <stddef.h>
头文件中。
示例
查询基本数据类型的大小
#include <stdio.h>
int main() {
printf("Size of char: %zu bytes\n", sizeof(char));
printf("Size of int: %zu bytes\n", sizeof(int));
printf("Size of float: %zu bytes\n", sizeof(float));
printf("Size of double: %zu bytes\n", sizeof(double));
printf("Size of long: %zu bytes\n", sizeof(long));
printf("Size of long long: %zu bytes\n", sizeof(long long));
return 0;
}
输出结果可能会因平台不同而有所差异,但典型的输出如下:
Size of char: 1 bytes
Size of int: 4 bytes
Size of float: 4 bytes
Size of double: 8 bytes
Size of long: 8 bytes
Size of long long: 8 bytes
查询数组的大小
#include <stdio.h>
int main() {
int arr[5] = {
1, 2, 3, 4, 5};
printf("Size of array arr: %zu bytes\n", sizeof(arr)); // 整个数组的大小
printf("Size of single element in arr: %zu bytes\n", sizeof(arr[0])); // 单个元素的大小
return 0;
}
查询指针的大小
#include <stdio.h>
int main() {
int *ptr;
printf("Size of pointer ptr: %zu bytes\n", sizeof(ptr)); // 指针的大小
return 0;
}
查询结构体的大小
#include <stdio.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p = {
10, 20};
printf("Size of struct Point: %zu bytes\n", sizeof(struct Point));
printf("Size of variable p: %zu bytes\n", sizeof(p));
return