⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划

🔗 《C语言趣味教程》👈 猛戳订阅!!!

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_02

—— 热门专栏《维生素C语言》的重制版 ——


  • 💭 写在前面:这是一套 C 语言趣味教学专栏,目前正在火热连载中,欢迎猛戳订阅!本专栏保证篇篇精品,继续保持本人一贯的幽默式写作风格,当然,在有趣的同时也同样会保证文章的质量,旨在能够产出 "有趣的干货" !

    本系列教程不管是零基础还是有基础的读者都可以阅读,可以先看看目录! 标题前带星号 (*) 的部分不建议初学者阅读,因为内容难免会超出当前章节的知识点,面向的是对 C 语言有一定基础或已经学过一遍的读者,初学者可自行选择跳过带星号的标题内容,等到后期再回过头来学习。

    值得一提的是,本专栏 强烈建议使用网页端阅读! 享受极度舒适的排版!你也可以展开目录,看看有没有你感兴趣的部分! 本章是本教程的开篇之作,篇幅较长且内容极为丰富 (全文近两万字),是本团队 (只有我一个人的团队)

" 有趣的写作风格,还有特制的表情包,而且还干货满满!太下饭了!"

—— 沃兹基硕德

📜 本章目录:

Ⅰ. 你好, 世界!(Hello World)

0x00 引入:HelloWorld 的由来

0x01 代码编辑器的选用(推荐 VS2022)

0x02 创建新项目

0x03 敲下这 “跨越历史” 的 Hello World!

Ⅱ. 头文件(Header)

0x00 引入:什么是头文件?

0x01 标准输入输出库 stdio

0x02 引入头文件的方式

* 0x03 养成声明与定义分离的习惯

* 0x04 头文件保护(防止头文件重复包含)

* 0x05 条件引用技术

* 0x06 实践错误记录:#include 展开的问题

* 0x07 技巧:学会定义 _GLOBAL_H 来管理头文件

* 0x08 整理:stdio 定义的库变量、库宏和库函数

Ⅲ.  main 函数(Main Function)

0x00 引入:继续观察 HelloWorld 示例

0x01 什么是 main 函数?

0x02 常规写法: int main() 和 int main(void)

* 0x03 带参写法:int main(int argc, char *argv[])

* 0x04 颇有争议的写法:void main()

* 0x05 存在于 C89 的写法:main()

* 0x06 错误写法:mian()

* 0x07 main 函数执行前做的事

* 0x08 可以调用 main 函数吗?

Ⅳ. 深入浅出:Hello,World!

0x00 引入:再看 Hello,World!

0x01 函数体的概念(花括号匹配原则)

0x02 简单介绍一下 printf 函数

0x03 分号,语句结束的标志!

0x04 返回语句 return 

* 0x05 关于 return 后面带括号的写法

0x06 深入浅出:Hello,World!

尾记:如何学习一门编程语言?


Ⅰ. 你好, 世界!(Hello World)

  • 本章是首个章节,将通过计算机最经典的示例程序  Hello World 来展开我们的教程,考虑到 C 语言历史大家应该早已屡见不鲜,所以这里我们选择介绍 Hello World 的历史和由来。然后带着大家创建项目并敲下这最经典的代码。

0x00 引入:HelloWorld 的由来

"所有的伟大,都源于一个勇敢的开始!"


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_03

❓ 思考:什么是 Hello World ?它又是怎么来的?


Hello World 是一种常见的计算机程序,通常作为一个新编程语言或平台的入门示例。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_04

 它的起源可以追溯到 1974 年,由计算机科学家 布莱恩·柯林汉 创造。

没错,就是那个 80 岁还在咔咔咔写代码的巨佬,贝尔实验室的 神仙

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_05

 在 C 语言第一本教材《C程序设计语言》中,

他使用了 Hello World 作为一个简单的示例,来介绍 C 语言的基本语法。

他与 C 语言之父 —— 丹尼斯里奇 共同合作了撰写了这本书,K&R 就是两人名字的缩写。


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_06


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_07

 同时,他还是开发 Unix 的主要贡献者,Unix 就是由柯林汉命名的!

"全员恶人?什么恶人?全员神仙!"

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_08


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_09

 可以这么说,当你把 Hello World 这几个字成功打印到屏幕上时,

你的内心体验到的不仅仅是一种成功的喜悦,更重要的是,你正在亲身经历一个跨越历史的时刻!

" 编程生涯,由此开始!"

Hello World 究竟从何而来?

当 Forbes India 杂志采访柯林汉时,他本人对自己这段传奇故事中一些记忆已经有点儿模糊了。当被问及为什么选择 "Hello, World! "时,他回答道:"我只记得,我好像看过一幅漫画,讲述一枚鸡蛋和一只小鸡的故事,在那副漫画中,小鸡说了一句 Hello World " ——



⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_10


0x01 代码编辑器的选用(推荐 VS2022)

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_11

 刚才我们了解了 Hello World 的故事,敲之前还需要做一些 "必要" 的准备!正所谓:

