【重学C语言】三、C语言最简单的程序
- 最简单的程序
- 常量和变量
- 数据类型和存储
- [`printf` 函数](#
printf
函数) - [`scanf` 函数](#
scanf
函数) - [`getchar()` 与 `putchar()` 函数](#
getchar()
与putchar()
函数) -
-
- [`getchar()` 函数](#
getchar()
函数) - [`putchar()` 函数](#
putchar()
函数) - 注意事项
- [`getchar()` 函数](#
-
最简单的程序
c
#include <stdio.h>
int main(int argc, char* argv[]){
return 0;
}
-
#include <stdio.h>
:这是C语言的预处理指令,用于包含标准输入输出库(Standard Input/Output Library)。这样,程序就可以使用printf、scanf等函数。 -
int main(int argc, char* argv[])
:这是C程序的主函数。程序从这里开始执行。main函数有两个参数argc
:命令行参数的数量(包括程序名本身)。argv
:一个指向字符指针的数组,用于存储命令行参数的值。
-
return 0;
:这表示main函数的结束,并返回一个整数值0。在C语言中,main函数返回0通常表示程序成功执行。
头文件
头文件(Header Files)通常用于声明函数、变量、宏定义、类型定义等,它们被包含(include)在源文件中,以便在编译时能够找到这些声明。头文件有助于组织代码,提高代码的可读性和可维护性,并促进代码的重用。
头文件通常以 .h
作为文件扩展名。以下是一些关于C语言头文件的常见点:
-
预处理指令 :在C源文件中,我们使用
#include
预处理指令来包含头文件。这告诉编译器在编译之前要查找并包含指定的头文件。c#include <stdio.h> // 包含标准输入输出库的头文件
或者,对于用户自定义的头文件,可以使用双引号:
c#include "myheader.h" // 包含用户自定义的头文件
-
标准库头文件 :C语言标准库提供了一系列头文件,用于支持各种功能,如输入输出、字符串处理、数学运算等。这些头文件通常位于系统的特定目录下,并使用尖括号(
< >
)包含。例如:
<stdio.h>
:用于输入输出函数,如printf
和scanf
。<stdlib.h>
:包含常用的工具函数,如内存分配、随机数生成等。<string.h>
:包含字符串处理函数,如strcpy
和strlen
。<math.h>
:包含数学函数和宏,如sin
、cos
和sqrt
。
-
用户自定义头文件:除了标准库头文件外,程序员还可以创建自己的头文件,以组织和共享代码。这些头文件可以包含用户定义的函数原型、变量声明、宏定义等。
-
防止头文件重复包含 :为了避免头文件被多次包含,通常会在头文件的开始处使用预处理器指令来检查是否已包含该头文件。这通常是通过定义一个唯一的宏并使用
#ifndef
、#define
和#endif
来实现的。c#ifndef MYHEADER_H // 检查MYHEADER_H是否已定义 #define MYHEADER_H // 定义MYHEADER_H // 头文件的内容 #endif // 结束条件编译
-
内联函数 :在头文件中,有时也会定义内联函数(使用
inline
关键字)。内联函数是一种优化手段,建议编译器在调用点将函数体直接插入,而不是进行常规的函数调用。 -
头文件与源文件 :在C语言项目中,通常将函数的实现放在源文件中(
.c
文件),而将函数的声明放在头文件中。这样,其他源文件可以通过包含相应的头文件来使用这些函数。
通过合理地使用头文件,可以保持代码的模块化,使得代码更易于管理和维护。同时,这也促进了代码的重用,使得不同部分的代码能够共享相同的函数和变量声明。
使用尖括号 < >
当使用尖括号 < >
来包含头文件时,编译器会在标准库目录(由编译器的安装和配置决定)中查找该头文件。这些目录通常包含系统提供的标准库头文件。例如:
c
#include <stdio.h>
这告诉编译器在标准库目录中查找 stdio.h
头文件。
使用双引号 ""
当使用双引号 ""
来包含头文件时,编译器首先会在当前源文件所在的目录(或指定的其他目录)中查找头文件。如果未找到,编译器通常会继续查找标准库目录。这种方式通常用于包含用户自定义的头文件或项目特定的头文件。例如:
c
#include "myheader.h"
这告诉编译器首先在当前目录(或指定的其他目录)中查找 myheader.h
头文件。
区别与注意事项
-
搜索路径 :尖括号
< >
主要用于搜索系统头文件,而双引号""
用于搜索用户自定义或项目特定的头文件。 -
可移植性 :使用尖括号
< >
包含标准库头文件通常更具可移植性,因为不同系统和编译器通常都会将标准库安装在相同或类似的目录结构中。而使用双引号""
包含头文件时,如果头文件的位置不是固定的,那么代码的可移植性可能会受到影响。 -
项目组织 :在大型项目中,通常会将头文件组织在特定的目录中,并使用双引号
""
来包含它们。这样可以更好地控制头文件的搜索路径,避免命名冲突,并提高代码的组织性。 -
编译器选项 :一些编译器允许通过编译选项来指定额外的头文件搜索路径。这可以进一步影响
< >
和""
的行为。
示例
假设你有以下文件结构:
project/
main.c
include/
myheader.h
在 main.c
中,你应该使用双引号来包含 myheader.h
:
c
#include "include/myheader.h"
或者,你可以通过编译选项告诉编译器在 include/
目录下查找头文件,然后在 main.c
中直接使用:
c
#include "myheader.h"
而对于标准库头文件,如 stdio.h
,你应该始终使用尖括号来包含它:
c
#include <stdio.h>
如果你希望使用尖括号 < >
来包含自己写的头文件,你需要将头文件放在编译器的包含文件搜索路径中。这样,编译器在编译时就能够找到这些头文件。
将自定义头文件添加到编译器的搜索路径中,通常有以下几种方法:
-
将头文件放在系统包含目录中:这通常不是一个推荐的做法,因为系统包含目录是专门为系统库保留的。修改这些目录可能会导致与系统库的冲突或权限问题。
-
修改编译器的包含目录选项 :大多数编译器都允许通过命令行选项来指定额外的包含目录。例如,在GCC和Clang中,你可以使用
-I
选项来添加包含目录。假设你的头文件位于
my_include_dir
目录下,你可以在编译时这样指定:shgcc -I./my_include_dir main.c -o main
这样,编译器会在
my_include_dir
目录中搜索通过尖括号包含的头文件。 -
使用环境变量 :某些编译器和系统允许使用环境变量来设置包含目录。例如,
C_INCLUDE_PATH
环境变量在某些系统中可以用来指定额外的包含目录。 -
项目特定的构建系统:如果你使用像Makefile、CMake或其他构建系统来管理你的项目,这些系统通常都提供了配置包含目录的方法。你可以在这些构建脚本中指定额外的包含目录,以便使用尖括号包含自定义头文件。
在将自定义头文件添加到编译器的搜索路径后,你就可以在源文件中使用尖括号来包含它们了:
c
#include <my_custom_header.h>
请注意,使用尖括号来包含自定义头文件可能会降低代码的可移植性,因为其他开发者或构建系统可能不知道这些自定义头文件的路径。因此,在团队项目中或需要高度可移植性的情况下,通常推荐使用双引号来包含自定义头文件。这样,你可以通过构建系统或源代码管理来确保包含路径的一致性。
主函数
main
函数是程序的入口点,有且只有一个。当你运行一个C程序时,操作系统会调用 main
函数,并从那里开始执行你的代码。main
函数的标准定义如下:
c
int main() {
// 程序执行的代码
return 0;
}
这里,int
表示 main
函数返回一个整数值,通常用来表示程序的退出状态。return 0;
表示程序正常退出。在大多数操作系统中,返回 0
通常表示程序成功执行,而非零值表示出现了某种错误。
main
函数可以带有参数,通常用于从命令行接收输入。带有参数的 main
函数定义如下:
c
int main(int argc, char *argv[]) {
// 程序执行的代码
return 0;
}
在这里:
argc
(argument count)是一个整数,表示传递给程序的命令行参数的数量(包括程序名本身)。argv
(argument vector)是一个指向字符指针的数组,每个指针指向一个命令行参数的字符串。argv[0]
通常是程序的名称,argv[1]
是第一个命令行参数,依此类推。
例如,如果你有一个名为 my_program
的程序,并在命令行中这样调用它:
sh
my_program arg1 arg2 arg3
那么 argc
的值会是 4
,而 argv
数组会包含以下字符串:
argv[0]
: "my_program"argv[1]
: "arg1"argv[2]
: "arg2"argv[3]
: "arg3"
通过处理 argc
和 argv
,你的程序可以基于命令行参数来执行不同的操作。这是许多命令行工具常用的方法。
main
函数也可以包含第三个参数,一个指向 char *
的指针,用于存储环境变量。这通常用于更复杂的应用,例如:
c
int main(int argc, char *argv[], char *envp[]) {
// 程序执行的代码
// 可以使用 envp 来访问环境变量
return 0;
}
在这里,envp
是一个指向环境字符串的指针数组,每个字符串的格式通常是 "NAME=value"
。这个参数不常用,除非你需要直接访问或修改环境变量。
认识三个错误
- error C1083: 无法打开包括文件: "luck.h": No such file or directory
- error LNK2019: 无法解析的外部符号 main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了该符号
- error LNK2005: main 已经在 test_demo.obj 中定义 多个主函数问题
常量和变量
常量
常量(Constant)在编程中是一个非常重要的概念,它指的是在程序运行期间其值不能改变的量。一旦常量被赋值,它的值就不能再被修改。这种特性使得常量在程序中非常有用,特别是在需要确保某个值在程序执行过程中始终保持不变时。
以下是关于常量的一些基本介绍:
-
定义方式:
- 在许多编程语言中,常量通常使用特定的关键字或符号来定义,例如
const
(C++、JavaScript)、final
(Java)或#define
(C、C++的预处理指令)。 - 常量可以是基本数据类型(如整数、浮点数、字符等),也可以是复合数据类型(如数组、结构体、类等)。
- 在许多编程语言中,常量通常使用特定的关键字或符号来定义,例如
-
命名规范:
- 常量通常使用大写字母命名,以便与其他变量区分开来。例如,
MAX_VALUE
或PI
。 - 命名应简洁明了,能够清晰地表达常量的含义和用途。
- 常量通常使用大写字母命名,以便与其他变量区分开来。例如,
-
用途:
- 常量常用于表示程序中不会改变的值,如数学常数、物理常量、配置参数等。
- 使用常量可以提高代码的可读性和可维护性,因为常量名通常比魔法数字(直接在代码中使用的数字)更有意义。
- 常量还可以用于限制某些变量的取值范围,防止程序中出现意外的值。
-
与变量的区别:
- 常量的值在程序运行期间保持不变,而变量的值可以在程序运行过程中被修改。
- 常量通常在定义时就进行初始化,而变量可以在定义后随时赋值。
- 由于常量的值不可变,因此它们通常比变量更安全,可以避免因误修改而导致的程序错误。
-
示例:
-
在C++中,可以使用
const
关键字定义常量:cppconst int MAX_VALUE = 100; // 定义一个整型常量 const double PI = 3.14159; // 定义一个浮点型常量
-
在JavaScript中,同样可以使用
const
关键字:javascriptconst MAX_VALUE = 100; // 定义一个常量 // MAX_VALUE = 200; // 这行代码会报错,因为MAX_VALUE是常量,不能被重新赋值
-
了解和使用常量是编程基础中的一部分,它们有助于编写出更加健壮、易于理解和维护的代码。
常量的类型可以根据其表示的数据类型或用途进行分类。以下是一些常见的常量类型及其描述:
-
字符串常量:
- 定义:凡是用双引号引起来的部分,称为字符串常量。
- 示例:"Hello, World!"、"abc"、"123"。
- 用途:表示文本信息。
-
整数常量:
- 定义:直接写上的数字,没有小数点。
- 示例:100、200、-250、0。
- 整数常量可以使用十进制、二进制、八进制或十六进制表示法。
- 用途:表示整数值。
-
浮点数常量:
- 定义:直接写上的数字,有小数点。
- 示例:2.5、-3.14、0.0。
- 用途:表示带有小数部分的数值。
科学计数法是一种用于表示非常大或非常小的数值的方法。它把一个数表示成a与10的n次幂相乘的形式,其中1≤|a|<10,n为整数。这种表示方法避免了浪费大量的空间和时间来标记或运算较大的数。
例如,当我们要表示一个非常大的数,如光的速度约为300,000,000米/秒,或者全世界的人口数约为6,100,000,000时,使用科学计数法可以更方便地表示这些数。光的速度可以表示为3×10^8^米/秒,而全世界的人口数可以表示为6.1×10^9^。
在编程中,科学计数法也常用于表示浮点数。例如,在C语言中,可以使用科学计数法来表示浮点数,如
float x = 32e-1
和float y = 45e2
,其中e
代表10的幂。因此,科学计数法常量就是使用科学计数法表示的数值常量,它们可以是整数、浮点数或其他类型的数据,但都以科学计数法的形式表示。这种表示方法有助于简化大数或小数的书写和计算,提高代码的可读性和效率。
-
字符常量:
- 定义:凡是用单引号引起来的单个字符。
- 示例:'A'、'b'、'9'、'中'。
- 用途:表示单个字符。
-
布尔常量:
- 定义:只有两个取值,即真(true)和假(false)。
- 用途:表示逻辑值。
-
空常量:
- 定义:null,代表没有任何数据。
- 用途:表示空值或缺失值。
-
特殊常量:
- 定义:一些特殊的固定值,用于表示一些特定的含义。
- 用途:根据具体的应用场景和编程语言而定。
此外,根据编程语言的不同,可能还有其他类型的常量,例如枚举常量(在枚举类型中定义的常量)或符号常量(通常用于表示一些固定的值或配置参数,它们不占内存,只是在预编译阶段使用)。
需要注意的是,不同类型的常量在编程中有不同的用途和限制,并且它们的定义和使用方式可能因编程语言的不同而有所差异。因此,在实际编程中,需要根据具体的编程语言和需求来选择合适的常量类型。
ASCII 码表
转义字符
转义字符(Escape Characters)用于表示一些无法直接打印出来的字符,或者用于表示具有特殊含义的字符。转义字符以反斜杠 \
开始,后跟一个或多个字符,以表示特定的字符或操作。
例如:
c
#include <stdio.h>
int main() {
printf("Hello, World!\n"); // 输出 "Hello, World!" 并换行
printf("This is a tab:\tAnd this is after the tab.\n"); // 输出制表符和文本
printf("This is a backslash: \\ And this is after the backslash.\n"); // 输出反斜杠和文本
return 0;
}
在这个例子中,\n
使得 "Hello, World!" 后面的内容在新的一行开始,\t
插入了一个制表符,\\
则用于在字符串中表示实际的反斜杠字符。
请注意,转义字符必须在字符串或字符常量中使用,否则它们将被视为普通字符。例如,在 printf
函数的参数中,转义字符被识别并转换为相应的字符。
此外,\ddd
和 \xhh
允许你使用八进制或十六进制数来表示字符。这在处理非打印字符或特定控制字符时非常有用。例如,ASCII码中的换行符(LF)可以用 \x0A
或 \012
来表示。
关键字
C语言的关键字是用于定义变量类型、控制程序流程、进行函数声明等特定功能的预定义保留标识符。它们具有特殊的含义,并且不能被用作变量名、函数名或任何其他标识符。以下是C语言中的一些主要关键字分类及其示例:
数据类型关键字
char
:声明字符型变量或函数。double
:声明双精度变量或函数。float
:声明浮点型变量或函数。int
:声明整型变量或函数。long
:声明长整型变量或函数。short
:声明短整型变量或函数。signed
:声明有符号类型变量或函数。unsigned
:声明无符号类型变量或函数。void
:声明无类型(通常用于函数返回类型、指针类型等)。
存储类关键字
auto
:声明自动变量(局部变量)。extern
:声明变量或函数是在其他文件中定义的。register
:建议编译器将局部变量存储在寄存器中(现代编译器通常忽略此建议)。static
:声明静态变量或函数(生命周期持续到程序结束)。
修饰符关键字
const
:声明常量或常量指针。volatile
:告诉编译器该变量可能会被程序之外的因素(如硬件)改变。
控制流程关键字
break
:跳出当前循环或switch语句。continue
:跳过当前循环的剩余部分,进入下一次迭代。if
:条件语句。else
:与if
一起使用的条件语句。switch
:多分支选择语句。case
:与switch
一起使用,表示一个分支。default
:与switch
一起使用,表示默认分支。for
:循环语句。while
:循环语句。do-while
:循环语句。goto
:无条件跳转到标签位置(在现代编程中很少使用)。return
:从函数中返回。
函数相关关键字
call
:在函数调用时使用,但不是C语言的关键字。C语言使用函数名直接调用函数。
其他关键字
assert
:用于在运行时进行断言验证。enum
:声明枚举类型。struct
:声明结构体类型。union
:声明共用体类型。typedef
:为数据类型定义别名。sizeof
:获取类型或对象的大小。
请注意,不是所有的C编译器都支持相同的关键字集,某些关键字可能是特定编译器或平台特有的。此外,call
并不是C语言的标准关键字,它可能是在某些上下文或特定库中被用作宏或函数名。在编写C语言代码时,应确保使用的关键字符合所使用编译器和标准的规范。
变量
在C语言中,变量是用来存储不同类型的数据的内存位置。你可以给这些内存位置赋予一个名字,这样你就可以在程序的后续部分引用它们。每个变量都有一个特定的类型,它决定了变量可以存储什么类型的数据,以及这些数据如何存储在内存中。
变量声明
在C语言中,你需要在使用变量之前声明它。声明变量时,你需要指定变量的类型,以及变量的名称。例如:
c
int myVariable;
这里,int
是变量类型(整数),myVariable
是变量名。
变量初始化
你可以在声明变量的同时给它一个初始值。这叫做变量的初始化。例如:
c
int myVariable = 10;
在这里,myVariable
被初始化为整数值 10
。
变量类型
整数类型
-
int
:有符号整数类型,用于存储正整数、负整数和零。具体的存储大小和范围取决于编译器和平台,但通常情况下,int
类型占用4个字节(32位),并可以表示从 -2,147,483,648 到 2,147,483,647 的范围(包括边界值)。 -
short
或short int
:有符号短整数类型,通常占用2个字节(16位)。 -
long
或long int
:有符号长整数类型,通常占用4个字节(32位)或更多,取决于编译器和平台。 -
long long
或long long int
:有符号长长整数类型,通常占用8个字节(64位),用于存储非常大的整数。 -
unsigned
:无符号整数类型前缀,可以与上述整数类型组合使用(例如unsigned int
、unsigned short
、unsigned long
等),表示只能存储非负整数的类型。
浮点类型
-
float
:单精度浮点数类型,通常占用4个字节(32位)。 -
double
:双精度浮点数类型,通常占用8个字节(64位),提供比float
更高的精度。 -
long double
:扩展精度浮点数类型,其精度和大小因编译器而异。
字符类型
char
:字符类型,通常占用1个字节(8位),用于存储字符(如字母、数字或符号)。它也可以用来存储小范围的整数。
变量的作用域和生命周期
变量的作用域是指变量在程序中的可见性,即在哪里可以访问该变量。变量的生命周期是指变量在内存中存在的时间。这取决于变量是在哪里声明的:
- 局部变量:在函数或代码块内部声明的变量。它们的作用域仅限于声明它们的函数或代码块。当函数或代码块执行完毕时,局部变量会被销毁。
- 全局变量:在函数外部声明的变量。它们的作用域是整个程序,从声明点开始直到程序结束。全局变量的生命周期是整个程序的执行时间。
- 静态变量 :使用
static
关键字声明的变量。静态变量的生命周期是整个程序的执行时间,但它们的作用域取决于它们的声明位置(函数内部或外部)。
标识符
在C语言中,标识符是用来给变量、函数、类型定义等命名的。标识符的命名需要遵循一定的规则,以确保代码的可读性和避免语法错误。以下是C语言标识符的命名规则:
字符组成
- 标识符由字母(a-z,A-Z)、数字(0-9)和下划线(_)组成。
- 标识符的第一个字符必须是字母或下划线,不能是数字。
大小写敏感
- C语言中的标识符是大小写敏感的,即大写字母和小写字母被视为不同的字符。例如,
myVariable
和myvariable
是两个不同的标识符。
长度限制
- 虽然C语言标准没有明确规定标识符的最大长度,但不同的编译器可能有不同的限制。一般来说,建议标识符长度不要过长,以提高代码的可读性。
避免使用关键字
- 标识符不能是C语言的关键字或保留字。关键字是C语言中具有特殊含义的词汇,如
int
、if
、for
等。使用关键字作为标识符会导致语法错误。
命名风格
虽然C语言标准没有规定具体的命名风格,但通常建议采用有意义的命名,以提高代码的可读性。例如,变量名应该能够清晰地表达其用途或存储的数据类型。
命名风格对于代码的可读性和维护性至关重要。以下是一些常见的命名风格及其特点:
-
小驼峰命名法 (lowerCamelCase)
- 变量名、函数名等由多个单词组成时,第一个单词首字母小写,后面每个单词的首字母大写。
- 示例:
firstName
、calculateSum
-
大驼峰命名法 (UpperCamelCase)
- 类名通常使用这种风格,也称为帕斯卡命名法。
- 示例:
Student
、GoodStudent
-
下划线命名法 (snake_case)
- 单词之间用下划线分隔,常用于变量名和函数名。
- 示例:
my_variable
、get_next_value
-
匈牙利命名法
- 变量名以类型信息开始,后面跟具体的名称。这种方法在现代编程实践中逐渐被淘汰,因为它增加了代码的冗余性。
- 示例:
intAge
、strFirstName
-
Unix风格
- 单词全部小写,多个单词之间用下划线分隔。
- 示例:
text_mutex
、kernel_text_address
在命名时,除了考虑风格外,还需要注意以下几点:
- 避免使用拼音和英文混合命名,以提高代码的国际化和可读性。
- 变量名应该尽量简洁且具有描述性,能够准确反映变量的用途或内容。
- 避免使用与C语言关键字或保留字相同的名称,以防止编译错误。
- 避免使用仅靠大小写区分的相似标识符,以减少混淆和错误。
选择合适的命名风格可以提高代码的一致性和可读性,使得代码更易于维护和扩展。不同的团队或项目可能会采用不同的命名风格,因此在实际开发中,最好遵循团队或项目的约定。
命名规则示例
以下是一些符合C语言标识符命名规则的示例:
c
int myInteger; // 正确的标识符
char firstName; // 正确的标识符
float averageScore; // 正确的标识符
_privateVariable; // 正确的标识符,以下划线开头
以下是一些不符合C语言标识符命名规则的示例:
c
2ndNumber; // 错误:以数字开头
int; // 错误:使用了关键字
my-variable; // 错误:包含非法字符(-)
遵循这些规则可以帮助你编写出清晰、易读的C语言代码。
使用变量示例
下面是一个简单的C程序示例,演示了如何声明、初始化和使用变量:
c
#include <stdio.h>
int main() {
int myInteger = 42; // 声明并初始化一个整数变量
char myCharacter = 'A'; // 声明并初始化一个字符变量
float myFloat = 3.14f; // 声明并初始化一个浮点数变量
printf("整数变量: %d\n", myInteger);
printf("字符变量: %c\n", myCharacter);
printf("浮点数变量: %f\n", myFloat);
return 0;
}
在这个示例中,我们声明并初始化了三个不同类型的变量,并使用 printf
函数将它们的值输出到控制台。
数据类型和存储
存储大小和对齐
- 数据类型的大小(以字节为单位)可以通过 sizeof 运算符来确定。例如,sizeof(int) 将返回 int 类型在特定编译器和平台上的大小(以字节为单位)。
- 数据类型在内存中的存储通常遵循对齐规则,这是为了提高数据访问的效率。对齐规则可能因编译器和硬件平台而异。
存储表示
- 整型数据通常以二进制补码形式存储,这种方式能够表示正数、负数和零。
- 浮点型数据按照IEEE 754标准存储,该标准定义了单精度和双精度浮点数的格式。
- 字符型数据通常以ASCII码或相应的字符集编码存储。
printf
函数
printf
函数是 C 语言中的一个标准库函数,用于格式化输出到标准输出设备(通常是终端或控制台)。它定义在 <stdio.h>
头文件中,是 C 语言标准输入输出库(stdio 库)的一部分。
printf
函数的基本语法如下:
c
int printf(const char *format, ...);
format
是一个包含文本和格式说明符的字符串,用于指定输出格式。格式说明符以%
开头,后面跟着一个或多个字符,用来表示如何格式化相应的参数。...
表示可变参数列表,用于提供与format
字符串中的格式说明符对应的值。
printf
函数根据 format
字符串中的格式说明符来格式化输出参数,并将结果发送到标准输出。它返回一个整数,表示成功打印的字符数(不包括结尾的空字符)。
%%
表示一个 %
此外,还可以使用格式修饰符来控制输出宽度、精度和对齐方式。这些修饰符通常与printf
和scanf
等函数一起使用。以下是一些常见的格式修饰符:
-
宽度修饰符 (
-
,0
, 数字,*
)-
:左对齐输出。0
:用0填充空白位置(仅与数字一起使用)。- 数字:指定最小字段宽度。
*
:宽度由后续参数指定。例如:printf("%-10d", 123);
会左对齐输出数字123,并用空格填充至10个字符的宽度。
-
精度修饰符 (
.
后跟数字或*
)- 用于浮点数和字符串:指定小数点后的位数或最大字符数。
*
:精度由后续参数指定。例如:printf("%.2f", 3.14159);
会输出3.14
。
-
长度修饰符 (
h
,l
,ll
,L
)- 用于指定整数和浮点数的类型大小。例如:
printf("%lld", 123456789012345LL);
用于输出长整型(long long int)。
- 用于指定整数和浮点数的类型大小。例如:
结合使用这些修饰符,你可以更精细地控制输出格式。例如:
c
#include <stdio.h>
int main() {
int num = 123;
float f = 3.14159;
printf("%-10d\n", num); // 左对齐,宽度为10
printf("%010d\n", num); // 右对齐,用0填充至宽度10
printf("%.2f\n", f); // 保留两位小数
printf("%10.2f\n", f); // 宽度为10,保留两位小数
return 0;
}
注意:格式修饰符的具体效果和可用性可能因编译器和平台的不同而有所差异。因此,在实际使用时,建议查阅相关文档或参考手册以确保正确使用。
下面是一个简单的 printf
函数使用示例:
c
#include <stdio.h>
int main() {
int a = 10;
float b = 3.14;
char c = 'A';
char str[] = "Hello, World!";
printf("整数:%d\n", a);
printf("浮点数:%.2f\n", b);
printf("字符:%c\n", c);
printf("字符串:%s\n", str);
return 0;
}
运行这个程序会在控制台上输出:
c
整数:10
浮点数:3.14
字符:A
字符串:Hello, World!
注意,printf
函数不会自动在输出末尾添加换行符,如果需要换行,需要在 format
字符串中显式添加 \n
。同时,由于 printf
使用了可变参数列表,所以类型不匹配或格式错误可能会导致未定义的行为或运行时错误。因此,在使用 printf
时需要确保提供的参数与 format
字符串中的格式说明符相匹配。
scanf
函数
scanf函数是C语言中的一个标准库函数,用于从标准输入流(通常是键盘)读取数据。它声明在头文件stdio.h
中,因此在使用scanf函数时需要加上#include <stdio.h>
。
scanf函数的基本语法如下:
c
scanf("format string", &variable1, &variable2, ...);
- "format string"是一个格式化字符串,用于指定要读取的数据类型和数据格式
&variable1
、&variable2
等是要读取数据的变量的地址。
scanf函数会根据格式化字符串指定的格式来解析输入,将解析的结果存储到指定的变量中。例如,如果我们想要从用户那里获取一个整数并将其存储到一个名为num的变量中,我们可以使用以下代码:
c
int num;
scanf("%d", &num);
在格式化字符串中,可以使用占位符来表示要读取的数据类型。例如,%d
表示整数,%f
表示浮点数,%s
表示字符串等。scanf函数在处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等,所以用户输入的数据之间可以有一个或多个空格。
需要注意的是,在使用scanf函数时,需要确保提供的变量地址与格式化字符串中的占位符类型和数量相匹配,否则可能会导致错误或不可预测的行为。此外,还需要注意处理用户输入错误或不符合预期的情况,以避免程序崩溃或数据错误。
scanf_s
是一个安全的版本的 scanf
函数,它主要在 Microsoft 的 Visual C++ 编译器和一些其他兼容的编译器中可用。这个函数是为了解决 scanf
函数中可能存在的缓冲区溢出问题而引入的。
在 scanf
中,如果用户输入的数据超出了目标变量的容量,可能会导致缓冲区溢出,这是一个严重的安全问题。为了解决这个问题,scanf_s
函数需要更多的参数来指定输入数据的最大大小。
例如,使用 scanf
读取一个字符串可能如下:
c
char buffer[10];
scanf("%s", buffer);
但使用 scanf_s
时,你需要指定目标缓冲区的大小:
c
char buffer[10];
scanf_s("%s", buffer, (unsigned)_countof(buffer));
这里,_countof
是一个宏,用于计算数组的元素数量。这样,scanf_s
就可以确保不会读取超过缓冲区大小的输入。
需要注意的是,scanf_s
不是标准 C 的一部分,因此在非 Microsoft 的编译器上可能不可用。在编写跨平台的代码时,你可能需要使用其他方法来确保输入的安全性,比如使用 fgets
读取一行,然后手动解析它,或者使用第三方库提供的更安全的输入函数。
总的来说,如果你正在使用 Microsoft 的 Visual C++ 或兼容的编译器,并且关心输入的安全性,那么使用 scanf_s
替代 scanf
是一个好主意。但在其他情况下,你可能需要寻找其他解决方案。
总之,scanf函数是C语言中实现用户交互和数据处理的重要工具之一,掌握其使用方法和注意事项对于编写高效、安全的C语言程序至关重要。
getchar()
与 putchar()
函数
getchar()
和 putchar()
是C语言标准库中的两个函数,用于从标准输入(通常是键盘)读取单个字符和向标准输出(通常是屏幕)写入单个字符。
getchar()
函数
getchar()
函数用于从标准输入流(stdin)中读取下一个字符,并将其作为 int
类型的值返回。如果读取成功,则返回读取的字符的ASCII码值;如果读取失败或到达文件末尾,则返回 EOF
(End Of File)。
示例:
c
#include <stdio.h>
int main() {
int ch;
printf("请输入一个字符:");
ch = getchar();
if (ch != EOF) {
printf("你输入的字符是:%c\n", (char)ch);
} else {
printf("读取失败或到达文件末尾。\n");
}
return 0;
}
putchar()
函数
putchar()
函数用于将单个字符写入到标准输出流(stdout)。它接受一个 int
类型的参数(通常是字符的ASCII码值),并将其对应的字符输出到屏幕上。如果输出成功,则返回写入的字符;否则返回 EOF
。
示例:
c
#include <stdio.h>
int main() {
char ch = 'A';
putchar(ch); // 输出字符 'A'
putchar('\n'); // 输出换行符
return 0;
}
注意事项
getchar()
和putchar()
函数都是阻塞的,意味着程序会等待用户的输入或输出操作完成才会继续执行。- 在处理
getchar()
的返回值时,通常要检查它是否等于EOF
,以确保没有发生错误或到达文件末尾。 getchar()
和putchar()
也可以用于文件操作,只需将文件指针作为stdin
或stdout
的替代品传递给fgetc()
和fputc()
函数即可。
这两个函数非常适用于简单的字符输入/输出操作,但如果你需要处理更复杂的输入/输出任务(如格式化输出、读取多个字符等),那么你可能需要使用更强大的函数,如 scanf()
、printf()
和 fgets()
等。