C 基础语法
AUTOGEN db1cfcb4a66a48a0907746848a657f32
C语言基础语法
本文档介绍了基本的C语言相关语法和介绍
本系列仍有C语言的内存管理专题、指针专题和数据结构专题。
1.1 介绍
早期历史
- 1969-1970 年,Tompson 在 BCPL 语言上创造 B 语言。
- 1971-1977 年,Ritchie 改造 B 语言增加数据类型创造了 C 语言,伴随 Unix 产生而产生。
- 1977-1979 年,C 语言伴随 Unix 移植性需求而繁荣发展。
Ken Thompson 与 Dennis M. Ritchie 这对好基友都是图灵奖获得者。
为PDP7编写操作系统,使用C改写
POSIX 标准
可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX)是IEEE为要在各种UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称,其正式称呼为IEEE Std 1003
C标准化
1989年ANSI C作为C89
1990年ISO采纳标准为C90
1999年发布C99
在 C 语言高速发展时期,从大型主机到小型微机,衍生了 C 语言的很多不同版本。
为统一 C 语言版本,1983 年美国国家标准局 ANSI - American National Standards Institute 成立了一个委员会,来制定 C 语言标准。1989 年 C 语言标准被批准,被称为 ANSI X3.159-1989 Programming Language C。第一个版本的 C 语言标准通常被称为 ANSI C。又由于这个版本是 89 年完成制定的,因此也被称为 C89。
后来 ANSI 把这个标准提交到 ISO 国际化标准组织,1990 年被 ISO 采纳为国际标准,称为 ISO C。又因为这个版本是 1990 年发布的,因此也被称为 C90。
ANSI C(C89) 与 ISO C(C90)内容基本相同,主要是格式组织不一样。看到 ANSI C、ISO C、C89、C90,要知道这些标准的内容都是一样的。
2011年12月8日,ISO 又正式发布了新的标准,称为 ISO/IEC9899: 2011,简称为 C11。
目前,几乎所有的开发工具都支持 ANSI/ISO C 标准,是 C 语言用得最广泛的一个标准版本。
在 ANSI C 标准确立之后,C 语言的规范在很长一段时间内都没有大的变动。1995 年 C 程序设计语言工作组对 C 语言进行了一些修改,成为后来的 1999 年发布的 ISO/IEC 9899:1999 标准,通常被成为 C99。
但是商业公司对 C99 的支持所表现出来的兴趣不同,GCC 和其它一些商业编译器支持 C99 的大部分特性的時候,微软和 Borland 却没有什么动作。
GCC - GNU Compiler Collection 编译器套件作为 GNU 工程开发的支持多种编程语言的编译器,在 C 言语标准的支持上一直走在前面。
例如,GCC 9.3.0 支持以下标准,使用 gcc -v –help 可以查询当前版本支持的标准:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| Standard | GCC 9.3 | GCC 8.1 | GCC 5.3 | Note |
|---------------------|---------|---------|---------|----------------------------------------------|
| -std=c11 | ✅ | ✅ | ✅ | ✓ ISO 2011 C |
| -std=c17 | ✅ | ✅ | ❌ | ✓ ISO 2017 C (2018) |
| -std=c18 | ✅ | ✅ | ❌ | ✓ ISO 2017 C (2018),同 `-std=c17` |
| -std=c1x | ✅ | ✅ | ✅ | ✗ 弃用,`-std=c11` 替代,同 `-std=c11` |
| -std=c2x | ✅ | ❌ | ❌ | ✓ ISO 202X C 标准草案 [体验] |
| -std=c89 | ✅ | ✅ | ✅ | ✓ ISO 1990 C 标准,同 `-std=c90` |
| -std=c90 | ✅ | ✅ | ✅ | ✓ ISO 1990 C |
| -std=c99 | ✅ | ✅ | ✅ | ✓ ISO 1999 C |
| -std=c9x | ✅ | ✅ | ✅ | ✗ 弃用,`-std=c99` 替代,同 `-std=c99` |
| -std=iso9899:1990 | ✅ | ✅ | ✅ | ✓ ISO 1990 C 标准,同 std=c90 |
| -std=iso9899:199409 | ✅ | ✅ | ✅ | ✓ ISO 1990 C as amended in 1994 |
| -std=iso9899:1999 | ✅ | ✅ | ✅ | ✓ ISO 1999 C 标准,同 std=c99 |
| -std=iso9899:199x | ✅ | ✅ | ✅ | ✗ 弃用,`-std=iso9899:1999` 替代,同 std=c99 |
| -std=iso9899:2011 | ✅ | ✅ | ✅ | ✓ ISO 2011 C 标准,同 std=c11 |
| -std=iso9899:2017 | ✅ | ✅ | ❌ | ✓ ISO 2017 C (2018),同 `-std=c17` |
| -std=iso9899:2018 | ✅ | ✅ | ❌ | ✓ ISO 2017 C (2018),同 `-std=c17` |
指令集
RISC精简指令集-ARM,M1芯片,苹果A系列芯片
CISC复杂指令集-X86
1.2 基本元素
关键字(Keywords)
关键字是C语言中预先保留的单词,具有特定意义,不能用作变量名或其他标识符。以下是一些常见的C语言关键字:
auto
:用于声明自动存储期的变量。break
:用于跳出最近的一个循环或switch语句。case
:用于switch语句中的分支选择。char
:用于声明字符型变量。const
:用于定义常量。continue
:用于跳过当前循环的剩余部分,直接进入下一次循环。default
:用于switch语句中的默认分支。do
:用于do-while循环的开始。double
:用于声明双精度浮点型变量。else
:用于if语句的”否则”分支。enum
:用于声明枚举类型。extern
:用于声明外部链接的变量或函数。float
:用于声明单精度浮点型变量。for
:用于for循环。goto
:用于无条件跳转到程序中的某个位置。if
:用于条件判断。int
:用于声明整型变量。long
:用于声明长整型变量。register
:用于声明寄存器变量。return
:用于从函数返回值。short
:用于声明短整型变量。signed
:用于声明有符号类型。sizeof
:用于获取变量或类型的大小。static
:用于声明静态存储期的变量或函数。struct
:用于声明结构体类型。switch
:用于多条件选择。typedef
:用于为类型创建别名。union
:用于声明联合体类型。unsigned
:用于声明无符号类型。void
:用于声明没有值的变量或函数返回类型。volatile
:用于声明易失性变量。- 等。
标识符(Identifiers)
标识符是程序员定义的变量名、常量名、类型名等。它们必须遵循以下规则:
- 标识符的第一个字符必须是字母或下划线(_)。
- 标识符的其余部分可以是字母、数字或下划线。
- 标识符不能是C语言的关键字。
- 标识符是大小写敏感的。
常量(Constants)
常量是程序中不会改变的值。常量可以是整型、浮点型、字符型等。例如:
- 整型常量:
42
,-10
,0x1A
(十六进制)。 - 浮点型常量:
3.14
,-0.001
,1.2e10
(科学记数法)。 - 字符型常量:
'A'
,'\n'
(换行符)。
字符串字面量(String Literals)
字符串字面量是括在双引号中的字符序列。例如:
"Hello, World!"
"C Programming"
字符串字面量在内存中以字符数组的形式存储,并且以空字符('\0'
)结尾。
运算符(Operators)
运算符用于执行程序中的操作。C语言中的运算符包括:
- 算术运算符:
+
,-
,*
,/
,%
,++
,--
。 - 关系运算符:
==
,!=
,<
,>
,<=
,>=
。 - 逻辑运算符:
&&
,||
,!
。 - 位运算符:
&
,|
,^
,<<
,>>
。 - 赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
。 - 条件运算符:
?:
。 - 其他运算符:
sizeof
,&
(取地址),*
(解引用)。
分隔符(Separators)
分隔符用于分隔程序中的不同部分。常见的分隔符包括:
- 分号(
;
):用于结束语句。 - 逗号(
,
):用于分隔参数列表或声明中的多个变量。 - 括号(
()
,[]
,{}
):用于定义作用域或数组索引。 - 点(
.
):用于访问结构体或联合体的成员。
1.3 数据类型
基本数据类型
整型(Integer Types)
1
int
:标准整型,通常为4个字节。
- 例子:
int age = 30;
- 例子:
1
short
:短整型,通常为2个字节。
- 例子:
short year = 2024;
- 例子:
1
long
:长整型,通常为4个字节或8个字节(取决于平台)。
- 例子:
long population = 1000000000;
- 例子:
1
long long
:更长的整型,至少64位。
- 例子:
long long bigNumber = 1234567890123456789LL;
- 例子:
字符型(Character Type)
1
char
:用于存储单个字符,通常为1个字节。
- 例子:
char letter = 'A';
- 例子:
浮点型(Floating-Point Types)
1
float
:单精度浮点型,通常为4个字节。
- 例子:
float pi = 3.14159265f;
- 例子:
1
double
:双精度浮点型,通常为8个字节。
- 例子:
double area = 3.14159265358979323846;
- 例子:
1
long double
:更长的双精度浮点型,精度和大小依赖于编译器。
- 例子:
long double largeNumber = 12345678901234567890.123456789012L;
- 例子:
无符号类型(Unsigned Types)
- 可以用于整型和字符型,表示只能存储非负数。
- 例子:
unsigned int uInt = 42;
unsigned char uChar = 255;
布尔类型(Boolean Type)
C99标准中引入了
1
_Bool
或
1
bool
(通常使用宏定义)。
- 例子:
_Bool isTrue = 1;
- 例子:
派生数据类型
枚举类型(Enumeration Types)
用于定义一组命名的整型常量。
例子:
1 2
cenum Color { RED, GREEN, BLUE }; int favorite = GREEN;
结构体(Structures)
允许将多个不同类型的数据项组合成一个单一的类型。
例子:
1 2 3 4 5 6
cstruct Student { int id; char name[50]; float gpa; }; struct Student alice = {1, "Alice", 3.5};
联合体(Unions)
类似于结构体,但所有成员共享相同的内存位置。
例子:
1 2 3 4 5 6
cunion Data { int i; float f; char *s; }; union Data data = { .f = 3.14 };
数组(Arrays)
相同类型的元素集合。
例子:
1 2
cint numbers[5] = {1, 2, 3, 4, 5}; char str[] = "Hello";
指针(Pointers)
存储另一个变量的内存地址。
例子:
1 2 3
cint *p = NULL; // 指针初始化为NULL int value = 10; p = &value; // p现在指向value的地址
函数(Functions)
可以返回基本数据类型或派生数据类型。
例子:
1 2 3
cint add(int a, int b) { return a + b; }
void类型
表示没有值或不关心返回类型的函数。
例子:
1 2 3
cvoid printHello() { printf("Hello, World!\n"); }
每种数据类型都有其特定的用途和内存占用,选择合适的数据类型对于编写高效和可读的代码至关重要。
1.4 变量与常量
变量(Variables)
变量是程序中用于存储数据的容器。变量必须在使用前声明,声明时需要指定变量的类型,因为C语言是一种静态类型语言。变量的命名遵循一定的规则,通常以字母或下划线开头,后跟字母、数字或下划线的组合。
变量的声明和初始化:
1
2
3
4
5
6
7
8
cint age; // 声明一个整型变量age
age = 25; // 初始化变量age为25
float salary; // 声明一个浮点型变量salary
salary = 3500.0; // 初始化变量salary为3500.0
char initial; // 声明一个字符型变量initial
initial = 'A'; // 初始化变量initial为'A'
变量的作用域:
- 局部变量:在函数内部声明,只在该函数内部可见。
- 全局变量:在所有函数外部声明,可以在程序的任何部分访问。
变量的生命周期:
- 自动存储期的变量(如局部变量)在声明它们的块进入时创建,在块退出时销毁。
- 静态存储期的变量(使用
static
关键字声明)在程序开始时创建,在程序结束时销毁。
常量(Constants)
常量是程序中一旦初始化后其值就不可改变的数据。常量可以是数值常量、字符常量、字符串常量等。
数值常量:
- 整型常量可以是十进制、八进制(以0开头)或十六进制(以0x开头)。
- 浮点型常量通常带有小数点或使用科学记数法表示。
字符常量:
- 使用单引号括起来的单个字符。
- 可以表示为普通字符或转义序列。
字符串常量:
- 使用双引号括起来的字符序列。
- 字符串字面量在内存中以字符数组的形式存在,以空字符
\0
结尾。
定义和使用常量:
1
2
3
4
5
cconst int MAX_USERS = 100; // 定义一个整型常量MAX_USERS
const float PI = 3.14159; // 定义一个浮点型常量PI
const char NEWLINE = '\n'; // 定义一个字符常量NEWLINE
const char greeting[] = "Hello, World!"; // 定义一个字符串常量
常量的优点:
- 使程序更易读,因为它们提供了有意义的名称。
- 使程序更易维护,因为改变常量的值只需在一处进行。
- 编译器可以对常量进行优化,提高程序效率。
常量的限制:
- 常量必须在编译时就已知其值。
- 常量不能被修改。
变量和常量是C语言编程中不可或缺的部分,正确地使用它们可以提高代码的可读性、可维护性和效率。
预定义常量(Micro Variable)
在C语言中,定义好的量通常指的是预定义的宏和常量,它们在标准库中定义,用于提供一些特定的、在编译时已知的数值。这些宏和常量通常定义在<limits.h>
(C89标准)或<stdint.h>
(C99标准及以后)等头文件中。以下是一些常见的预定义量:
- 整数类型的最大值和最小值
INT_MAX
:int
类型能表示的最大值。INT_MIN
:int
类型能表示的最小值(通常是 -INT_MAX - 1)。LONG_MAX
和LONG_MIN
:长整型(long int
)的最大值和最小值。SHORT_MAX
和SHORT_MIN
:短整型(short int
)的最大值和最小值。
- 字符类型的最大值
CHAR_MAX
和CHAR_MIN
:char
类型的最大值和最小值,取决于char
是否是有符号类型。
- 浮点类型的最大值和最小值
FLT_MAX
和FLT_MIN
:单精度浮点型(float
)的最大值和最小正非零值。DBL_MAX
和DBL_MIN
:双精度浮点型(double
)的最大值和最小正非零值。
- 浮点类型的精度和epsilon值
FLT_DIG
和DBL_DIG
:单精度和双精度浮点数的位数。FLT_EPSILON
和DBL_EPSILON
:单精度和双精度浮点数的最小正非零值,使得 1.0 + epsilon 不等于 1.0。
- 其他宏
CHAR_BIT
:每个字符中位数的位数,通常是 8。SCHAR_MAX
和SCHAR_MIN
:有符号字符类型(signed char
)的最大值和最小值。UCHAR_MAX
:无符号字符类型(unsigned char
)的最大值。
- 宽字符和长长类型的最大值
WCHAR_MAX
和WCHAR_MIN
:宽字符类型(wchar_t
)的最大值和最小值。LLONG_MAX
和LLONG_MIN
:长长整型(long long int
)的最大值和最小值。
- 浮点类型的特殊值
HUGE_VAL
:表示无穷大的宏。
要获取这些宏和常量的值,你需要包含相应的头文件,例如:
1
2
c#include <limits.h>
#include <float.h>
然后,你可以在你的程序中使用这些宏。例如:
1
2
cprintf("The maximum value of int is %d\n", INT_MAX);
printf("The minimum value of double is %e\n", DBL_MIN);
这些预定义的量对于编写可移植的程序非常有用,因为它们提供了与平台无关的数值。不同的编译器和平台可能会有不同的具体数值,但是它们都会遵循C标准的规定。
1.5 存储类
在C语言中,存储类定义了变量或函数的生命周期和作用域。以下是C语言中的几种存储类:
auto
这是默认的存储类,用于函数内部声明的局部变量。
auto
存储类表明变量的生命周期仅限于函数的执行期间,当函数调用结束时,这些变量的存储空间会被释放。例子:
1 2 3
cvoid func() { auto int x = 10; // 声明一个自动存储的整型变量x }
注意:在现代C语言标准中(C99及以后),
auto
关键字是可选的,因为局部变量默认就是自动存储的。
register
register
存储类用于声明寄存器变量。寄存器变量通常存储在CPU的寄存器中,而不是内存中,这可以加快访问速度。然而,由于寄存器的数量有限,编译器可以决定是否真的将变量存储在寄存器中。例子:
1 2 3
cvoid func() { register int x = 10; // 尝试将变量x存储在寄存器中 }
注意:
register
关键字在现代编程中很少使用,因为现代编译器通常能够自动优化变量的存储位置。
static
static
存储类用于声明具有静态存储期的变量或函数。对于变量,这意味着变量的生命周期贯穿整个程序的运行期,即使声明它们的函数或代码块已经执行完毕。对于函数,static
函数只能在定义它们的文件内部访问。例子:
1 2
c static int x = 10; // 声明一个静态变量x,它在程序的整个生命周期内都存在
静态变量的初始化只在程序的第一次运行时发生。
extern
extern
存储类用于声明具有外部链接的变量或函数。这意味着这些变量或函数可以在程序的其他文件中访问。extern
关键字通常用于在头文件中声明全局变量或函数,以便在多个源文件中共享。例子:
1 2 3 4 5
c// file1.c int x; // 定义一个全局变量x // file2.c extern int x; // 声明x为外部变量,允许在file2中访问
当使用
extern
声明全局变量时,必须在程序的某个地方有一个对应的定义。
这些存储类提供了不同的内存管理和访问控制机制,使得程序员可以根据需要选择最合适的存储类来声明变量和函数。
1.6 程序控制
C语言提供了多种逻辑语句来控制程序的流程,包括条件判断、循环和跳转语句。下面是对这些逻辑语句的详细说明:
条件判断语句
if 语句
用于根据条件执行不同的代码块。
语法:
1 2 3 4 5
cif (condition) { // 条件为真时执行的代码 } else { // 条件为假时执行的代码 }
可以嵌套使用,形成多重条件判断。
switch 语句
用于基于不同的情况执行不同的代码块。
语法:
1 2 3 4 5 6 7 8
cswitch (expression) { case constant-expression: // 当expression与constant-expression相等时执行的代码 break; // ... default: // 如果没有匹配的case,则执行这里的代码 }
break
语句用于退出switch结构,防止执行后续的case。
条件运算符(三元运算符)
用于基于条件选择两个值中的一个。
语法:
1 2
c result = condition ? value_if_true : value_if_false;
这是一种简洁的条件表达式,常用于简单的条件赋值。
循环语句
for 循环
用于重复执行一段代码,直到给定的条件不再满足。
语法:
1 2 3
cfor (initialization; condition; increment) { // 循环体 }
循环开始前初始化变量,然后检查条件,如果条件为真,执行循环体,然后进行增量操作,再次检查条件。
while 循环
先判断条件,如果条件为真,则执行循环体,然后重复这个过程。
语法:
1 2 3
cwhile (condition) { // 循环体 }
do-while 循环
先执行循环体,然后判断条件,如果条件为真,则重复执行循环体。
语法:
1 2 3
cdo { // 循环体 } while (condition);
至少执行一次循环体。
goto 语句
goto
语句用于实现无条件跳转,即跳转到程序中预先定义的标签位置。语法:
1 2 3 4 5 6 7
clabel: // 一些代码 // 某个条件满足时,跳转到标签位置 if (some_condition) { goto label; }
使用
goto
可以实现复杂的跳转逻辑,但过度使用或不当使用会导致程序的流程难以理解和维护,因此通常不推荐使用goto
语句。
这些逻辑语句是C语言控制流的基础,它们允许程序员根据不同的条件和需求来控制程序的执行路径。在实际编程中,合理使用这些语句可以提高代码的效率和可读性。
在C语言中,函数是执行特定任务的代码块,可以包含输入参数和返回值。作用域规则定义了变量和函数的可见性范围。下面是C语言中函数和作用域规则的详细说明:
1.7 函数
函数(Functions)
函数定义
函数由返回类型、函数名、参数列表和函数体组成。
语法:
1 2 3
creturnType functionName(parameterType parameterName, ...) { // 函数体 }
返回类型
- 指定函数执行完成后返回的数据类型,可以是基本数据类型、结构体类型或
void
(无返回值)。
- 指定函数执行完成后返回的数据类型,可以是基本数据类型、结构体类型或
函数名
- 唯一标识函数的名称,遵循标识符的命名规则。
参数列表
- 函数可以接收零个或多个参数,参数是传递给函数的值或变量的引用。
函数体
- 包含实现函数功能的语句序列。
函数原型
- 在函数定义之前声明函数的类型和参数,以便编译器在调用函数时检查类型匹配。
递归函数
- 函数在其定义中直接或间接调用自身。
函数指针
- 变量可以存储函数的地址,指向函数的变量称为函数指针。
作用域规则(Scope Rules)
- 局部作用域
- 局部变量(使用
auto
或不指定存储类)仅在其定义的函数或代码块内部可见。 - 当控制流离开定义局部变量的代码块时,局部变量的生命周期结束。
- 局部变量(使用
- 全局作用域
- 全局变量(使用
static
关键字或在所有函数外部定义的变量)在整个程序中可见。 - 全局变量的生命周期贯穿整个程序的运行期。
- 全局变量(使用
- 复合语句作用域
- 在复合语句(由花括号
{}
包围的代码块)中定义的变量仅在该复合语句内部可见。
- 在复合语句(由花括号
- 块作用域
- 使用
{...}
定义的代码块可以创建一个新的作用域,变量在这个作用域内声明,离开作用域后变量的生命周期结束。
- 使用
- 函数作用域
- 函数内部定义的变量仅在该函数内部可见,函数调用结束后,这些变量的生命周期结束。
- extern关键字
- 使用
extern
关键字可以声明一个具有外部链接的变量或函数,这意味着它可以在其他文件中访问。
- 使用
- 静态函数
- 使用
static
关键字定义的函数只能在定义它们的文件内部访问,具有内部链接。
- 使用
- 静态全局变量
- 即使使用
static
关键字,全局变量仍然可以在其他文件中通过extern
关键字访问,但它们在初始化时具有静态存储期。
- 即使使用
理解函数和作用域规则对于编写结构清晰、易于维护的C语言程序至关重要。合理地使用函数可以提高代码的复用性,而正确地管理作用域可以避免变量名冲突和意外的变量覆盖。
1.8 指针
在C语言中,指针是一种特殊的变量,它存储了另一个变量的内存地址。指针提供了一种间接访问和操作内存的方式,是C语言中非常强大和灵活的特性之一。以下是对C语言指针的详细说明:
指针的基本概念
- 指针变量
- 指针变量用于存储另一个变量的内存地址。
- 声明指针时,需要指定指针指向的变量类型。
- 地址运算符
&
- 用于获取变量的内存地址。
- 间接引用运算符
\*
- 用于访问指针指向的内存地址中存储的值。
指针的声明和初始化
1
2
3
cint var = 10; // 声明一个整型变量var
int *ptr; // 声明一个整型指针ptr
ptr = &var; // 将ptr初始化为var的地址
指针的类型
- 指针的类型决定了指针可以指向哪种类型的数据。例如,
int *
是指向int
的指针,double *
是指向double
的指针。
指针的算术运算
- 指针可以进行加法和减法运算,步长取决于指针指向的数据类型的大小。
指针与数组
- 指针和数组在C语言中密切相关。数组名本身就是一个指向数组首元素的指针。
指针的高级用法
- 指针数组
- 一个数组,其元素都是指针。
- 函数指针
- 指针可以指向函数,用于调用函数或在不同情况下动态选择函数。
- 指针和结构体
- 结构体指针用于访问结构体的成员。
- 动态内存分配
- 使用
malloc
,calloc
,realloc
和free
等函数在堆上分配和释放内存。
- 使用
- 指针的指针
- 一个指针,它存储了另一个指针的地址。
- 空指针
- 一个指针被初始化为
NULL
,表示它不指向任何地址。
- 一个指针被初始化为
指针的安全和陷阱
- 内存泄漏:忘记释放动态分配的内存。
- 野指针:未初始化或已释放内存的指针。
- 缓冲区溢出:错误的指针操作可能导致数据溢出到相邻的内存区域。
- 指针混淆:错误的指针运算可能导致程序崩溃或不可预测的行为。
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
c#include <stdio.h>
int main() {
int num = 20;
int *p = # // p是一个指针,存储了num的地址
printf("Value of num = %d\n", num);
printf("Value available at address in p = %d\n", *p);
int arr[] = {10, 20, 30};
int *p2 = arr; // p2是指向数组首元素的指针
printf("Value of arr[0] = %d\n", *(p2 + 0)); // 输出10
printf("Value of arr[1] = %d\n", *(p2 + 1)); // 输出20
return 0;
}
指针是C语言中一个非常强大的特性,但也需要谨慎使用。正确地理解和使用指针可以提高程序的性能和灵活性,但错误的指针操作也可能导致程序错误和安全问题。
1.9 输入输出
标准输入输出库函数
C语言标准库中的<stdio.h>
头文件提供了一系列的标准输入输出函数,这些函数通过标准输入输出流(stdin、stdout)进行操作。
- printf()
- 用于格式化输出到标准输出(stdout)。
- 语法:
int printf(const char *format, ...);
- 可以包含多种格式说明符,如
%d
(整数)、%f
(浮点数)、%s
(字符串)等。
- scanf()
- 用于从标准输入(stdin)读取并格式化数据。
- 语法:
int scanf(const char *format, ...);
- 同样支持多种格式说明符,与
printf()
相对应。
- fprintf()
- 类似于
printf()
,但输出到指定的文件流。
- 类似于
- fscanf()
- 类似于
scanf()
,但从指定的文件流读取数据。
- 类似于
- sprintf()
- 将格式化的数据写入字符串。
- sscanf()
- 从字符串中读取数据。
输入输出示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
c#include <stdio.h>
int main() {
int age;
float salary;
char name[50];
// 使用printf()输出文本
printf("Please enter your name: ");
// 使用scanf()从标准输入读取字符串
scanf("%s", name);
// 继续读取其他数据
printf("Please enter your age: ");
scanf("%d", &age);
printf("Please enter your salary: ");
scanf("%f", &salary);
// 输出读取的数据
printf("Name: %s\n", name);
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
return 0;
}
文件输入输出
除了标准输入输出外,C语言还提供了文件输入输出功能,允许程序读写文件。
- fopen()
- 用于打开文件,并返回一个文件指针。
- 语法:
FILE *fopen(const char *filename, const char *mode);
- fclose()
- 用于关闭文件,释放文件资源。
- 语法:
int fclose(FILE *stream);
- fread()
- 从文件中读取数据到缓冲区。
- fwrite()
- 将数据从缓冲区写入文件。
- fgets()
- 从文件中读取一行数据到字符串。
- fputs()
- 将字符串写入文件。
文件输入输出示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
c#include <stdio.h>
int main() {
FILE *file;
char filename[] = "example.txt";
char buffer[100];
// 打开文件用于写入
file = fopen(filename, "w");
if (file == NULL) {
perror("Error opening file");
return 1;
}
// 写入数据到文件
fputs("Hello, World!\n", file);
// 关闭文件
fclose(file);
// 打开文件用于读取
file = fopen(filename, "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
// 从文件读取数据
fgets(buffer, sizeof(buffer), file);
// 输出读取的数据
printf("%s", buffer);
// 关闭文件
fclose(file);
return 0;
}
作用域和可移植性
- 输入输出函数通常具有全局作用域,可以在程序的任何地方使用。
- 使用标准输入输出函数可以提高程序的可移植性,因为这些函数与操作系统无关。
注意事项
- 始终检查文件操作的返回值,确保操作成功。
- 使用完毕后应关闭所有打开的文件。
- 格式化字符串应谨慎使用,避免产生安全漏洞,如缓冲区溢出。
C语言的输入输出机制为程序提供了与用户和文件系统交互的能力,是程序设计中不可或缺的一部分。正确地使用这些功能可以提高程序的用户体验和数据管理能力。