" 工欲善其事,必先利其器!"

我们先来对 "代码编辑器" 做一个简单的了解,我们这里指的 "编辑器" 是 集成开发环境 (IDE) 

集成开发环境(即 IDE)是一种软件应用程序,提供了一个集成的开发环境,包括代码编辑器、编译器、调试器和其他开发工具,用于简化和加速软件开发的过程。IDE 通常用于软件开发,尤其是针对特定编程语言的开发,例如 Java、Python、C++ 等。IDE 的主要优点是提供了一个集成的工作流程,使得开发人员能够更加高效地编写、测试和调试代码。IDE通常具有自动代码完成、语法高亮、代码调试、版本控制等功能,可以大大提高开发效率和代码质量。常见的 IDE 包括 Visual Studio、Eclipse、和 IntelliJ IDEA 等。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_12

 下面我先打开我的编辑器,我的代码编辑器是:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_13


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_14

 大人!时代变了!怎么还有人在用VC6.0 啊!不会吧?

哈哈哈哈哈,怎么会!我用的可是支持脑机接口编程的VS2077 :

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_15


开个玩笑,其实是 VS2022!本系列博客 Windows 系统下一律选用 Visual Studio 2022

我们也是非常推荐大家使用 VS2022 的,臣以为 VS2022 真乃 宇宙最强编辑器 也!

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_16


" 强烈建议安装,用过都说好,下载链接我贴到下面了。"

🔗 VS 官网:Visual Studio 2022 IDE - 适用于软件开发人员的编程工具

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_17


学习阶段我们使用免费的 Community2022 版本 (社区版) 就完全够用了,大家可以自行下载。

关于为什么选用 VS

关于 Visual Studio 的安装,网上有很多很不错的教学视频,简易大家跟着视频去安装。 安装过程还是没有什么难度的,喝杯咖啡的功夫就可以搞定了!和安装 Java 环境比真的容易太多,Java 还要手动配置环境,设置电脑环境变量等一系列杂七杂八的工作…… 而 Visual Studio 非常人性化地一条龙服务,几乎帮你包揽了这一切!(还有个编辑器叫CLion,J家的开包即用的编辑器,但本专栏还是选用了 VS)


此外,微软还有一个叫 Vscode 的神器,这个环境的准备就复杂得多,我们也不简易新手一上来就用 Vscode(虽然我当年系统性学C的时候就直接捣鼓了 Vscode 环境),安装环境的过程是非常劝退人的,所以也不建议。值得一提的是,Vscode 属于轻量化的编辑器,并不是 IDE!


当然有些学校可能会要求大家使用 Dev C++,那个也是个不错的编辑器,但是还是建议大家去使用 Visual Studio,因为后续章节中会附带一些基础调试的教学,是基于 Visual Studio 的,本专栏在 Window 下也都是采用 Visual Studio 来给大家做教学演示的,所以为了更好地贴合本专栏进行系统性地学习,尽量选用 Visual Studio! (ps:再多说一句:如果你学校推荐的编译器是 VC6.0 + 和用到包浆的 PPT,额…… )


* 当然,为了更方便大家系统性地学习,后期我会更新一篇 VS 的安装教程的。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_18

下面,我们将认为读者已经安装好了 VS2022,并带着大家手把手敲出 Hello,World!

📌 注意:所以为了更好地贴合本专栏进行系统性地学习,尽量选用 Visual Studio

0x02 创建新项目

首先,找到桌面快捷方式,双击打开 Visual Studio 2022

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_19

(简约而不失优雅的加载页面...)

 进入下方页面后,点击右下角的 创建新项目 (N)

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_20


 然后点击 空项目

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_21


随后到了配置新项目,这里创建项目名称,演示阶段名字自行取,之后点击 创建 (C) 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_22


创建完新项目后,我们找到 解决方案资源管理器,右键 "源文件",点击 "添加新建项" :

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_23


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_24

名称我们就取为 test.c,然后点 添加(A),我们的工程就会在指定的位置很好地创建出来:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_25


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_26

 这里的 .c 文件 和 .h 以及 VS 默认的 .cpp

简而言之,.c 文件用于 C 语言编程,.cpp 文件用于 C++ 编程:

  • .c 是 C 源代码文件的扩展名。
  • .cpp 是 C++ 源代码文件的扩展名。
  • .h 是头文件 (header file) ,包含在 C 或 C++ 程序中,用于声明函数、变量和数据结构等,以便在程序的其他部分中使用。

(这些我们在后期讲解 编译链接 时都会详细讲解,这里只需要 .c 是 C 语言源文件的后缀即可)

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_27

创建完毕后,我们就可以在 test.c 下打代码了:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_28


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_29

 至此,我们要做的准备工作基本就完成了,下面我们就要开始敲 Hello,World 了!

0x03 敲下这 “跨越历史” 的 Hello World!

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_30

 如果你是一个初学者,我们很能理解你迫切的想要敲出 Hello,World 的心情!

我们现在就拉出本教程的第一个代码 —— Hello,World,来让大家 一睹芳容 !

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_31


