C chapter1 基本概念


前言

本文程序开发基于 Ubuntu 环境下的 gcc 编译器。

本着没整理就是没学习的原则,菜鸡的我又来学 C 了。

前面学 C 都是学到在 CTF 中够用就行了,不过这次打算一定要把 C 研究透。

基本结构

C 中一个程序最基本的结构就是头文件和主函数,以下列代码为例。

头文件就是使用#include包含的库文件,即stdio.h这样。

以下库文件就是最常用的库stdio.h库,因为它是输入输出标准库,我们要调用其中函数输入输出内容。

主函数就是main函数,是程序的入口函数,笼统的说程序从这里开始执行。

我们需要将以下代码保存为后缀名为.c的源文件,然后使用 gcc 编译器进行编译。

经典程序 Hello,world!

利用stdio.h标准输入输出库中的printf函数输出文本。

输出的文本中\n 是一个转义字符表示换行。

下面这行程序输出一行指定文本

1
2
3
4
5
6
7
#include <stdio.h>
int main(){
printf("Hello,world\n");
return 0;
}
//运行结果
Hello,world

printf函数

用于格式化输出到标准输出(屏幕)。

其基本语法如下:

1
2
#include <stdio.h>
int printf(const char *format,...)

函数括号中的是函数参数。

  • printf参数
    • format:格式化字符串,用于指定输出的格式。格式化符号如%d%f%s等用于显示不同类型的数据。
    • %d:表示输出整数
    • %f:表示输出浮点数
    • %c:表示输出字符
    • %s:表示输出字符串
  • ...可变参数,根据format字符串中指定的格式进行匹配。

字符

字符规则就像英语中的拼写规则,决定你在源程序中如何形成单独的字符片段,也就是标记(token)。

一个 ANSI C 程序由声明和函数组成。函数定义了需要执行的工作,而声明则描述了函数和(或)函数将要操作的数据类型(有时候是数据本身)。

标准规定 C 字符集必须包括英语所有的大写和小写字母,数字 0 到 9,以及下面这些符号:

! # % () + , - . / ?
; <> = ? [] \ ^ _ {} | ~

三字母词

三字母词(trigrph),三字母词就是几个字符的序列,合起来表示另一个字符。三字母词使 C 环境可以在某些缺失一些必需的字符的字符集上实现。

常见三字母词:

??( [ ??< { ??= #
??) ] ??> } ??/ \
??! | ??’ ^ ??- ~
两个问号开头再尾随一共字符一般不会出现在其它表达式中,所以把三字母词用这种形式来表示,这样就不致于引起误解。

当你编写某些 C 代码时,你在一些上下文环境里想使用某个特定的字符,却可能无法如愿,因为该字符在这个环境里有特别的意义。例如,双引号 “ 用于界定字符串常量,你如何在一个字符串常量内部包含一共双引号呢?这个适合就需要通过转义字符了。

转义字符

C 语言的转义字符用于表示那些不能直接在字符串中输入的字符。

常见的转义字符包括:

符号 含义
\‘ 单引号
\“ 双引号
\? 问号
\\ 反斜线
\a 响铃
\b 退格
\f 分页符
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符

标识符

标识符(indentifier)就是变量、函数、类型等的名字。它们由大小写字母、数字和下划线组成,但不能以数字开头。

C 是一种大小写敏感的语言,所以 abc、Abc、abC 和 ABC 是 4 个不同的标识符。

标识符的长度没有限制,但标准允许编译器忽略第 31 个字符以后的字符。标准同时允许编译器对用于表示外部名字(也就是由链接器操纵的名字)的标识符进行限制,只识别前六位不区分大小写的字符。

下列 C 语言关键字是被保留的,它们不能作为标识符使用:

auto do goto signed unsigned
break double if sizeof void
case else int static volatile
char enum long struct while
const extern register switch continue
float return typedef default for
short union

注释

注释用于在代码中添加说明文字,帮助他人理解代码的功能和意图。

注释不会被编译器执行,因此不会影响程序的运行。

C 语言支持两种类型的注释:

单行注释

1
//这是单行注释

多行注释

