开始使用C
要开始使用 C,您需要做两件事:
- 用于编写 C 代码的文本编辑器,如记事本
- 编译器,如 GCC,将 C 代码翻译成计算机可以理解的语言
有许多文本编辑器和编译器可供选择。 我们介绍几种编辑器和编译环境(见下文)。
注意 ⚠️ :Linux 和 Mac 系统可以直接安装 GCC,Windows 系统可以安装 MinGW。
编辑器
Visual Studio Code
VSCode 下载安装地址:Visual Studio Code – Code Editing. Redefined
下载安装好 VSCode 后,我们需要安装下面两个插件更方便的支持 C 语言环境。
C 快速入门
如果您不理解下面的代码,请不要担心 —— 我们将在后面的章节中详细讨论。 现在,专注于如何运行代码。
创建第一个程序
让我们创建我们的第一个 C 文件。
打开编辑器选择菜单 File
> New File
创建一个空的文本文件。
编写以下 C 代码并将文件另存为 hello.c
(File
> Save As
):
#include <stdio.h>
int main() {
printf("Hello World!");
return 0;
}
编辑器编译运行
在 Visual Studio Code 中,点击 Run Code
以运行(执行)程序。 结果看起来像这样:
[Running] cd "/Users/mack/master/C/" && gcc demo.c -o demo && "/Users/mack/master/C/"demo
Hello World!
[Done] exited with code=0 in 1.199 seconds
命令行编译运行
通过命令行进入 hello.c
所在目录
cd /Users/mack/master/C
使用 gcc
编译 hello.c
文件,
$ gcc -o hello hello.c
运行成功之后会在当前目录下看到 hello 文件,直接在命令后输入 ./hello
命令,您将得到如下输出结果:
Hello World
Docker 运行环境
可以简单用 Docker 来安装编译环境,这样在 Windows、MacOS、Linux 下都可以拥有完全相同的编译环境。
镜像基于 Alpine Linux 镜像,只有 5MB 镜像,并且包含 C/C++ 编译器(gcc/g++ 包)
- 安装 Docker
- 使用 docker pull 命令
docker pull frolvlad/alpine-gxx
- 创建 Dockerfile 文件
FROM alpine:3.14 RUN apk add --no-cache gcc musl-dev RUN apk add --no-cache g++
- 生成本地镜像
docker build -t myalpine .
- 运行映像,把当前路径($PWD)映射至容器的 /test 目录,用 gcc 编译程序,exit返回:
docker run -it -v$PWD:/test myalpine root@b1a38bd7107a:/# cd test root@b1a38bd7107a:/test# gcc -o hello hello.c Hello World root@b1a38bd7107a:/test# exit exit
C 语法
C 语言规定了源代码中每个词汇、语句的含义,也规定了它们该如何组织在一起,这就是语法(Syntax)。您已经在 开始
章节中看到以下代码。 让我们分解它以更好地理解它:
#include <stdio.h>
int main() {
printf("Hello World!");
return 0;
}
示例说明
第 1 行:#include <stdio.h>
是一个头文件库,可让我们使用输入和输出函数,例如 printf()
(在第 4 行中使用)。头文件向 C++
程序添加功能。
第 2 行:空行。 C
忽略空格。 但是我们使用它来使代码更具可读性。
第 3 行:另一个总是出现在 C
程序中的东西是 main()
。 这称为函数。 其大括号 {}
内的任何代码都将被执行。
第 4 行:printf()
是用于将文本输出/打印到屏幕的函数。 在示例中,它将输出 Hello World
。
第 5 行:return 0
结束 main()
函数。
第 6 行:不要忘记添加右大括号 }
以实际结束 main
函数。
C 输出(打印文本)
printf()
函数用于输出值/打印文本
格式控制符
格式控制符 | 说明 |
---|---|
%c |
输出一个单一的字符 |
%hd %d %ld |
以十进制、有符号的形式输出 short、int、long 类型的整数 |
%hu %u %lu |
以十进制、无符号的形式输出 short、int、long 类型的整数 |
%ho %o %lo |
以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
%#ho %#o %#lo |
以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
%hx %x %lx <br> %hX %X %lX |
以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
%#hx %#x %#lx <br>%#hX %#X %#lX |
以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
%f %lf |
以十进制的形式输出 float 、double 类型的小数 |
%e %le %E %lE |
以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
%g %lg %G %lG |
以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
%s |
输出一个字符串 |
#include <stdio.h>
int main() {
printf("Hello World!");
return 0;
}
您可以根据需要添加任意数量的 printf()
函数。 但是,请注意,它不会在输出末尾插入新行:
#include <stdio.h>
int main() {
printf("Hello World!");
printf("I am learning C.");
return 0;
}
%d
输出整数
#include <stdio.h>
int main()
{
int testInteger = 5;
printf("Number = %d", testInteger);
return 0;
}
输出 Number = 5
%f
输出浮点型数据
#include <stdio.h>
int main()
{
float f;
printf("Enter a number: ");
// %f 匹配浮点型数据
scanf("%f", &f);
printf("Value = %f", f);
return 0;
}
short/int/long
short | int | long | |
---|---|---|---|
8 进制 | %ho |
%o |
%lo |
10 进制 | %hd |
%d |
%ld |
16 进制 | %hx 或者 %hX |
%x 或者 %X |
%lx 或者 %lX |
short a = 0b1010110; // 2 进制数字
int b = 02713; // 8 进制数字
long c = 0X1DAB83; // 16 进制数字
printf("a=%ho, b=%o, c=%lo\n", a, b, c); // 以 8 进制形似输出
// a=126, b=2713, c=7325603
printf("a=%hd, b=%d, c=%ld\n", a, b, c); // 以 10 进制形式输出
// a=86, b=1483, c=1944451
printf("a=%hx, b=%x, c=%lx\n", a, b, c); // 以 16 进制形式输出(字母小写)
// a=56, b=5cb, c=1dab83
printf("a=%hX, b=%X, c=%lX\n", a, b, c); // 以 16 进制形式输出(字母大写)
// a=56, b=5CB, c=1DAB83
控制空格数
#include <stdio.h>
int main()
{
int a1=20, a2=345, a3=700, a4=22;
int b1=56720, b2=9999, b3=20098, b4=2;
int c1=233, c2=205, c3=1, c4=6666;
int d1=34, d2=0, d3=23, d4=23006783;
printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
return 0;
}
输出结果:
20 345 700 22
56720 9999 20098 2
233 205 1 6666
34 0 23 23006783
%-9d
中,d
表示以 10 进制输出,9
表示最少占 9
个字符的宽度,宽度不足以空格补齐,-
表示左对齐。综合起来,%-9d
表示以 10 进制输出,左对齐,宽度最小为 9
个字符。大家可以亲自试试 %9d
的输出效果。
标志字符
-
表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐。+
用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号。空格
用于整数或者小数,输出值为正时冠以空格,为负时冠以负号。#
对于八进制(%o
)和十六进制(%x
/%X
)整数,#
表示在输出时添加前缀;八进制的前缀是0
,十六进制的前缀是0x
/0X
。对于小数(%f
/%e
/%g
),#
表示强迫输出小数点。如果没有小数部分,默认是不输出小数点的,加上#
以后,即使没有小数部分也会带上小数点。
#include <stdio.h>
int main(){
int m = 192, n = -943;
float f = 84.342;
printf("m=%10d, m=%-10d\n", m, m); // 演示 - 的用法
printf("m=%+d, n=%+d\n", m, n); // 演示 + 的用法
printf("m=% d, n=% d\n", m, n); // 演示空格的用法
printf("f=%.0f, f=%#.0f\n", f, f); // 演示 # 的用法
return 0;
}
运行结果:
m= 192, m=192 # 当以%10d输出 m 时,是右对齐,
# 所以在 192 前面补七个空格;
# 当以%-10d输出 m 时,是左对齐,所以在 192 后面补七个空格。
m=+192, n=-943 # m 是正数,以 %+d 输出时要带上正号;n 是负数,以%+d输出时要带上负号。
m= 192, n=-943 # m 是正数,以 % d 输出时要在前面加空格;n 是负数,以% d输出时要在前面加负号。
f=84, f=84. # %.0f表示保留 0 位小数,也就是只输出整数部分,不输出小数部分。
# 默认情况下,这种输出形式是不带小数点的,但是如果有了#标志,
# 那么就要在整数的后面“硬加上”一个小数点,以和纯整数区分开。
格式控制符
%[flag][width][.precision]type
[ ]
表示此处的内容可有可无,是可以省略的。type
(必须有)表示输出类型,比如%d
、%f
、%c
、%lf
,type
就分别对应d
、f
、c
、lf
;再如,%-9d
中type
对应d
。width
表示最小输出宽度,也就是至少占用几个字符的位置;例如,%-9d
中width
对应9
,表示输出结果最少占用9
个字符的宽度。
#include <stdio.h>
int main(){
int n = 234;
float f = 9.8;
char c = '@';
char *str = "https://github.com/jaywcjlove/c-tutorial";
printf("%10d%12f%4c%8s", n, f, c, str);
return 0;
}
运行结果:
234 9.800000 @https://github.com/jaywcjlove/c-tutorial
对输出结果的说明:
n
的指定输出宽度为 10,234 的宽度为 3,所以前边要补上 7 个空格。f
的指定输出宽度为 12,9.800000 的宽度为 8,所以前边要补上 4 个空格。str
的指定输出宽度为 8,"https://github.com/jaywcjlove/c-tutorial" 的宽度为40
,超过了 8,所以指定输出宽度不再起作用,而是按照str
的实际宽度输出。
输出精度/宽度
#include <stdio.h>
int main(){
int n = 123456;
double f = 882.923672;
char *str = "abcdefghi";
printf("n: %.9d %.4d\n", n, n);
printf("f: %.2lf %.4lf %.10lf\n", f, f, f);
printf("str: %.5s %.15s\n", str, str);
return 0;
}
运行结果:
n: 000123456 123456
f: 882.92 882.9237 882.9236720000
str: abcde abcdefghi
对输出结果的说明:
- 对于
n
,.precision
表示最小输出宽度。n 本身的宽度为 6,当precision
为 9 时,大于 6,要在 n 的前面补 3 个 0;当precision
为 4 时,小于 6,不再起作用。 - 对于
f
,.precision
表示输出精度。f 的小数部分有 6 位数字,当precision
为 2 或者 4 时,都小于 6,要按照四舍五入的原则截断小数;当 precision 为 10 时,大于 6,要在小数的后面补四个 0。 - 对于
str
,.precision
表示最大输出宽度。str
本身的宽度为 9,当precision
为 5 时,小于 9,要截取 str 的前 5 个字符;当 precision 为 15 时,大于 9,不再起作用。
C 注释 Comments
注释可用于解释代码,并使其更具可读性。它还可用于在测试替代代码时阻止执行。
注释可以是单行或多行的。
单行注释
单行注释以两个正斜杠 (//
) 开头。
//
和行尾之间的任何文本都会被编译器忽略(不会被执行)。
此示例在一行代码之前使用单行注释:
// 这是一个注释
printf("Hello World!");
此示例在代码行末尾使用单行注释:
printf("Hello World!"); // 这是一个注释
多行注释
多行注释以 /*
开头,以 */
结尾。
/*
和 */
之间的任何文本都将被编译器忽略:
/* 下面的代码将打印出 Hello World!
到屏幕上,真是太棒了 */
printf("Hello World!");
单行注释还是多行注释?
您要使用哪个取决于您。 通常,我们使用 //
表示简短的注释,使用 /*
*/
表示较长的注释。
很高兴知道:在 C99 版本(1999 年发布)之前,您只能在 C
中使用多行注释。
C 变量
变量是存储数据值的容器。
在 C 中,有不同类型的变量(用不同的关键字定义),例如:
int
– 存储整数(整数),不带小数,例如 123 或 -123float
– 存储浮点数,带小数,例如 19.99 或 -19.99char
– 存储单个字符,例如“a”或“B”。 字符值用单引号括起来
声明(创建)变量
要创建变量,请指定类型并为其赋值:
语法 Syntax
type variableName = value;
其中 type
是 C 类型之一(例如 int
),variableName
是变量的名称(例如 x 或 myName)。 等号用于为变量赋值
。
因此,要创建一个应该存储数字的变量,请查看以下示例:
示例 1
创建一个名为 myNum
的 int
类型变量并将值 15
分配给它:
int myNum = 15;
示例 2
你也可以声明一个变量而不赋值,然后再赋值:
int myNum;
myNum = 15;
示例 3
注意:如果您为现有变量分配新值,它将覆盖以前的值:
int myNum = 15; // myNum 值为 15
myNum = 10; // 现在 myNum 值为 10
输出变量
您从 输出章节 中了解到,您可以使用 printf()
函数输出值/打印文本:
printf("Hello World!");
在许多其他编程语言(如 Python、Java 和 C++)中,您通常会使用 print函数 显示变量的值。 但是,这在 C 中是不能使用的:
int myNum = 15;
printf(myNum); // 没发生什么事
<!–rehype:style= background: #ff000036;–>
要在 C 中输出变量,您必须熟悉所谓的“格式说明符”。
格式说明符
格式说明符与 printf()
函数一起使用,以告诉编译器变量存储什么类型的数据。 它基本上是变量值的占位符。
格式说明符以百分号 %
开头,后跟一个字符。
例如,要输出 int
变量的值,您必须在 printf()
函数中使用用双引号括起来的格式说明符 %d
或 %i
:
int myNum = 15;
printf("%d", myNum); // 输出 15
要打印其他类型,请使用 %c
表示 char
和 %f
表示 float
:
// 创建变量
int myNum = 5; // 整数 Integer (whole number)
float myFloatNum = 5.99; // 浮点数 Floating point number
char myLetter = 'D'; // 字符串 Character
// 打印变量
printf("%d\n", myNum);
printf("%f\n", myFloatNum);
printf("%c\n", myLetter);
要组合文本和变量,请在 printf()
函数中用逗号分隔它们:
int myNum = 6;
printf("我最喜欢的号码是: %d", myNum);
要在单个 printf()
函数中打印不同的类型,可以使用以下命令:
int myNum = 5;
char myLetter = 'D';
printf("My number is %d and my letter is %c", myNum, myLetter);
您将了解有关 下一章中的数据类型 的更多信息。
添加变量相加
要将变量添加到另一个变量,可以使用 +
运算符:
int x = 5;
int y = 6;
int sum = x + y;
printf("%d", sum);
声明多个变量
要声明多个相同类型的变量,请使用逗号分隔列表:
int x = 5, y = 6, z = 50;
printf("%d", x + y + z);
您还可以将相同的值分配给相同类型的多个变量:
int x, y, z;
x = y = z = 50;
printf("%d", x + y + z);
C 变量名称
所有 C 变量必须用唯一名称 标识。
这些唯一名称称为标识符。
标识符可以是短名称(如 x 和 y)或更具描述性的名称(age、sum、totalVolume)。
注意: 建议使用描述性名称以创建可理解和可维护的代码:
// 👍 Good
int minutesPerHour = 60;
// 👎 正确的,但并不那么容易理解 `m` 实际上是什么
int m = 60;
命名变量的 一般规则 是:
- 名称可以包含字母、数字和下划线
- 名称必须以字母或下划线 (_) 开头
- 名称区分大小写(
myVar
和myvar
是不同的变量) - 名称不能包含空格或特殊字符,如
!
、#
、%
等。 - 保留字(如
int
)不能用作名称
C 数据类型 Data Types
数据类型
如变量章节中所述,C 中的变量必须是指定的数据类型,并且必须在 printf()
函数中使用格式说明符才能显示 它:
// 创建变量
int myNum = 5; // 整数
float myFloatNum = 5.99; // 浮点数
char myLetter = 'D'; // 字符串
// 打印输出变量
printf("%d\n", myNum);
printf("%f\n", myFloatNum);
printf("%c\n", myLetter);
基本数据类型
数据类型指定变量将存储的信息的大小和类型。
让我们看看基本的数据类型。它的大小是根据 32 位体系结构给出的:
数据类型 | 大小 Size | 范围 Range | 描述 Description |
---|---|---|---|
char |
1 字节 | −128 ~ 127 |
单个字符/字母/数字/ASCII |
signed char | 1 字节 | −128 ~ 127 |
– |
unsigned char | 1 字节 | 0 ~ 255 |
– |
int |
2 到 4 字节 |
−32,768 ~ 32,767 |
存储整数 |
signed int | 2 字节 | −32,768 ~ 32,767 |
|
unsigned int | 2 字节 | 0 ~ 65,535 |
|
short int | 2 字节 | −32,768 ~ 32,767 |
|
signed short int | 2 字节 | −32,768 ~ 32,767 |
|
unsigned short int | 2 字节 | 0 ~ 65,535 |
|
long int | 4 字节 | -2,147,483,648 ~ 2,147,483,647 |
|
signed long int | 4 字节 | -2,147,483,648 ~ 2,147,483,647 |
|
unsigned long int | 4 字节 | 0 ~ 4,294,967,295 |
|
float |
4 字节 | ||
double |
8 字节 | ||
long double | 10 字节 |
说 明 | 字符型 | 短整型 | 整型 | 长整型 | 单精度浮点型 | 双精度浮点型 | 无类型 |
---|---|---|---|---|---|---|---|
数据类型 | char | short | int | long | float | double | void |
基本格式说明符
每种数据类型都有不同的格式说明符。 这里是其中的一些:
格式说明符 | 数据类型 |
---|---|
%d 或 %i |
int 整数 |
%f |
float 单精度的十进制类型 |
%lf |
double 高精度浮点数据或数字 |
%c |
char 字符 |
%s |
用于 strings,您可以在 strings 中了解更多信息 |
short | int | long | |
---|---|---|---|
8 进制 | %ho |
%o |
%lo |
10 进制 | %hd |
%d |
%ld |
16 进制 | %hx 或者 %hX |
%x 或者 %X |
%lx 或者 %lX |
int 整数
// 创建 整数 变量
int myNum = 5;
// 打印输出变量
printf("%d\n", myNum);
float 单精度的十进制类型
float myFloatNum = 5.99; // 浮点数
// 打印输出变量
printf("%f\n", myFloatNum);
char 字符
char myLetter = 'D'; // 字符串
// 打印输出变量
printf("%c\n", myLetter);
double 高精度浮点数据或数字
范围是 1.7E-308
到 1.7E+308
。双精度数据可以用实数 (1
到 10
)、小数 (0.1
到 11.002
) 和负数 (-1 到 -0.00002) 表示。它可以容纳大约 15 到 16 位小数点前后的数字。
double myDouble = 3.2325467;
// 打印输出变量
printf("%lf\n", myDouble);
C 常量 Constants
常量
当您不希望其他人(或您自己)覆盖现有变量值时,请使用 const
关键字(这会将变量声明为常量
,这意味着不可更改且只读):
const int myNum = 15; // myNum 将始终为 15
myNum = 10; // error: assignment of read-only variable 'myNum'
<!–rehype:style= background: #ff000036;–>
当您具有不太可能更改的值时,您应该始终将变量声明为常量:
const int minutesPerHour = 60;
const float PI = 3.14;
常量笔记
当你声明一个常量变量时,它必须被赋值:
像这样:
const int minutesPerHour = 60;
然而,下面示例将不起作用报错:
const int minutesPerHour;
minutesPerHour = 60; // error
<!–rehype:style= background: #ff000036;–>
最佳实践
关于常量变量的另一件事是,用 大写
声明它们被认为是一种好习惯。 它不是必需的,但对代码可读性很有用,并且对于 C 程序员来说很常见:
const int BIRTHYEAR = 1980;
C 运算符 Operators
运算符
运算符用于对变量和值执行操作。
在下面的示例中,我们使用 +
运算符 将两个值相加:
int myNum = 100 + 50;
尽管 +
运算符经常用于将两个值相加,就像上面的示例一样,它也可以用于将一个变量和一个值相加,或者一个变量和另一个变量相加:
int sum1 = 100 + 50; // 150 (100 + 50)
int sum2 = sum1 + 250; // 400 (150 + 250)
int sum3 = sum2 + sum2; // 800 (400 + 400)
C 将运算符分为以下几组:
- 算术运算符 (Arithmetic operators)
- 赋值运算符 (Assignment operators)
- 比较运算符 (Comparison operators)
- 逻辑运算符 (Logical operators)
- 位运算符 (Bitwise operators)
算术运算符
算术运算符用于执行常见的数学运算。
Operator | Name | Description | Example |
---|---|---|---|
+ | 加 Addition | 将两个值相加 | x + y |
– | 减 Subtraction | 从另一个值中减去一个值 | x – y |
* | 乘 Multiplication | 将两个值相乘 | x * y |
/ | 除 Division | 将一个值除以另一个 | x / y |
% | 取模 Modulus | 返回除法余数 | x % y |
++ | 增量 Increment | 将变量的值增加 1 | ++x |
— | 乘量 Decrement | 将变量的值减 1 | –x |
赋值运算符
赋值运算符用于为变量赋值。
在下面的示例中,我们使用 赋值 运算符 (=
) 将值 10 分配给名为 x 的变量:
int x = 10;
加法赋值运算符 (+=
) 将值添加到变量:
int x = 10;
x += 5;
所有赋值运算符的列表:
符号 | 示例 | 如同 |
---|---|---|
= | x = 5 | x = 5 |
+= | x += 3 | x = x + 3 |
-= | x -= 3 | x = x – 3 |
*= | x *= 3 | x = x * 3 |
/= | x /= 3 | x = x / 3 |
%= | x %= 3 | x = x % 3 |
&= | x &= 3 | x = x & 3 |
|= | x |= 3 | x = x | 3 |
^= | x ^= 3 | x = x ^ 3 |
>>= | x >>= 3 | x = x >> 3 |
<<= | x <<= 3 | x = x << 3 |
比较运算符
比较运算符用于比较两个值。
注意: 比较的返回值为 true (1
) 或 false (0
)。
在以下示例中,我们使用 大于 运算符 (>
) 来确定 5 是否大于 3:
int x = 5;
int y = 3;
printf("%d", x > y); // 返回 1(真),因为 5 大于 3
所有比较运算符的列表:
符号 | 名称 | 示例 |
---|---|---|
== | 等于 | x == y |
!= | 不等于 | x != y |
> | 大于 | x > y |
< | 小于 | x < y |
>= | 大于或等于 | x >= y |
<= | 小于或等于 | x <= y |
逻辑运算符
逻辑运算符用于确定变量或值之间的逻辑:
符号 | 名称 | 说明 | 示例 |
---|---|---|---|
&& | 与逻辑 | 如果两个语句都为真,则返回真 | x < 5 && x < 10 |
|| | 或逻辑 | 如果其中一个语句为真,则返回真 | x < 5 || x < 4 |
! | 非逻辑 | 反转结果,如果结果为真则返回假 | !(x < 5 && x < 10) |
位运算符
运算符 | 描述 | 实例 |
---|---|---|
& |
按位与操作,按二进制位进行"与"运算 | (A & B) 将得到 12 即为 0000 1100 |
| |
按位或运算符,按二进制位进行"或"运算 | (A | B) 将得到 61 即为 0011 1101 |
^ |
异或运算符,按二进制位进行"异或"运算 | (A ^ B) 将得到 49 即为 0011 0001 |
~ |
取反运算符,按二进制位进行"取反"运算 | (~A) 将得到 -61 即为 1100 0011 |
<< |
二进制左移运算符 | A << 2 将得到 240 即为 1111 0000 |
>> |
二进制右移运算符 | A >> 2 将得到 15 即为 0000 1111 |
下面的实例,了解 C 语言中所有可用的位运算符
#include <stdio.h>
int main()
{
unsigned int a = 60; /* 60 = 0011 1100 */
unsigned int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
printf("Line 1 - c 的值是 %d\n", c );
c = a | b; /* 61 = 0011 1101 */
printf("Line 2 - c 的值是 %d\n", c );
c = a ^ b; /* 49 = 0011 0001 */
printf("Line 3 - c 的值是 %d\n", c );
c = ~a; /*-61 = 1100 0011 */
printf("Line 4 - c 的值是 %d\n", c );
c = a << 2; /* 240 = 1111 0000 */
printf("Line 5 - c 的值是 %d\n", c );
c = a >> 2; /* 15 = 0000 1111 */
printf("Line 6 - c 的值是 %d\n", c );
}
当上面的代码被编译和执行时,它会产生下列结果:
Line 1 - c 的值是 12
Line 2 - c 的值是 61
Line 3 - c 的值是 49
Line 4 - c 的值是 -61
Line 5 - c 的值是 240
Line 6 - c 的值是 15
sizeof 运算符
可以使用 sizeof
运算符找到数据类型或变量的内存大小(以字节为单位):
int myInt;
float myFloat;
double myDouble;
char myChar;
printf("%lu\n", sizeof(myInt));
printf("%lu\n", sizeof(myFloat));
printf("%lu\n", sizeof(myDouble));
printf("%lu\n", sizeof(myChar));
C If … Else
条件和 If 语句
您从 运算符章节 中了解到,C 支持数学中的常见逻辑条件:
- 小于:a < b
- 小于或等于:a <= b
- 大于:a > b
- 大于等于:a >= b
- 等于 a == b
- 不等于:a != b
您可以使用这些条件为不同的决策执行不同的操作。
C 有以下条件语句:
- 使用
if
指定要执行的代码块,如果指定条件为真 - 使用
else
指定要执行的代码块,如果相同的条件为假 - 如果第一个条件为假,则使用
else if
指定要测试的新条件 - 使用
switch
指定要执行的许多替代代码块
if 语句
使用 if
语句指定在条件为 true
时要执行的 C 代码块。
语法
if (条件) {
// 条件为真(true)时执行的代码块
}
其执行过程可表示为下图:
┆
╭┈┈┈┈┈▼┈┈┈┈┈╮
┆ condition ├┈┈┈╮
╰┈┈┈┈┈┈┈┈┈┈┈╯ ┆
┆ true ┆ false
▼ ┆
╭┈┈┈┈┴┈┈┈┈╮ ┆
┆ 语句块 ┆ ┆
╰┈┈┈┈┬┈┈┈┈╯ ┆
├┈◀┈┈┈┈┈┈┈╯
▼
请注意,if
是小写字母。 大写字母(If 或 IF)会产生错误。
在下面的示例中,我们测试两个值来确定 20 是否大于 18。如果条件为 true
,则打印一些文本:
if (20 > 18) {
printf("20 大于 18");
}
我们还可以测试变量:
int x = 20;
int y = 18;
if (x > y) {
printf("x 大于 y");
}
示例说明
在上面的示例中,我们使用两个变量 x 和 y 来测试 x 是否大于 y(使用 >
运算符)。 由于 x 为 20,y 为 18,并且我们知道 20 大于 18,我们在屏幕上打印 “x 大于 y”。
else 语句
使用 else
语句指定在条件(condition) 为 false
时要执行的代码块。
语法
if (条件) {
// 条件为真(true)时执行的代码块
} else {
// 条件为假(false)时执行的代码块
}
其执行过程可表示为下图:
┆
▼
true ╭┈┈┈┈┈┴┈┈┈┈┈╮ false
╭┈┈┈┈┈┈┈┤ condition ├┈┈┈┈┈┈┈╮
▼ ╰┈┈┈┈┈┈┈┈┈┈┈╯ ▼
╭┈┈┈┈┴┈┈┈┈┈╮ ╭┈┈┈┈┴┈┈┈┈┈╮
┆ 语句块1 ┆ ┆ 语句块2 ┆
╰┈┈┈┈┬┈┈┈┈┈╯ ╰┈┈┈┈┬┈┈┈┈┈╯
╰┈┈┈┈┈┈┈┈┈┈┈┈┈┬┈┈┈┈┈┈┈┈┈┈┈┈┈╯
▼
int time = 20;
if (time < 18) {
printf("再会!");
} else {
printf("晚上好!");
}
// 输出 -> "晚上好!"
示例说明
在上面的示例中,时间 (20) 大于 18,因此条件为 false
。 因此,我们转到 else
条件并打印到屏幕“晚安”。 如果时间小于 18,程序将打印 “晚上好!”。
else if 语句
如果第一个条件为 false
,则使用 else if
语句指定新条件。
语法
if (条件1) {
// 如果 条件 1 为真(true),则要执行的代码块
} else if (条件2) {
// 如果 条件 1 为假(false)且 条件 2 为真(true),则要执行的代码块
} else {
// 如果 条件 1 为假(false)且 条件 2 为假(false),则要执行的代码块
}
int time = 22;
if (time < 10) {
printf("早上好!");
} else if (time < 20) {
printf("再会!");
} else {
printf("晚上好!");
}
// 输出 -> "晚上好!"
示例说明
在上面的示例中,时间 (22) 大于 10,因此第一个条件为 false
。 else if
语句中的下一个条件也是 false
,所以我们继续讨论 else
条件,因为 条件1 和 条件2 都是 false
– 并打印到 屏幕“晚上好”。
但是,如果时间是 14,我们的程序将打印“再会!”。
另一个例子
这个例子展示了如何使用 if..else if
来判断一个数字是正数还是负数:
int myNum = 10; // Is this a positive or negative number?
if (myNum > 0)
printf("该值为正数。");
else if (myNum < 0)
printf("该值为负数。");
else
printf("值为 0。");
三元运算符
还有一个简写 if else
,它被称为三元运算符,因为它由三个操作数组成。它可用于用单行替换多行代码。它通常用于替换简单的 if else
语句:
变量 = (条件) ? 表达式真(true) : 表达式假(false);
原始示例:
int time = 20;
if (time < 18) {
printf("再会!");
} else {
printf("晚上好!");
}
简化示例:
int time = 20;
(time < 18) ? printf("再会!") : printf("晚上好!");
如果您想使用传统的 if...else
语句或三元运算符,这完全取决于您。
C Switch
Switch 语句
您可以使用 switch
语句,而不是编写 许多 if..else
语句。
switch
语句选择要执行的许多代码块之一:
语法
switch(表达式) {
case 整型数值1:
// 代码块 1
break;
case 整型数值2:
// 代码块 2
break;
default:
// 代码块 3
}
这是它的工作原理:
switch
表达式被计算一次- 将表达式的值与每个
case
的值进行比较 - 如果匹配,则执行关联的代码块
break
语句跳出 switch 块并停止执行default
语句是可选的,指定在没有大小写匹配时运行的一些代码
下面的示例使用工作日编号来计算工作日名称:
int day = 4;
switch (day) {
case 1:
printf("周一");
break;
case 2:
printf("周二");
break;
case 3:
printf("周三");
break;
case 4:
printf("周四");
break;
case 5:
printf("周五");
break;
case 6:
printf("周六");
break;
case 7:
printf("周日");
break;
}
// 输出 -> "周四" (day 4)
中断关键字 break
当 C 到达一个 break
关键字时,它会跳出 switch
块。
这将停止在块内执行更多代码和案例测试。
找到匹配项并完成工作后,就该休息一下了。 无需进行更多测试。
中断可以节省大量执行时间,因为它“忽略”了 switch 块中所有其余代码的执行。
默认关键字 default
default
关键字指定在没有大小写匹配时运行的一些代码:
int day = 4;
switch (day) {
case 6:
printf("今天是星期六");
break;
case 7:
printf("今天是星期日");
break;
default:
printf("期待周末");
}
// 输出 -> "期待周末"
注意: switch
中的最后一条语句必须使用 default
关键字,并且不需要 break
。default
不是必须的。当没有 default
时,如果所有 case
都匹配失败,那么就什么都不执行。
关键字 case
case 10: printf("..."); break; // ✅ 💯 正确
case 8+9: printf("..."); break; // ✅ 💯 正确
case 'A': printf("..."); break; // ✅ 💯 正确,字符和整数可以相互转换
case 'A'+19: printf("..."); break; // ✅ 💯 正确,字符和整数可以相互转换
case 9.5: printf("..."); break; // ❌ 错误,不能为小数
case a: printf("..."); break; // ❌ 错误,不能包含变量
case a+10: printf("..."); break; // ❌ 错误,不能包含变量
C While 循环
循环
只要达到指定的条件,循环就可以执行一段代码。
循环很方便,因为它们可以节省时间、减少错误并且使代码更具可读性。
While 循环
只要指定条件为 true
,while
循环就会遍历代码块:
语法
while (条件) {
// 要执行的代码块
}
在下面的示例中,只要变量 (i
) 小于 5
,循环中的代码就会一遍又一遍地运行:
int i = 0;
while (i < 5) {
printf("%d\n", i);
i++;
}
注意: 不要忘记增加条件中使用的变量(i++
),否则循环永远不会结束,成为“死循环”!
Do/While 循环
do/while
循环是 while
循环的变体。 该循环将执行一次代码块,在检查条件是否为真之前,只要条件为真,它将重复循环。
语法
do {
// 要执行的代码块
} while (条件);
下面的示例使用 do/while
循环。 循环将始终至少执行一次,即使条件为假,因为代码块在条件测试之前执行:
int i = 0;
do {
printf("%d\n", i);
i++;
} while (i < 5);
注意: 不要忘记增加条件中使用的变量,否则循环永远不会结束,成为“死循环”!
另外一个示例
#include <stdio.h>
int main(){
int n=0;
printf("Input a string:");
while(getchar()!='\n') n++;
printf("字符数: %d\n", n);
return 0;
}
运行结果:
输入一个字符串:c tutorial ↙
字符数: 15
程序中的循环条件为 getchar()!='\n'
,其意义是,只要从键盘输入的字符不是回车就继续循环。循环体 n++;
完成对输入字符个数计数。
C For 循环
For 循环
当您确切知道要循环一段代码的次数时,请使用 for
循环而不是 while
循环:
语法
for (表达式 1; 表达式 2; 表达式 3) {
// 要执行的代码块
}
表达式 1
在代码块执行之前执行(一次)。
表达式 2
定义了执行代码块的条件。
表达式 3
在代码块执行后(每次)执行。
下面的示例将打印数字 0 到 4:
示例
int i;
for (i = 0; i < 5; i++) {
printf("%d\n", i);
}
示例说明
表达式 1
: 在循环开始之前设置一个变量(int i = 0)。
表达式 2
: 定义循环运行的条件(i 必须小于 5)。 如果条件为真,循环将重新开始,如果条件为假,循环将结束。
表达式 3
: 每次执行循环中的代码块时增加一个值 (i++)。
另一个例子
此示例将仅打印 0 到 10 之间的偶数值:
for (i = 0; i <= 10; i = i + 2) {
printf("%d\n", i);
}
C 跳出循环 Break/Continue
Break
您已经看过本教程前面章节中使用的 break
语句。 它用于“跳出” switch
语句。
break
语句也可用于跳出循环。
这个例子在 i
等于 4 时跳出循环:
int i;
for (i = 0; i < 10; i++) {
if (i == 4) {
break;
}
printf("%d\n", i);
}
Continue
如果指定条件发生,continue
语句会中断一次迭代(在循环中),并继续循环中的下一次迭代。
此示例跳过 4 的值:
int i;
for (i = 0; i < 10; i++) {
if (i == 4) {
continue;
}
printf("%d\n", i);
}
在 While 循环中中断并继续
您还可以在 while
循环中使用 break
和 continue
:
Break 示例
int i = 0;
while (i < 10) {
if (i == 4) {
break;
}
printf("%d\n", i);
i++;
}
Continue 示例
int i = 0;
while (i < 10) {
i++;
if (i == 4) {
continue;
}
printf("%d\n", i);
}
break 示例
#include <stdio.h>
int main(){
int i=1, sum=0;
while(1){ // 循环条件为死循环
sum+=i;
i++;
if(i>100) break;
}
printf("%d\n", sum);
return 0;
}
// 运行结果:5050
while
循环条件为 1
,是一个死循环。当执行到第 100
次循环的时候,计算完 i++;
后 i
的值为 101
,此时 if
语句的条件 i> 100
成立,执行 break;
语句,结束循环
#include <stdio.h>
int main(){
int i=1, j;
while(1){ // 外层循环
j=1;
while(1){ // 内层循环
printf("%-4d", i*j);
j++;
if(j>4) break; // 跳出内层循环
}
printf("\n");
i++;
if(i>4) break; // 跳出外层循环
}
return 0;
}
// 运行结果:
// 1 2 3 4
// 2 4 6 8
// 3 6 9 12
// 4 8 12 16
当 j>4
成立时,执行 break;
,跳出内层循环;外层循环依然执行,直到 i>4
成立,跳出外层循环。内层循环共执行了 4
次,外层循环共执行了 1
次。
continue 示例
语句的作用是跳过循环体中剩余的语句而强制进入下一次循环。continue
语句只用在 while
、for
循环中,常与 if
条件语句一起使用,判断条件是否成立。
#include <stdio.h>
int main(){
char c = 0;
while(c!='\n'){ // 回车键结束循环
c=getchar();
if(c=='4' || c=='5'){ // 按下的是数字键 4 或 5
continue; // 跳过当次循环,进入下次循环
}
putchar(c);
}
return 0;
}
// 运行结果:
// 0123456789↙
// 01236789
程序遇到 while
时,变量 c
的值为 '\0'
,循环条件 c!='\n'
成立,开始第一次循环。getchar()
使程序暂停执行,等待用户输入,直到用户按下回车键才开始读取字符。
本例我们输入的是 0123456789
,当读取到 4
或 5
时,if
的条件 c=='4'||c=='5'
成立,就执行 continue
语句,结束当前循环,直接进入下一次循环,也就是说 putchar(c);
不会被执行到。而读取到其他数字时,if
的条件不成立,continue
语句不会被执行到,putchar(c);
就会输出读取到的字符。
break
与 continue
的对比:break
用来结束所有循环,循环语句不再有执行的机会;continue
用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。
C 数组 Arrays
数组用于在单个变量中存储多个值,而不是为每个值声明单独的变量。
要创建一个数组,请定义数据类型(如int
)并指定数组的名称,后跟方括号[]
。
要向其中插入值,请在花括号 {}
内使用逗号 ,
分隔的列表:
int myNumbers[] = {25, 50, 75, 100};
我们现在创建了一个包含四个整数的数组的变量。
访问数组的元素
要访问数组元素,请参考其索引号。
数组索引以 0 开头:[0]
是第一个元素。[1]
是第二个元素,依此类推。
此语句访问 myNumbers
中的第一个元素 [0] 的值:
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]);
// 输出 25
更改数组元素
要更改特定元素的值,请参阅索引号:
myNumbers[0] = 33;
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;
printf("%d", myNumbers[0]);
// 现在输出 33 而不是 25
循环遍历数组
您可以使用 for
循环遍历数组元素。
以下示例输出 myNumbers
数组中的所有元素:
int myNumbers[] = {25, 50, 75, 100};
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", myNumbers[i]);
}
设置数组大小
另一种创建数组的常用方法是指定数组的大小,然后再添加元素:
// 声明一个由四个整数组成的数组:
int myNumbers[4];
// 添加元素
myNumbers[0] = 25;
myNumbers[1] = 50;
myNumbers[2] = 75;
myNumbers[3] = 100;
使用这种方法,**你必须知道数组的大小,**才能让程序存储足够的内存。
您无法在创建后更改数组的大小。
C 枚举 Enum
C 中的枚举也称为枚举类型。 它是一种用户定义的数据类型,由整数值组成,并为这些值提供有意义的名称。 在 C 中使用 enum
使程序易于理解和维护。 枚举是使用 enum 关键字定义的。
以下是在 C 中定义枚举的方式:
enum flag { integer_const1, integer_const2, .....integter_constN };
enum
是一个新的关键字,专门用来定义枚举类型,这也是它在 C 语言中的唯一用途;flag
是枚举类型的名字;integer_const1, integer_const2, integer_const3, …… 是每个值对应的名字的列表。注意最后的 ;
不能少。
创建枚举类型
列出一个星期有几天
enum week { Mon, Tues, Wed, Thurs, Fri, Sat, Sun };
可以看到,我们仅仅给出了名字,却没有给出名字对应的值,这是因为枚举值默认从 0
开始,往后逐个加 1
(递增);也就是说,week
中的 Mon、Tues …… Sun 对应的值分别为 0、1 … 6。
我们也可以给每个名字都指定一个值:
enum week{ Mon = 1, Tues = 2, Wed = 3, Thurs = 4, Fri = 5, Sat = 6, Sun = 7 };
上面示例可以简化一下,只给第一个名字指定值,这样枚举值就从 1 开始递增,跟上面的写法是等效的:
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
定义枚举变量
enum week a, b, c;
定义枚举类型的同时定义变量
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;
有了枚举变量,就可以把列表中的值赋给它
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
enum week a = Mon, b = Wed, c = Sat;
// 或者
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a = Mon, b = Wed, c = Sat;
示例
下面是一个简单的枚举示例应用,判断用户输入的是星期几:
#include <stdio.h>
int main(){
enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day;
scanf("%d", &day);
switch(day){
case Mon: puts("Monday"); break;
case Tues: puts("Tuesday"); break;
case Wed: puts("Wednesday"); break;
case Thurs: puts("Thursday"); break;
case Fri: puts("Friday"); break;
case Sat: puts("Saturday"); break;
case Sun: puts("Sunday"); break;
default: puts("Error!");
}
return 0;
}
- 枚举列表中的 Mon、Tues、Wed 这些标识符的作用范围是全局的(严格来说是 main() 函数内部),不能再定义与它们名字相同的变量。
- Mon、Tues、Wed 等都是常量,不能对它们赋值,只能将它们的值赋给其他的变量。
C 字符串 Strings
字符串用于存储文本/字符。
例如,“Hello World”是一个字符串。
与许多其他编程语言不同,C 没有 String 类型 来轻松创建字符串变量。但是,您可以使用 char
类型并创建一个字符 array 以在 C 中创建一个字符串:
char greetings[] = "Hello World!";
请注意,您必须使用双引号。
要输出字符串,您可以使用 printf()
函数和格式说明符 %s
来告诉 C 我们现在正在处理字符串:
char greetings[] = "Hello World!";
printf("%s", greetings);
访问字符串
由于字符串实际上是 C 中的数组,因此您可以通过在方括号 []
中引用其索引号来访问字符串。
此示例打印 greetings 中的第一个字符 (0):
char greetings[] = "Hello World!";
printf("%c", greetings[0]);
请注意,我们必须使用 %c
格式说明符来打印单个字符。
修改字符串
要更改字符串中特定字符的值,请参考索引号,并使用单引号:
char greetings[] = "Hello World!";
greetings[0] = 'J';
printf("%s", greetings);
// 输出 "Jello World!" 而不是 "Hello World!"
另一种创建字符串的方法
在上面的示例中,我们使用 “字符串文字(string literal)” 来创建字符串变量。 这是在 C 中创建字符串的最简单方法。
您还应该注意,您可以使用一组字符创建一个字符串。 此示例将产生与上述示例相同的结果:
char greetings[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
printf("%s", greetings);
为什么我们在末尾包含 \0
字符? 这被称为 “空终止字符(null termininating character)”,在使用此方法创建字符串时必须包含。 它告诉 C 这是字符串的结尾。
差异
两种创建字符串的方法之间的区别在于,第一种方法更容易编写,并且您不必包含 \0
字符,因为 C 会为您完成。
您应该注意,两个数组的大小是相同的:它们都有 13 个字符(顺便说一下,空格也算作一个字符),包括 \0
字符:
char greetings[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
char greetings2[] = "Hello World!";
printf("%lu\n", sizeof(greetings)); // 输出 13
printf("%lu\n", sizeof(greetings2)); // 输出 13
C 用户输入 User Input
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。
您已经了解到 printf()
用于在 C
中输出值。
要获取用户输入,您可以使用 scanf()
函数
用户输入
输出用户输入的数字
#include <stdio.h>
int main( ) {
// 创建一个整数变量来存储我们从用户那里得到的数字
int myNum;
// 要求用户输入一个数字
printf("请输入一个数字: \n");
// 获取并保存用户输入的号码
scanf("%d", &myNum);
// 输出用户输入的数字
printf("您输入的数字: %d", myNum);
}
scanf()
函数有两个参数:变量的格式说明符(上例中的 %d
)和引用运算符(&myNum
),它存储变量的内存地址。
用户输入字符串
您还可以获取用户输入的字符串:
#include <stdio.h>
int main( ) {
// 创建一个字符串
char firstName[30];
// 要求用户输入一些文本
printf("输入您的名字: \n");
// 获取并保存文本
scanf("%s", firstName);
// 输出文本
printf("Hello %s.", firstName);
}
请注意,您必须指定字符串/数组的大小(我们使用了一个非常高的数字,30,但至少我们确定它将为名字存储足够的字符),并且您不必指定引用运算符 (&
) 在 scanf()
中处理字符串时。
C 内存地址 Memory Address
内存地址
在 C 中创建变量时,会为该变量分配一个内存地址。
内存地址是变量在计算机上的存储位置。
当我们给变量赋值时,它就存储在这个内存地址中。
要访问它,请使用引用运算符 (&
),结果将表示变量的存储位置:
int myAge = 43;
printf("%p", &myAge); // 输出: 0x7ffe5367e044
注意: 内存地址为十六进制形式(0x..
)。 您可能不会在您的程序中得到相同的结果。
您还应该注意,&myAge
通常被称为 指针
。 指针基本上将变量的内存地址存储为其值。 要打印指针值,我们使用 %p
格式说明符。 您将在下一章了解更多关于 指针 的内容。
为什么知道内存地址很有用?
Pointers 在 C 中很重要,因为它们使您能够操作计算机内存中的数据 – 这可以减少代码并提高性能。
指针是使 C 从其他编程语言中脱颖而出的因素之一,例如 Python
和 Java
。
C 指针 Pointers
创建指针
您从上一章中了解到,我们可以使用引用运算符 &
获取变量的内存地址:
int myAge = 43; // 一个 int 变量
printf("%d", myAge); // 输出 myAge (43) 的值
printf("%p", &myAge); // 输出myAge的内存地址(0x7ffe5367e044)
在上面的示例中,&myAge
也称为指针。
指针 是一个变量,它存储另一个变量的内存地址作为其值。
指针变量 指向同一类型的数据类型(如 int
),并使用 *
运算符创建。 您正在使用的变量的地址分配给指针:
int myAge = 43; // 一个 int 变量
int* ptr = &myAge; // 一个名为 ptr 的指针变量,用于存储 myAge 的地址
// 输出 myAge (43) 的值
printf("%d\n", myAge);
// 输出myAge的内存地址(0x7ffe5367e044)
printf("%p\n", \&myAge);
// 用指针(0x7ffe5367e044)输出myAge的内存地址
printf("%p\n", ptr);
示例说明
创建一个名为 ptr
的指针变量,它指向一个 int
变量(myAge
)。 请注意,指针的类型必须与您正在使用的变量的类型相匹配。
使用 &
运算符存储 myAge
变量的内存地址,并将其分配给指针。
现在,ptr
保存了myAge
的内存地址的值。
取消引用
在上面的示例中,我们使用指针变量来获取变量的内存地址(与 &
**reference ** 运算符一起使用)。
但是,您也可以使用 *
运算符(**dereference ** 运算符)获取指针指向的变量的值:
int myAge = 43; // 变量声明
int* ptr = &myAge; // 指针声明
// 参考:用指针输出 myAge 的内存地址(0x7ffe5367e044)
printf("%p\n", ptr);
// 取消引用:用指针输出 myAge 的值 (43)
printf("%d\n", *ptr);
请注意,*
符号在这里可能会造成混淆,因为它在我们的代码中做了两件不同的事情:
- 在声明 (
int* ptr
) 中使用时,它会创建一个指针变量。 - 在声明中不使用时,它充当解引用运算符。
我为什么要学习指针?
指针在 C 语言中很重要,因为它们使您能够操作计算机内存中的数据——这可以减少代码并提高性能。
三种声明指针变量的方式
int* myNum; // 最常被使用
int *myNum;
int * myNum;
C 预处理器
C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。
预处理器指令
所有的预处理器命令都是以井号(#
)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:
指令 | 描述 |
---|---|
#define |
定义宏 |
#include |
包含一个源代码文件 |
#undef |
取消已定义的宏 |
#ifdef |
如果宏已经定义,则返回真 |
#ifndef |
如果宏没有定义,则返回真 |
#if |
如果给定条件为真,则编译下面代码 |
#else |
#if 的替代方案 |
#elif |
如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif |
结束一个 #if……#else 条件编译块 |
#error |
当遇到标准错误时,输出错误消息 |
#pragma |
使用标准化方法,向编译器发布特殊的命令到编译器中 |
预处理器实例
分析下面的实例来理解不同的指令。
#define MAX_ARRAY_LENGTH 20
这个指令告诉 CPP
把所有的 MAX_ARRAY_LENGTH
替换为 20
。使用 #define
定义常量来增强可读性。
#include <stdio.h>
#include "myheader.h"
这些指令告诉 CPP
从系统库中获取 stdio.h
,并添加文本到当前的源文件中。下一行告诉 CPP 从本地目录中获取 myheader.h
,并添加内容到当前的源文件中。
#undef FILE_SIZE
#define FILE_SIZE 42
这个指令告诉 CPP
取消已定义的 FILE_SIZE
,并定义它为 42
。
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
这个指令告诉 CPP
只有当 MESSAGE
未定义时,才定义 MESSAGE
。
#ifdef DEBUG
/* 您的调试语句在这里 */
#endif
这个指令告诉 CPP
如果定义了 DEBUG
,则执行处理语句。在编译时,如果您向 gcc
编译器传递了 DDEBUG
开关量,这个指令就非常有用。它定义了 DEBUG
,您可以在编译期间随时开启或关闭调试。
预定义宏
ANSI C 定义了许多宏。在编程中您可以使用这些宏,但是不能直接修改这些预定义的宏。
宏 | 描述 |
---|---|
__DATE__ |
当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
__TIME__ |
当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
__FILE__ |
这会包含当前文件名,一个字符串常量。 |
__LINE__ |
这会包含当前行号,一个十进制常量。 |
__STDC__ |
当编译器以 ANSI 标准编译时,则定义为 1 。 |
#include <stdio.h>
int main() {
printf("File :%s\n", __FILE__);
printf("Date :%s\n", __DATE__);
printf("Time :%s\n", __TIME__);
printf("Line :%d\n", __LINE__);
printf("ANSI :%d\n", __STDC__);
}
预处理器运算符
C 预处理器提供了下列的运算符来帮助您创建宏:
宏延续运算符(\)
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符 \
。例如:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符 #
。在宏中使用的该运算符有一个特定的参数或参数列表。例如:
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Carole and Debra: We love you!
标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void){
int token34 = 40;
tokenpaster(34);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
token34 = 40
这是怎么发生的,因为这个实例会从编译器产生下列的实际输出:
printf ("token34 = %d", token34);
这个实例演示了 token##n
会连接到 token34
中,在这里,我们使用了字符串常量化运算符(#)和标记粘贴运算符(##)。
defined() 运算符
预处理器 defined
运算符是用在常量表达式中的,用来确定一个标识符是否已经使用 #define
定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。下面的实例演示了 defined()
运算符的用法:
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Here is the message: You wish!
参数化的宏
CPP 一个强大的功能是可以使用参数化的宏来模拟函数。例如,下面的代码是计算一个数的平方:
int square(int x) {
return x * x;
}
我们可以使用宏重写上面的代码,如下:
#define square(x) ((x) * (x))
在使用带有参数的宏之前,必须使用 #define
指令定义。参数列表是括在圆括号内,且必须紧跟在宏名称的后边。宏名称和左圆括号之间不允许有空格。例如:
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Max between 20 and 10 is 20
C 函数 Functions
函数是一段代码,只有在被调用时才会运行。
您可以将数据(称为参数)传递给函数。
函数用于执行某些操作,它们对于重用代码很重要:定义代码一次,多次使用。
预定义函数
所以事实证明你已经知道函数是什么了。 在学习本教程的过程中,您一直在使用它!
比如 main()
是一个函数,用来执行代码,printf()
是一个函数; 用于向屏幕输出/打印文本:
int main() {
printf("Hello World!");
return 0;
}
创建函数
要创建(通常称为 declare)您自己的函数,请指定函数的名称,后跟括号 ()
和大括号 {}
:
语法
void myFunction() {
// 要执行的代码
}
示例解释
myFunction()
是函数的名称void
表示函数没有返回值。 您将在下一章稍后了解有关返回值的更多信息- 在函数(主体)内部,添加定义函数应该做什么的代码
调用函数
声明的函数不会立即执行。 它们被“保存以备后用”,并在调用时执行。
要调用函数,请编写函数名,后跟两个括号 ()
和一个分号 ;
在以下示例中,myFunction()
用于在调用时打印文本(操作):
在 main
中,调用 myFunction()
:
// 创建函数
void myFunction() {
printf("晚上好!");
}
int main() {
myFunction(); // 调用函数
return 0;
}
// 输出 -> "晚上好!"
一个函数可以被多次调用:
void myFunction() {
printf("晚上好!");
}
int main() {
myFunction();
myFunction();
myFunction();
return 0;
}
// 晚上好!
// 晚上好!
// 晚上好!
C 函数参数
参数
信息可以作为参数(Parameters/Arguments)传递给函数。 参数在函数内部充当变量。
参数在函数名之后的括号内指定。您可以添加任意数量的参数,只需用逗号分隔它们:
returnType functionName(parameter1, parameter2, parameter3) {
// 要执行的代码
}
以下函数采用带有 name 作为参数的 string of characters。当函数被调用时,我们传递一个名字,这个名字在函数内部用来打印“Hello”和每个人的名字。
void myFunction(char name[]) {
printf("Hello %s\n", name);
}
int main() {
myFunction("Liam");
myFunction("Jenny");
myFunction("Kenny");
return 0;
}
// Hello Liam
// Hello Jenny
// Hello Kenny
当一个 parameter 被传递给函数时,它被称为一个 argument。 因此,从上面的示例中:name
是一个parameter,而Liam
、Jenny
和Anja
是arguments。
多个参数
在函数内部,您可以添加任意数量的参数(parameters):
void myFunction(char name[], int age) {
printf("Hello %s. You are %d years old.\n", name, age);
}
int main() {
myFunction("Liam", 3);
myFunction("Jenny", 14);
myFunction("Anja", 30);
return 0;
}
// Hello Liam. You are 3 years old.
// Hello Jenny. You are 14 years old.
// Hello Anja. You are 30 years old.
请注意,当您使用多个参数(parameters)时,函数调用必须具有与参数(arguments)相同数量的参数(parameters),并且参数(arguments)必须以相同的顺序传递。
返回值
前面示例中使用的 void
关键字表示函数不应返回值。 如果希望函数返回值,可以使用数据类型(如int
或float
等)代替void
,并在函数内部使用return
关键字:
int myFunction(int x) {
return 5 + x;
}
int main() {
printf("Result is: %d", myFunction(3));
return 0;
}
// 输出 8 (5 + 3)
此示例返回具有**两个参数(parameters)**的函数的总和:
int myFunction(int x, int y) {
return x + y;
}
int main() {
printf("Result is: %d", myFunction(5, 3));
return 0;
}
// Outputs 8 (5 + 3)
您还可以将结果存储在变量中:
int myFunction(int x, int y) {
return x + y;
}
int main() {
int result = myFunction(5, 3);
printf("Result is = %d", result);
return 0;
}
// Outputs 8 (5 + 3)
C 函数声明和定义
您刚刚从前面的章节中了解到,您可以通过以下方式创建和调用函数:
示例 1
// 创建函数
void myFunction() {
printf("晚上好!");
}
int main() {
myFunction(); // 调用函数
return 0;
}
一个函数由两部分组成:
- Declaration声明: 函数的名称、返回类型和参数(如果有)
- definition: 函数体(要执行的代码)
void myFunction() { // 声明 declaration
// 函数体(要执行的代码)(definition)
}
对于代码优化,建议将函数的声明和定义分开。
你会经常看到 C 程序在 main()
上方有函数声明,在 main()
下方有函数定义。 这将使代码更好地组织和更容易阅读:
示例 2
// 函数声明
void myFunction();
// 主要方法
int main() {
myFunction(); // 调用函数
return 0;
}
// 函数定义
void myFunction() {
printf("晚上好!");
}
另一个例子
如果我们使用上一章中关于函数参数和返回值的示例:
int myFunction(int x, int y) {
return x + y;
}
int main() {
int result = myFunction(5, 3);
printf("Result is = %d", result);
return 0;
}
// 输出 8 (5 + 3)
像这样编写它被认为是一种好习惯:
// 函数声明
int myFunction(int, int);
// 主要方法
int main() {
int result = myFunction(5, 3); // 调用函数
printf("Result is = %d", result);
return 0;
}
// 函数定义
int myFunction(int x, int y) {
return x + y;
}
C 递归
递归是使函数调用本身的技术。 这种技术提供了一种将复杂问题分解为更容易解决的简单问题的方法。
递归可能有点难以理解。 弄清楚它是如何工作的最好方法是尝试它。
递归示例
将两个数字相加很容易,但将一系列数字相加则比较复杂。 在以下示例中,递归用于将一系列数字相加,方法是将其分解为两个数字相加的简单任务:
int sum(int k);
int main() {
int result = sum(10);
printf("%d", result);
return 0;
}
int sum(int k) {
if (k > 0) {
return k + sum(k - 1);
} else {
return 0;
}
}
示例解释
当调用 sum()
函数时,它会将参数 k
添加到所有小于 k
的数字的总和中并返回结果。 当 k 变为 0 时,函数只返回 0。运行时,程序按以下步骤操作:
10 + sum(9)
10 + ( 9 + sum(8) )
10 + ( 9 + ( 8 + sum(7) ) )
...
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + sum(0)
10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
由于当 k
为 0
时函数不会调用自身,所以程序会停在那里并返回结果。
开发人员应该非常小心递归,因为很容易陷入编写一个永不终止的函数,或者一个使用过多内存或处理器能力的函数。 但是,如果编写正确,递归可能是一种非常有效且数学上优雅的编程方法。
C 数学函数
还有一个可用的数学函数列表,允许您对数字执行数学任务。
要使用它们,您必须在程序中包含 math.h
头文件:
#include <math.h>
平方根
要找到数字的平方根,请使用 sqrt()
函数:
printf("%f", sqrt(16));
四舍五入
ceil()
函数将一个数字向上舍入到最接近的整数,而 floor()
方法将一个数字向下舍入到最接近的整数,并返回结果:
printf("%f", ceil(1.4));
printf("%f", floor(1.4));
Power
pow()
函数返回 x 的 y 次方 (xy):
printf("%f", pow(4, 3));
其他数学函数
下表列出了其他流行的数学函数(来自 <math.h>
库):
函数 | 描述 Description |
---|---|
abs(x) | 返回 x 的绝对值 |
acos(x) | 返回 x 的反余弦值 |
asin(x) | 返回 x 的反正弦值 |
atan(x) | 返回 x 的反正切 |
cbrt(x) | 返回 x 的立方根 |
cos(x) | 返回 x 的余弦 |
exp(x) | 返回 Ex 的值 |
sin(x) | 返回 x 的正弦值(x 以弧度为单位) |
tan(x) | 返回角度的正切 |
C Structures (structs)
结构(也称为结构)是一种将多个相关变量组合到一个位置的方法。 结构中的每个变量都称为结构的成员。
与 array 不同,结构可以包含许多不同的数据类型(int、float、char 等)。
创建结构
您可以使用 struct
关键字创建结构,并在花括号内声明其每个成员:
struct MyStructure { // 结构声明
int myNum; // 成员(int 变量)
char myLetter; // 成员(char 变量)
}; // 用分号结束结构
要访问该结构,您必须为其创建一个变量。
在 main()
方法中使用 struct
关键字,后跟结构的名称,然后是结构变量的名称:
创建一个名为 s1
的结构变量:
struct myStructure {
int myNum;
char myLetter;
};
int main() {
struct myStructure s1;
return 0;
}
访问结构成员
要访问结构的成员,请使用点语法 (.
):
// 创建一个名为 myStructure 的结构
struct myStructure {
int myNum;
char myLetter;
};
int main() {
// 创建一个名为 **s1** 的 myStructure 结构变量
struct myStructure s1;
// 为 s1 的成员赋值
s1.myNum = 13;
s1.myLetter = 'B';
// 打印值
printf("My number: %d\n", s1.myNum);
printf("My letter: %c\n", s1.myLetter);
return 0;
}
现在,您只需使用一个结构即可轻松创建具有不同值的多个结构变量:
// 创建不同的结构变量
struct myStructure s1;
struct myStructure s2;
// 为不同的结构变量赋值
s1.myNum = 13;
s1.myLetter = 'B';
s2.myNum = 20;
s2.myLetter = 'C';
结构中的字符串呢?
请记住,C 中的字符串实际上是一个字符数组,不幸的是,您不能像这样为数组赋值:
struct myStructure {
int myNum;
char myLetter;
char myString[30]; // String
};
int main() {
struct myStructure s1;
// 试图为字符串赋值
s1.myString = "Some text";
// 尝试打印值
printf("My string: %s", s1.myString);
return 0;
}
<!–rehype:style= background: #ff000036;–>
会出现错误:
prog.c:12:15: error: assignment to expression with array type
<!–rehype:style= background: #ff000036;–>
但是,有一个解决方案! 您可以使用 strcpy()
函数并将值分配给 s1.myString
,如下所示:
struct myStructure {
int myNum;
char myLetter;
char myString[30]; // String
};
int main() {
struct myStructure s1;
// 使用 strcpy 函数为字符串赋值
strcpy(s1.myString, "Some text");
// 打印值
printf("My string: %s", s1.myString);
return 0;
}
结果:
My string: Some text
更简单的语法
您还可以在声明时在一行中为结构变量的成员赋值。
只需将值插入花括号 {}
内的逗号分隔列表中。 请注意,您不必通过这种技术对字符串值使用 strcpy()
函数:
// 创建结构
struct myStructure {
int myNum;
char myLetter;
char myString[30];
};
int main() {
// 创建一个结构变量并为其赋值
struct myStructure s1 = {13, 'B', "Some text"};
// 打印值
printf("%d %c %s", s1.myNum, s1.myLetter, s1.myString);
return 0;
}
注意: 插入值的顺序必须与结构中声明的变量类型的顺序相匹配(13 表示 int,’B’ 表示 char 等)。
复制结构
您还可以将一种结构分配给另一种结构。
在以下示例中,将 s1 的值复制到 s2:
struct myStructure s1 = {13, 'B', "Some text"};
struct myStructure s2;
s2 = s1;
修改值
如果要更改/修改值,可以使用点语法 (.
)。
要修改字符串值,strcpy()
函数再次很有用:
struct myStructure {
int myNum;
char myLetter;
char myString[30];
};
int main() {
// 创建一个结构变量并为其赋值
struct myStructure s1 = {13, 'B', "Some text"};
// 修改值
s1.myNum = 30;
s1.myLetter = 'C';
strcpy(s1.myString, "Something else");
// 打印值
printf("%d %c %s", s1.myNum, s1.myLetter, s1.myString);
return 0;
}
当您复制结构值时,修改值特别有用:
// 创建一个结构变量并为其赋值
struct myStructure s1 = {13, 'B', "Some text"};
// 创建另一个结构变量
struct myStructure s2;
// 将 s1 值复制到 s2
s2 = s1;
// 更改 s2 值
s2.myNum = 30;
s2.myLetter = 'C';
strcpy(s2.myString, "Something else");
// 打印值
printf("%d %c %s\n", s1.myNum, s1.myLetter, s1.myString);
printf("%d %c %s\n", s2.myNum, s2.myLetter, s2.myString);
好的,那么,结构如何有用?
想象一下,您必须编写一个程序来存储有关汽车的不同信息,例如品牌、型号和年份。 结构的优点在于您可以创建一个“汽车模板”并将其用于您制造的每辆汽车。 请参阅下面的真实示例。
现实生活中的例子
使用一个结构来存储关于 Cars 的不同信息:
struct Car {
char brand[50];
char model[50];
int year;
};
int main() {
struct Car car1 = {"BMW", "X5", 1999};
struct Car car2 = {"Ford", "Mustang", 1969};
struct Car car3 = {"Toyota", "Corolla", 2011};
printf("%s %s %d\n", car1.brand, car1.model, car1.year);
printf("%s %s %d\n", car2.brand, car2.model, car2.year);
printf("%s %s %d\n", car3.brand, car3.model, car3.year);
return 0;
}
C 中文件处理
在编程中,我们可能需要多次生成某些特定的输入数据。有时,仅在控制台上显示数据是不够的。要显示的数据可能非常大,在控制台上只能显示有限的数据,而且由于内存是容易丢失的,不可能一次又一次地恢复程序生成的数据。 但是,如果我们需要这样做,我们可以将其存储到本地文件系统中,该文件系统是易失的,并且每次都可以访问。 在这里,需要在 C 中处理文件。
C 中的文件处理使我们能够通过我们的 C 程序创建、更新、读取和删除存储在本地文件系统中的文件。 可以对文件执行以下操作。
- 创建新文件
- 打开现有文件
- 从文件中读取
- 写入文件
- 删除文件
文件处理函数
C 库中有许多函数可以打开、读取、写入、搜索和关闭文件。 文件函数列表如下:
函数 | 描述 Description |
---|---|
fopen() | 打开新文件或现有文件 |
fprintf() | 将数据写入文件 |
fscanf() | 从文件中读取数据 |
fputc() | 将一个字符写入文件 |
fgetc() | 从文件中读取一个字符 |
fclose() | 关闭文件 |
fseek() | 将文件指针设置到给定位置 |
fputw() | 将整数写入文件 |
fgetw() | 从文件中读取一个整数 |
ftell() | 返回当前位置 |
rewind() | 将文件指针设置为文件的开头 |
<!–rehype:style=width: 100%; display: inline-table;–> |
打开文件:fopen()
我们必须先打开一个文件,然后它才能被读取、写入或更新。fopen()
函数用于打开文件。fopen()
的语法如下所示:
FILE *fopen( const char * filename, const char * mode );
fopen() 函数接受两个参数:
- 文件名(字符串)。 如果文件存储在某个特定位置,那么我们必须提及文件存储的路径。例如,文件名可以像“/some_folder/some_file.ext”。
- 打开文件的模式。 它是一个字符串。
我们可以在 fopen() 函数中使用以下模式之一。
模式 Mode | 描述 Description |
---|---|
r | 以读取模式打开一个文本文件,允许读取文件。 |
w | 以写模式打开一个文本文件,允许写入文件。 |
a | 以追加模式打开一个文本文件,如果文件不存在,则会创建一个新文件。 |
r+ | 以读写模式打开一个文本文件,允许读写文件。 |
w+ | 以读写模式打开一个文本文件,允许读写文件。 |
a+ | 以读写模式打开一个文本文件,允许读写文件。 |
rb | 以读取模式打开二进制文件 |
wb | 以写入模式打开二进制文件 |
ab | 以追加模式打开二进制文件 |
rb+ | 以读写模式打开二进制文件 |
wb+ | 以读写模式打开二进制文件 |
ab+ | 以读写模式打开二进制文件 |
<!–rehype:style=width: 100%; display: inline-table;–> |
#include<stdio.h>
void main( ) {
FILE *fp;
char ch;
fp = fopen("file_handle.c", "r");
while (1) {
ch = fgetc(fp);
if (ch == EOF)
break;
printf("%c", ch);
}
fclose(fp);
}
输出,将打印文件的内容:
#include<stdio.h>
void main( ) {
FILE *fp;
char ch;
fp = fopen("file_handle.c", "r");
while (1) {
ch = fgetc(fp);
if (ch == EOF)
break;
printf("%c", ch);
}
fclose(fp);
}
关闭文件:fclose()
fclose()
函数用于关闭文件。 对文件执行所有操作后,必须关闭该文件。fclose()
函数的语法如下:
int fclose(FILE *fp);
写入文件:fprintf()
fprintf()
函数用于将字符集写入文件。 它将格式化的输出发送到流。
int fprintf(FILE *stream, const char *format [, argument, ...])
#include <stdio.h>
main() {
FILE *fp;
fp = fopen("file.txt", "w"); // 打开文件
fprintf(fp, "Hello file by fprintf...\n"); // 将数据写入文件
fclose(fp); // 关闭文件
}
读取文件:fscanf()
fscanf()
函数用于从文件中读取字符集。 它从文件中读取一个单词并在文件末尾返回 EOF。
int fscanf(FILE *stream, const char *format [, argument, ...])
#include <stdio.h>
main(){
FILE *fp;
char buff[255]; // 创建 char 数组来存储文件数据
fp = fopen("file.txt", "r");
while(fscanf(fp, "%s", buff)!=EOF) {
printf("%s ", buff);
}
fclose(fp);
}
C 文件示例:存储员工信息
让我们看一个文件处理示例,用于存储用户从控制台输入的员工信息。 我们将存储员工的 ID、姓名和薪水。
#include <stdio.h>
void main() {
FILE *fptr;
int id;
char name[30];
float salary;
fptr = fopen("emp.txt", "w+"); /* 打开文件 */
if (fptr == NULL) {
printf("文件不存在 \n");
return;
}
printf("Enter the id\n");
scanf("%d", &id);
fprintf(fptr, "Id= %d\n", id);
printf("Enter the name \n");
scanf("%s", name);
fprintf(fptr, "Name= %s\n", name);
printf("Enter the salary\n");
scanf("%f", &salary);
fprintf(fptr, "Salary= %.2f\n", salary);
fclose(fptr);
}
输出
Enter the id
1
Enter the name
kenny
Enter the salary
120000
现在从当前目录打开文件。你会看到 emp.txt 文件。它有以下信息:
Id= 1
Name= kenny
Salary= 120000
写入文件:fputc()
fputc()
函数用于将单个字符写入文件。它将一个字符输出到一个字符串
int fputc(int c, FILE *stream)
#include <stdio.h>
main(){
FILE *fp;
fp = fopen("file1.txt", "w"); // 打开文件
fputc('a',fp); // 将单个字符写入文件
fclose(fp); // 关闭文件
}
读取文件:fgetc()
fgetc()
函数从文件中返回单个字符。它从流中获取一个字符。它在文件末尾返回 EOF:
int fgetc(FILE *stream)
#include<stdio.h>
#include<conio.h>
void main() {
FILE *fp;
char c;
clrscr();
fp=fopen("myfile.txt", "r");
while((c=fgetc(fp))!=EOF){
printf("%c", c);
}
fclose(fp);
getch();
}
写入文件:fputs()
fputs()
函数将一行字符写入文件。它将字符串输出到流
int fputs(const char *s, FILE *stream)
#include<stdio.h>
#include<conio.h>
void main(){
FILE *fp;
clrscr();
fp = fopen("myfile2.txt","w");
fputs("hello c programming",fp);
fclose(fp);
getch();
}
读取文件:fgets()
fgets()
函数从文件中读取一行字符。 它从流中获取字符串:
char* fgets(char *s, int n, FILE *stream)
#include<stdio.h>
#include<conio.h>
void main() {
FILE *fp;
char text[300];
clrscr();
fp=fopen("myfile2.txt", "r");
printf("%s", fgets(text, 200, fp));
fclose(fp);
getch();
}
fseek()
fseek()
函数用于将文件指针设置为指定的偏移量。 它用于将数据写入所需位置的文件:
int fseek(FILE *stream, long int offset, int whence)
fseek()
函数中使用了 3 个常量:SEEK_SET
、SEEK_CUR
和 SEEK_END
。
#include <stdio.h>
void main(){
FILE *fp;
fp = fopen("myfile.txt","w+");
fputs("This is Book", fp);
fseek(fp, 7, SEEK_SET);
fputs("Kenny Wong", fp);
fclose(fp);
}
rewind()
rewind()
函数将文件指针设置在流的开头。 如果您必须多次使用流,这很有用:
void rewind(FILE *stream)
#include<stdio.h>
#include<conio.h>
void main(){
FILE *fp;
char c;
clrscr();
fp=fopen("file.txt", "r");
while((c=fgetc(fp)) != EOF){
printf("%c", c);
}
rewind(fp); // 将文件指针移动到文件的开头
while((c=fgetc(fp)) != EOF){
printf("%c", c);
}
fclose(fp);
getch();
}
// 输出
// Hello World!Hello World!
如您所见,rewind()
函数将文件指针移动到文件的开头,这就是 Hello World!
被打印 2 次的原因。 如果你不调用 rewind()
函数,“Hello World!” 将只打印一次。
ftell()
ftell()
函数返回指定流的当前文件位置。 我们可以使用 ftell()
函数在文件末尾移动文件指针后获取文件的总大小。 我们可以使用 SEEK_END
常量将文件指针移动到文件末尾。
long int ftell(FILE *stream)
#include <stdio.h>
#include <conio.h>
void main (){
FILE *fp;
int length;
clrscr();
fp = fopen("file.txt", "r");
fseek(fp, 0, SEEK_END);
length = ftell(fp);
fclose(fp);
printf("Size of file: %d bytes", length);
getch();
}
// 输出
// Size of file: 18 bytes
C 关键字
C 语言一共有32个关键字,如下表所示:
关键字 | 说明 | 关键字 | 说明 |
---|---|---|---|
auto | 声明自动变量 | static | 声明静态变量 |
short | 声明短整型变量或函数 | volatile | 说明变量在程序执行中可被隐含地改变 |
int | 声明整型变量或函数 | void | 声明函数无返回值或无参数,声明无类型指针 |
long | 声明长整型变量或函数 | if | 条件语句 |
float | 声明浮点型变量或函数 | else | 条件语句否定分支(与 if 连用) |
double | 声明双精度变量或函数 | switch | 用于开关语句 |
char | 声明字符型变量或函数 | case | 开关语句分支 |
struct | 声明结构体变量或函数 | for | 一种循环语句 |
union | 声明共用数据类型 | do | 循环语句的循环体 |
enum | 声明枚举类型 | while | 循环语句的循环条件 |
typedef | 用以给数据类型取别名 | goto | 无条件跳转语句 |
const | 声明只读变量 | continue | 结束当前循环,开始下一轮循环 |
unsigned | 声明无符号类型变量或函数 | break | 跳出当前循环 |
signed | 声明有符号类型变量或函数 | default | 开关语句中的“其他”分支 |
extern | 声明变量是在其他文件正声明 | sizeof | 计算数据类型长度 |
register | 声明寄存器变量 | return | 子程序返回语句(可以带参数,也可不带参数)循环条件 |
<!–rehype:style=width: 100%; display: inline-table;–> |