" 手动播放劲爆的新宝岛 bgm,敲下这令人激动的第一个代码!"

💬 代码演示:C 语言经典示例程序 —— Hello,World


#include <stdio.h>

int main()
{
	printf("Hello, World!\n");

	return 0;
}


你可以手动将代码打到源文件中,也可以直接点击右上角 "复制" 按钮,将代码粘贴到源文件中。

下面我们准备运行这段代码,点击顶部菜单栏 调试 (D) → 开始执行(不调试) 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_32


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_33

 当然你也可以使用快捷键运行,输入 Ctrl + F5

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_34


我们也推荐大家使用!因为作为键盘侠,使用快捷键会很 cool !

运行后就会弹出一个窗口 —— Microsoft Visual Stidio 调试控制台:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_35


我们可以看到,我们的 Hello, World! 在控制台上出现了,这是因为我们调用了打印函数 printf :


#include <stdio.h>

int main()
{
	printf("Hello, World!\n");  👈 打印 Hello,World

	return 0;
}


(如果是零基础读者,看不明白这段代码,这都没有关系,我们会深入浅出地慢慢讲解)

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_36

 只会敲 Hello World 的我瑟瑟发抖……

至此,我们的 Hello World 就敲完了,我们也成功的将这段话输出到了我们的显示器上。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_37

下面我们会慢慢地、深入浅出地讲解刚才的 Hello World 代码:

#include <stdio.h>

int main()
{
	printf("Hello, World!\n");  

	return 0;
}


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_38

 等讲解完这些必要的知识点后,我们再回来看这段代码,就会非常地清楚 ~

Ⅱ. 头文件(Header)

  • 围绕着 HelloWorld 继续推进,理解第一行代码我们需要先知道什么是头文件,以及我们引入的 stdio 库,这里我们会详细的讲解各种头文件的知识点,包含了头文件保护、声明定义分离、#include 展开等内容。

0x00 引入:什么是头文件?

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_39下面是 HelloWorld 示例程序的第一行代码:


#include <stdio.h>    // 引入头文件 stdio 库