1
2
3
/*
这是多行注释
*/

注意:注释不能够嵌套使用

定义变量

这里我们只介绍基本整型变量,详细的后面会讲述。

常见的变量类型有:

  • char:一般用于表示单个字符
  • short:占两个字节的整型
  • int:占四个字节的整型
  • long:至少2个字节,可能是4个字节。

以下代码声明了一个int型的变量并初始化值为 10。

利用printf格式化函数输出变量。

实例

1
2
3
4
5
int main(){
int a=10;
printf("%d",a);
return 0;
}

scanf函数

前面的printf函数用于输出内容,这里的scanf函数用于输入内容。

既然要输入内容,那么输入的内容就必须要有地方存储。

我们采用上面提到的变量来存储它。

1
2
#include <stdio.h>
int scanf(const char *format,...)
  • 参数
    • format:格式化字符串,用于指定输入的数据类型。
    • 常用格式化符号
      • %d:整数
      • %f:浮点数
      • %c:字符
      • %s:字符串
    • ...可变参数,用于存储输入的数据。每个变量应对应格式化字符串中的格式符。
      • scanf参数变量前必须加&

示例

下面代码定义了一个int变量 a 用于接收输入内容。

然后利用printf函数将输入的内容打印出来。

1
2
3
4
5
6
int main(){
int a;
scanf("%d",&a);
printf("%d\n",a);
return 0;
}

计算两数之和

以下程序定义了 3 个变量。

先利用printf函数输出提示信息。

通过scanf函数输入两个数,然后计算两个数的和存储到第三个变量。

之后利用printf格式化输出将和输出。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main(){

int a,b,sum;
printf("请输入两个数字\n");
scanf("%d %d",&a,&b);
sum=a+b;
printf("sum = %d\n",sum);

return 0;
}

Makefile

Makefile 是一种自动化构建工具的配置文件,通常用于管理和自动化编译程序的过程。它定义了如何从源代码生成目标文件,以及在目标文件发送变化时如何重新构建这些文件。以下是 Makefile 的基本概念:

  1. 目标(Target):需要生成的文件,如可执行文件或中间文件。目标通常是文件名,如hello
  2. 依赖(Dependency):目标文件生成所依赖的源文件或其它目标文件。例如,hello依赖于main.oprinthello.o
  3. 规则(Rule):指定如何从依赖文件生成目标文件的命令。规则通常包括目标文件、依赖文件以及执行的命令。
  4. 变量(Variable):用于定义可重复使用的值,如编译器或编译选项。变量简化了 Makefile 的编写和维护。例如,C=gcc
  5. 伪目标(Phony Target):不代表实际文件的目标,如clean,用于执行特定的操作,如删除生成的文件。

在 Linux 下进行 C 语言程序开发我们离不开 Makefile,Makefile 可以提升我们管理代码和编译代码的效率,可以避免重复工作。

我们利用 Makefile 来编译上述程序

使用 Makefile 的前提是系统上要安装 make 工具。

Ubuntu下可以执行如下命令安装。

1
apt install make
1
2
3
4
5
6
7
8
9
10
11
#规则:sum:main.c
#依赖:main.c
#命令:gcc - sum main.c
sum: main.c
gcc -o sum main.c
.PHONY:clean

#伪目标:.PHONY:clean
#规则:clean 执行下列命令
clean:
rm -r sum

将 Makefile 保存到代码文件目录命名为 Makefile。

然后使用make命令进行编译。

直接在命令行执行 make 命令即可。

对于上述 Mkaefile我们也可以写成下面这样,增加通用性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#变量
C=gcc
TARGET=sum
OBJ=main.c

#规则:目标:依赖
#命令:$(C)替换为gcc,$@ 代表目标文件名,$^ 代表所有依赖文件
$(TARGET):$(OBJ)
$(C) -o $@ $^

#伪目标:删除生成的目标文件
.PHONY:clean
clean:
rm -f $(TARGET)

上面的 Makefile 可以通过使用 make clean命令清理掉编译后的可执行文件,便于重新编译。

这些都是比较简单的,随着对 C 的学习我们还会编写更复杂的 Makefile。