* 注:两个斜杠 ( // ) 后面的内容属于「注释」,我们会在下一章进行讲解!

这一行代码的意思是 "引入头文件 stdio.h 库",我们先来简单介绍一下什么是 头文件 (header)。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_40


📚 概念:头文件, [英] header,[繁]

后缀为 .h 的文件被称为 "头文件" ,因为头文件的英文是 header,所以缩写为 .h

包含了 C 语言函数声明和宏定义,被多个源文件中引用共享。

头文件分为两种,一种是 "程序员自己编写的头文件",另一种是 "编译器自带的头文件" 。

关于头文件的一些细节

在 C 语言中,头文件是一个文件。头文件通常是源代码的形式,由编译器在处理另一个源文件时自动包含进来。程序员通过编译器指令将头文件包含进其他源文件的开始。


一个头文件包含类、子程序、变量和其他标识符的前置声明,需要在一个以上源文件中生命的标识符可以放在一个头文件中,并在需要的地方包含该头文件。头文件作为一种包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明。


早期的编程语言如 Basic、Fortran 并没有头文件的概念。

我们使用 #include


#include         引入头文件


我们的 Hello World 示例代码中,就是使用了 #include 来引用头文件的,引用的是 stdio.h 。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_41

 这是什么?下面,我们就来介绍一下这个 stdio.h。

0x01 标准输入输出库 stdio

stdio.h 是编译器自带的头文件,其中 .h 我们刚才说过,是扩展名,被称之为头文件。

stdio 全称 standard input & output,即标准输入输出,我们称之为 C 标准库,直接看图记忆:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_42


因为我们的 HelloWorld 示例程序需要用到 printf 这个函数,所以我们需要引入 stdio.h

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_43

 之所以叫做 stdio 是因为 standard input & output,而它表示了这库中最经典的两个函数:

  • printf() :标准输入函数(input)
  • scanf() :标准输出函数(output)

(对于这两个函数,我们后续会详细讲解)

📜 了解:stdio.h


/*
*stdio.h
*This file has no copyright assigned and is placed in the Public Domain.
*This file is a part of the mingw-runtime package.
*Now arranty is given;refer to the file DISCLAIMER within the package.
*
*Definitions of types and proto types of functions for 
*standard inputand output.
*
*NOTE:The file manipulation functions provided by Microsoft seem to
*work with either slash(/) or backslash(\) as the directory separator.
*
*/


0x02 引入头文件的方式

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_44

 我们前面讲了,我们使用 #include 来引用头文件,这个 #

预处理指令 #

刚才我们介绍了 stdio.h


#include <stdio.h>


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_45

 我们可以看到 #include 后面使用了两个尖括号 < > 包住了 stdio.h。

尖括号中引入的头文件基本都是 C 语言中各种各样的 库函数,刚才学习的 stdio 就是其中之一。

这行代码中,我们通过 #include <>

头文件的引入除了尖括号 < > ,还有双引号 " " 形式的引入:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_46


🔍 两种引用方式的区别:头文件用 < > 引入和用 " "

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_47

首先,< > 和 " "

 尖括号 < > 的查找策略:直接去标准路径下去查找,用来引用标准库的头文件。


#include <文件>   // 编译器将从标准库目录开始搜索


双引号 " "


#include "文件"   // 编译器将从用户的工作目录开始搜索


❓ 思考:那可不可以用双引号 " "


#include "stdio.h"


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_48

 当然,库文件也是可以使用 " " 包含的,但是不建议库文件用 " " 形式引入!

#include <stdio.h> 是库文件,你用引号形式引入 #include "stdio.h"

但是这样查找的效率就会大打折扣,因为会先从源文件所在目录下去找一遍,然后再去找。

我们既然明确知道自己是要使用库文件了,我们自然就直接使用尖括号才是正常的。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_49

而且这样也不容易区分是库文件还是本地文件。

(看到尖括号引入就知道是库文件,看到引号引入的就知道是本地文件,还是很香的)

① Linux环境 标准头文件的路径:


/usr/include


② VS环境 标准头文件的路径:


C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include


*

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_50

要做到声明与定义相分离,头文件中应当只存放声明,而不存放定义。

为了养成模块化编程的好习惯,我们尽量把代码分开写,声明放到头文件中,定义放到源文件中。

VS

  • 在 "头文件" 文件夹中右键取文件名以 .h 结尾即可创建头文件。
  • 在 "源文件" 文件夹中右键取文件名以 .c 结尾即可创建源文件。

C++ 语法中,类的成员函数可以在声明的同时被定义,并自动成为内联函数。这带来了书写上的便利,但也造成了风格的不一致,弊大于利。建议将成员函数的定义与声明分离开来。

* 0x04 头文件保护(防止头文件重复包含)

"使用头文件保护技术,能够有效提升代码的一致性和可维护性……"

当需要引入多个头文件时,头文件引用条目达到十几条以上时,难免会出现重复引用的失误。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_51

 比如下面这个代码,仔细观察你会发现不小心引入了两次 stdio:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cstdio>
#include <memory>
#include <cstdlib>
#include <random>
#include <cstdio>
#include <stdio.h>     ← 引入两次了
#include <utility>
#include <cstring>
#include <vector>
#include <functional>
...


这就是典型的重复引用错误,此时编译器会处理两次头文件的内容,从而产生错误。

为了防止这种情况的发生,经典的做法是将文件的整个内容都放到条件编译语句中。

具体做法是使用预处理指令 #ifndef、#define 和 #endif


#ifndef STDIO_H
#define STDIO_H

// stdio.h 的内容

#endif


这里我们使用了包装器 #ifndef,当再次引用头文件时条件为假,因为 STDIO_H

如此一来,预处理器会检查 STDIO_H 是否以及定义,如果没有定义就会进入 #ifndef

如果已经定义,此时预处理器会跳过文件的整个类容,编译器就会将其忽略不计。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_52

 该技术我们称之为 头文件保护 (header guards) ,可以有效确保头文件只被包含一次。

⚡ 如果嫌麻烦,还有一种非常简单的方法:


#pragma once // 让头文件即使被包含多次,也只包含一份


(具体的讲解我们会在后面的预处理章节进行更加深刻细致的讲解)

因此,为了预防头文件被重复引入,建议使用 ifndef / define / endif 结构产生与处理块。

*

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_53

条件引用是一种根据特定条件来选择性包含或排除代码的技术。

如果需要从多个不同的头文件选择一个引用到程序中,此时就需要用 "有条件引用" 来达成目的。

举个例子,现在我们需要指定不同的操作系统上使用的配置参数:


#if SYSTEM_1
   # include "system_1.h"
#elif SYSTEM_2
   # include "system_2.h"
#elif SYSTEM_3
   ...
#endif


当头文件较多时,此这么做显然不妥,我们可以预处理器使用宏来定义头文件的名称。

这种方法就是 有条件引用,不用头文件的名称作为 #include

而只需要用宏名称代替即可,因为 SYS_H 会拓展,预处理器会查找 sys_1.h :


#define SYS_H "sys_1.h"
 ...
 #include SYS_H


(SYSTEM_H 可通过 -D 选项被 Makefile 定义)

* 0x06 实践错误记录:#include 展开的问题

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_54

 #include

💭 举个例子:下面我们手动定义了一颗二叉树(Queue.c)


#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"

typedef char BTDataType;
typedef struct BinaryTreeNode {
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDataType data;
} BTNode;

//#include "Queue.h"  解决方案?

/* 创建新节点 */
BTNode* BuyNode(BTDataType x) {
	BTNode* new_node = (BTNode*)malloc(sizeof(BTNode));
	if (new_node == NULL) {
		printf("malloc failed!\n");
		exit(-1);
	}
	new_node->data = x;
	new_node->left = new_node->right = NULL;

	return new_node;
}

/* 手动创建二叉树 */
BTNode* CreateBinaryTree() {
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;

	return nodeA;
}


我们可以看到,我们 #include 引入了 Queue.h 文件,由于是我们的数据类型是 BTNode,

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_55

所以我们需要修改一下 Queue.h 中的 QueueDataType

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef BTNode* QueueDataType;

typedef struct QueueNode {
	struct QueueNode* next;
	QueueDataType data;
} QueueNode;

typedef struct Queue {
	QueueNode* pHead;
	QueueNode* pTail;
} Queue;

void QueueInit(Queue* pQ);                    //队列初始化
void QueueDestroy(Queue* pQ);                 //销毁队列
bool QueueIfEmpty(Queue* pQ);                 //判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x);   //入队
void QueuePop(Queue* pQ);                     //出队
QueueDataType QueueFront(Queue* pQ);          //返回队头数据
QueueDataType QueueBack(Queue* pQ);           //返回队尾数据
int QueueSize(Queue* pQ);                     //求队列大小


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_56

此时令人瞠目结舌的问题就出现了,我们运行代码就会产生报错:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_57


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_58

我们来冷静分析一下这个报错,分析完你会发现分析了个寂寞……

报错说又缺少 " { " 又缺少 " ) " 的,这明显是胡说八道。

我们的编译器在这里就显得没有这么智能了,报的错都开始胡言乱语了。

❓ 思考:这里产生问题的原因是什么呢?(提示:想想编译器的原则)

💡 编译器原则:编译器认识 int,是因为 int 是一个内置类型。但是 BTNode*

就需要 "往上面" 去找这个类型。这里显然往上找,是找不到它的定义的,所以编译器会报错。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_59


如果你要用这个类型,你就需要先定义这个类型。

test.c 文件中 #include "Queue.h"

此时,由于 BTNode* 会在上面展开,导致找不到 BTNode* 

❓ 思考:我把 #include

可以!遗憾的是只能解决这里 typedef BTNode* 的问题,还有 Queue.c 里的问题……

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_60

 那我们该怎么做,才能彻底解决呢? 

🔑 解决方案:使用前置声明。这样就不会带来问题了,满足了先声明后使用。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_61


💬 代码演示:使用 "前置声明" 修改后的 Queue.h


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

// 前置声明
struct BinaryTreeNode;
typedef struct BinaryTreeNode* QueueDataType;

typedef struct QueueNode {
	struct QueueNode* next;
	QueueDataType data;
} QueueNode;

typedef struct Queue {
	QueueNode* pHead;
	QueueNode* pTail;
} Queue;

void QueueInit(Queue* pQ);                    //队列初始化
void QueueDestroy(Queue* pQ);                 //销毁队列
bool QueueIfEmpty(Queue* pQ);                 //判断队列是否为空
void QueuePush(Queue* pQ, QueueDataType x);   //入队
void QueuePop(Queue* pQ);                     //出队
QueueDataType QueueFront(Queue* pQ);          //返回队头数据
QueueDataType QueueBack(Queue* pQ);           //返回队尾数据
int QueueSize(Queue* pQ);                     //求队列大小


此时问题就解决了,头文件展开后不会引发报错问题。

*

"有效避免头文件过多,乱七八糟,不方便管理的问题。"

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_62

如果一个工程中有大量的 .h 文件 和 .c

此时我们可以选择用一个 global.h 的头文件来把所有 .h

在除 global.h 文件外的头文件中包含此文件,这样我们就可以有条不紊地管理好所有的头文件了。


#ifndef _GLOBAL_H
#define _GLOBAL_H

#include <cstdio>
#include <memory>
#include <cstdlib>
#include <random>
#include <cstdio>
#include <utility>
#include <cstring>
#include <vector>
#include <iostream>
#include <functional>
...


*

📃 专栏阅读贴士

当标题前出现 *(星号)时,则说明该部分知识点是面向对 C 语言已经有一定基础的读者阅读的,讲解时难免会穿插不属于本章范围的内容。如果你是一名初学者,可以选择跳过。

对 stdio 有过一些了解的读者可能会知道 stdio.h

对于库中的变量、宏和函数,我们一般称之为 库变量库宏 和 库函数

头文件在 stdio.h 中定义了 3 个变量类型,分别是 FILE,size_t 和 fpos_t


FILE
size_t
fpos_t


  • FILE 类型:存储文件流信息的对象类型(文件章节我们会详细讲解)。
  • size_t 类型:无符号整数类型,为关键字 sizeof 的返回结果。
  • fpos_t 类型:存储文件中任何位置的对象类型。

 库宏 (Libary Micro):以下是 stdio.h 中定义的宏,一共有 16 种,我们先简单介绍一下。


1. NULL


NULL:空指针常量的值。


2. BUFSIZ


BUFSIZ:该宏为整数,代表了 setbuf 函数使用的缓冲区大小。


3. EOF


EOF:文件结束标志 EndOfFile,表示文件结束的负整数,非常常见!后续我们学习 getchar


4. FOPEN_MAX


FOPEN_MAX:该宏为整数,代表了系统可以同时打开的文件数量。


5. FILENAME_MAX


FILENAME_MAX:该宏为整数,代表了字符数组可以存储的文件名最大长度,如果事先没有任何限制,则该值为推荐的最大值。


6. L_tmpnam


L_tmpnam:该宏为整数,代表了字符数组可以存储的由 tmpnam 函数创建的临时文件名的最大长度。


7. _IOFBF
8. _IOLBF
9. _IONBF


拓展了带有特定值的整型常量表达式,适用于 setvbuf 的第三个参数。


10. SEEK_CUR
11. SEEK_END
12. SEEK_SET


在 fseek 函数中使用,用于在一个文件中定位不同的位置。


13. TMP_MAX


tmpnam 函数可生成的独特文件名的最大数量。


14. stderr
15. stdin
16. stdout


FILE

Ⅲ.  main 函数(Main Function)

  • 介绍完头文件后,下面我们将讲解 C 程序中最为重要的 main 函数,掌握 main 函数的特点与特性,探讨 main 函数的各种写法形式,并对这些写法进行说明讲解。

0x00 引入:继续观察 HelloWorld 示例

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_63

 刚才我们讲解了 #include <stdio.h>,下面我们来看第三行代码,int main() 

#include <stdio.h>

int main()
{
	printf("Hello, World!\n");

	return 0;
}


首先,int

* [注] int 是英文单词 integer 的缩写,意思是 "整数"。

而 int 后面跟着的 main 就表示了 main 函数,我们先了解下什么是 函数 (function) :

这里的函数可不是数学中的函数,在 C 语言中,函数是一种可重复使用的代码块,用于执行特定的任务。它是程序的基本组成部分,用于模块化和组织代码,使程序更易于理解、调试和维护。

0x01 什么是 main 函数?

📚 概念:main 函数,又称主函数,每个 C 程序都必须有一个 main 函数。


#include <stdio.h>

int main()       👈 程序从这里执行
{
	return 0;
}


主函数是程序执行的起点,你的程序会从这里开始执行,每次执行程序都从 main 函数开始。

一个工程中可以有多个 .c 文件,但多个 .c

"一个程序不能没有主函数,就像山东不能没有曹县!"

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_64


🔺 铁律:main函数是程序的入口,主函数有且仅有一个!

一个 C 语言程序的执行,从 main 函数开始,到 main 函数结束。 如何理解呢?

你可以把 main 函数想象成入口,一切都从这里开始执行,最后肯定也是要从这出去的。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_65

main 函数的返回类型取决于 main 标识符前是什么数据类型,比如这里是 int:

int main() 
{
    return 0;
}


那么该主函数就存在一个整型类型的返回值,我们需要使用 return 去返回(我们后续再说)。

在我们的演示代码 HelloWorld 中,我们可以看到,main 后面跟了一个括号,

括号中定义了函数的参数类型,而我们演示代码中,括号中是什么都没有的。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_66

 我们可以在括号里面加上一个 void,就像这样:

int main(void)


void 也是数据类型,表示无类型,演示代码中为了演示简单化,我们把括号中的 void

这里 在括号中加上 void 表示函数不接受任何参数, 后面接上的花括号,就是 函数体 了:


#include <stdio.h>        

int main(void)    // 主函数的返回类型是 int,主函数 void 不接受任何参数
{
	printf("Hello, World!\n");

	return 0;     // 由于主函数要求返回整型,这里 return 0
}


C 语言规定,在一个源程序中,main 函数的位置可以任意。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_67

下面我先列举一些大家常见的主函数形式,然后我们再慢慢评价。


int main()
int main(void)



void main()
void main(void)


带参型(带参形式,标准的 main 函数,带两参数):


int main(int argc, char **argv)
int main (int argc, char *argv[], char *envp[])


 超级简单型(存在于 C89 标准中的写法,没有返回值,没有入参):


main()

// Brian W. Kernighan 和 Dennis M. Ritchie 的经典巨著 
// The C programming Language 用的就是 main()。



int mian()


0x02 常规写法: int main() 和 int main(void)

" 约定俗成型,这应该是现在最普遍的写法了。"

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_68

 我们平时写 main 函数的时 main 函数参数一般都不写,即括号里什么都不填。

或者填上一个 void 表示主函数不接受任何参数:


int main()
int main(void)


📌 注意:值得注意的是,int main() 和 int main(void) 这两种写法是存在区别的!

  • int main():参数列表没有被 void 限制死,可以传入参数,程序需要返回值。
  • int main(void):不能传入参数,如果输入参数就会报错,程序必须要有返回值。

这两种写法中,更加建议加 void


int main(void) { /* ... */ }


*

" VS下输入 main 然后回车后就能自动补全的标准写法 "

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_69

该写法也是  C89/C99/C11 标准文档中提供的标准写法,也是很常见的一种写法:

int main(int argc, char *argv[]) { /* ... */ }


参数列表中,argc

argv 是一个指向字符指针数组的指针,用于存储命令行参数的字符串。

  • argv[0]通常是程序的名称或路径。
  • argv[1到 argv[argc-1是传递给程序的其他命令行参数。

💬 代码演示:int main(int argc, char *argv[]) 


#include <stdio.h>

int main(int argc, char *argv[]) 
{
    printf("命令行参数的数量:%d\n", argc);
    printf("第一个参数(程序名称):%s\n", argv[0]);

    for (int i = 1; i < argc; i++) 
    {
        printf("参数 %d: %s\n", i, argv[i]);
    }

    return 0;
}


* 0x04 颇有争议的写法:void main()

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_70

 首先抛出一个事实 —— C/C++ 中从来没有定义过 void main()

C++ 之父本贾尼在他的主页上的 FAQ 中明确地写着 ——

The definition void main() {/*...*/} is not and never has been C++, nor has it even been C.

(void main 从来就不存在于 C++ 或者 C)

void main 这种写法究竟规不规范?这个广为大家所知的 void main


void main()
void main(void)


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_71

光从语法角度上来看,C 语言里是可以编译运行 void main

因为 C 中的函数只关注函数名,而关注其返回类型,在找入口函数时仅仅在找 "main" 而已。

从汇编角度来看,返回值即函数返回时寄存器 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_72

的值,因此

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_72

 中总是有值的。

和变量一样,不主动赋值给的是随机数,因此 void main 即使返回类型是 void,也是有返回值的。

很多人说这种写法都是 "谭C" 带的,事实上并非如此,因为早在上世纪就已然出现了这种写法。

"还有甩锅给谭C的,更是无稽之谈,难道微软和 Borland 的工程师也是跟谭C学的?"

—— 电脑博物馆站长

 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_74

 当然,一些编译器 void main

有人说 void main 其实挺合理的,编写单片机的话你 return

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_75

此时出现了一个非常炸裂的回答:你可以不收,我不能不给。

哈哈哈哈,看来写代码也讲人情世故!return

最后,对 void main 感兴趣的朋友可以看此帖:

🔗 链接:C 语言的「void main」是怎么一代代传下来的? - 知乎

*

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_76

C89 标准中支持这种只丢一个 main() 的写法:

main()


C 语言中是调用者负责清栈任务,因此 main 函数不写返回参数依然是可以的。

你看到有人直接扔一个 main() 但是不报错,事实上也只是编辑器支持而已。

* 0x06 错误写法:mian()

" 因为真的很有趣,所以我不得不提一提。"

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_77

相信很多人都犯过这个错误,不小心把 main 打成了 mian……

int mian(void)


相信很多程序员在职业生涯中都犯过这个错误,看了半天没看出问题到底出在哪里:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_78


编译器只会去找 main,你不小心写成 mian 编译器会说没找到 main 函数的。

🔺 总结:我们只需老老实实地遵循 C 语言标准文档的两种写法,就行了。


int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_79

 可以保障代码的通用性和可移植性,没有 bug,岂不美哉?

* 0x07 main 函数执行前做的事

待更新……

*

 ❓ 思考:用户可不可以调用 main 函数?

这个问题乍一看会让人觉得,不可以吧……main 函数怎么可以让用户调呢: 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_80


很多人都没有想过这个问题,但事实上是可以的,main 函数是可以递归的。

可以是可以,但是调用会一直输出,直至栈溢出(触发 StackOverflow )。

VS 下触发警告:warning C4717: “main”: 如递归所有控件路径,函数将导致运行时堆栈溢出。

💬 代码演示:调用 main()


#include <stdio.h>

int main()
{
	printf("柠檬叶子C\n");
	main();

	return 0;
}


🚩 运行结果如下:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_81


Ⅳ. 深入浅出:Hello,World!

  • 最后,我们讲讲 Hello World 示例程序中剩下来的部分知识点,引出函数体的概念,简单介绍一下 printf 函数,谈论语句结束的标志(分号),最后讲解 return 语句。这些知识点讲完后,我们串联前面讲的内容,就能很清楚地理解 Hello World 了。

0x00 引入:再看 Hello,World!

" 通过这小小的 Hello,World 来展开我们的教学,岂不美哉?"


#include <stdio.h>

int main(void)
{
	printf("Hello, World!\n");

	return 0;
}


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_头文件_82

 我们刚才讲解了头文件和 main 函数,

现在大家应该能理解 #include <stdio.h> 和 int main()

下面我们来细说一下 main() 后面接的花括号 { },所以我们先要介绍一下函数体的概念。

再介绍一下 printf 函数,以及 return

0x01 函数体的概念(花括号匹配原则)

📚 概念:定义一个函数功能的所有代码组成的整体,称为 函数体 (Function Body) 。

函数体是用花括号括起来的若干语句,他们完成了一个函数的具体功能。

(函数声明与函数体组成函数定义,我们会在函数章节详细讲解)

💭 举个例子:比如下面的 main 函数下面跟着一对花括号,花括号内的内容就是函数体了。


#include <stdio.h>

int main(void)
{
	printf("Hello, World!\n");

	return 0;
}


⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_83

一对花括号 { } 由 左花括号 {  和 右花括号 } 组成,左花括号匹配右花括号。

比如这里的 main 函数,其 函数体包括左花括号和与之匹配的右花括号之间的任何内容

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_84


0x02 简单介绍一下 printf 函数


printf("Hello, World!\n");


printf 函数,该函数是 stdio.h 中的库函数,因此在使用前需要使用 #include 引入 stdio.h

这里我们就简单介绍一下,printf

双引号内的内容可以输出到 控制台,所以我们的 Hello,World 示例程序运行后控制台会显示:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_35


所以,你可以把它叫做 "打印函数",通过 printf 函数将 Hello,World 打印到了控制台上:


printf("需要打印到控制台的内容");


❓ 这里的 \n

因为 \n 是转义字符,用于换行,关于转义字符的知识我们会放到下一章进行系统性讲解。

0x03 分号,语句结束的标志!

 

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_86

 在我们日常生活中, "句号" 表示一段话的结束。

而在 C 语言中,语句的结束用 ;

可以看到,我们的 printf() 函数后面就有一个 分号 ;


printf("Hello,World\n");   👈 加分号
return 0;    👈 加分号


当一条语句结束时,我们需要手动给它加上分号,表明该语句结束。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_87


不加分号会报错,人难免是会忘的,如果忘记加了编译器会提示:

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_88


随着敲代码越来越熟练,忘记加分号的情况会越来越少的,就像你写作文不会忘记加句号一样。

遗漏分号会引发 C2143 号报错:error C2143: 语法错误: 缺少“;” 

当然了,也不能在不该加分号的地方加分号!

如果你在 main() 后面直接加分号,那就表示语句直接结束了,后面的 { } 就会让编译器一脸懵……

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_原力计划_89


所以,分号是初学者最容易犯错的地方,多用了会错少用了也会错。

但是不用担心,在后续学习过程中慢慢就会掌握了。(比如 do...while 语句 while 结尾要加分号)

0x04 返回语句 return 

我们前面说了,因为我们的 main 函数的返回值类型定义的是 int,所以我们需要 return 一个值:


#include <stdio.h>

int main(void)
{
	printf("Hello, World!\n");

	return 0;
}


📚 概念:在C 语言中,return 用于表示函数的返回值,用来将函数的结果返回给调用它的程序。


return 返回值;


因此,这里我们 return 一个 0,表示程序正常退出。

*

值得一提的是,return


return 表达式;    ✅ 常规写法
return (表达式);  ✅ 也是可以的


但是为了简单起见,一般我们不去写,当然写了也不会错,这取决于个人的编程习惯。

比如下面这两行实际效果是一样的:


return 10 + 20;
return (10 + 20);


我见过一些老的微软大佬写的代码里,return


return( 0);     // 括号直接顶着return


*库函数 src/strlen.c


size_t __cdecl strlen (
        const char * str
        )
{
        const char *eos = str;
 
        while( *eos++ ) ;
 
        return( eos - str - 1 );    👈 表达式
}


0x06 深入浅出:Hello,World!

现在,我们已经解释完所有 Hello,World 的细节了,并且把其中的知识点都讲的差不多了。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_#include_90

 现在我们再回到最初的起点,再次感受那个跨越历史的时刻……

#include <stdio.h>

int main(void)
{
	printf("Hello, World!\n");

	return 0;
}


经过本章的学习后,你也应该会有一个大概的认识了。

我们通过这 Hello World 来展开了我们的教学专栏,介绍了头文件、主函数和一些基础概念。

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_main函数_91

 这样的开篇方式我们构思了很久很久,希望能得到大家的喜爱!

C 语言之父 丹尼斯里奇 的曾言:

" C 诡异离奇,缺陷重重,却获得了巨大的成功。"

C 语言无疑是改变人类历史的最伟大发明之一……

在计算机蓬勃发展的今天,我们是站在了巨人的肩膀上,得以让我们看得更高,行的更远……

学习 C 语言的过程是漫长且枯燥的,一定要有一个非常强大的心理状态。

👑 翁凯老师说:在计算机里头没有任何黑魔法!

学计算机一定要有一个非常强大的心理状态,计算机的所有东西都是人做出来的,别人能想的出来,我也一定能想得出来,在计算机的世界里没有任何的黑魔法,所有的东西只不过是我现在不知道而已,总有一天我会把所有的细节、所有的内部的东西全搞明白的。

尾记:如何学习一门编程语言?

⚡【C语言趣味教程】(1):深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解_c语言_92


我的编程母语是易语言(一款中文编程语言),我的编程启蒙导师是觅风。

觅风老师在他的 《易语言视频教程》中说过:

" 兴趣是最好的老师。"

如果你对编程感兴趣,那么这条路及时很艰难,你也会有无穷无尽的力量。

其次就是多动手,代码眼看千遍不如手敲一遍,多动手去实操,毕竟:

" 纸上得来终觉浅,绝知此事要躬行。"

然后就是多看源码,看看高手是怎么写代码的,大有裨益。