对于大多数编程语言来说,我们第一个编写的程序都是一个打印 hello world 的代码,其本质是让编程语言与系统交互,在控制台中打印字符串。

让我们来看看C语言中,想要在控制台里面输出内容,应该怎么做吧!

1. hello world源码

对于大部分编程语言初学者而言,这是最熟悉的代码了。

1
2
3
4
5
6
7
#include <stdio.h> 

int main()
{
printf("hello world\n");
return 0;
}

2. 第一次运行代码

写到 VS2019 中后,我们使用键盘上的CTRL+S快捷键保存代码,再按 CTRL+F5 就可以运行代码了。

部分笔记本电脑上,F开头的功能键默认按下是调节亮度/声音等一些快捷功能,并不是F键本身的功能。这时候你需要使用FN+F功能键才能使用出F功能键原本的功能;比如CTRL+FN+F5来运行代码。

很多笔记本都可以用FN+ESC来锁定FN键,让F功能键恢复原本功能(不需要按FN),而调节亮度/声音等快捷控制方式变回FN+F功能键;如果你的笔记本不是用FN+ESC来锁定FN键,那可以百度一下你这台型号/品牌的笔记本如何锁定FN键(应该都有这个功能吧?没有就算了)

对于编程学习者来说,更建议你锁定FN键,避免每次 CTRL+F5 都得带上FN。

运行代码后,VS2019会执行编译流程,在项目路径生成一个可执行文件,并直接执行这个文件。看到的效果就是一个黑框(windows下的终端)弹出,显示出了我们代码的结果。即"hello world\n"字符串被打印到了终端上。

除了使用快捷键CTRL+F5运行代码,你还可以在 VS2019 的界面上方,点击调试-开始执行(不调试),后续的文档中将会解释什么是调试。现在你只需要知道这里也可以执行代码就行了。

3. 代码逐行解释

下面让我来逐行解释一下这份代码吧!

1
2
3
4
5
6
7
#include <stdio.h> // 1.printf库函数需要使用的头文件

int main() // 2.入口函数
{
printf("hello world\n"); // 3.调用printf函数,打印字符串
return 0; // 4.return是返回值并退出函数
}

先说明一下,C语言中,每一行代码后都需要跟着一个英文的分号;作为这一行代码的结束标记。一定不要忘记这个分号,也一定不要在不对的地方加上这个分号(不然就会出一些很难找的bug)

3.1. 函数的基本格式和main函数

在C/C++中,main函数是作为函数的入口,程序是从main函数开始运行的。

C语言中,函数的基本格式如下

1
2
3
4
5
函数返回值 函数名(函数参数类型 函数参数1, 函数参数类型 函数参数2, ...)
{
函数体
return 返回值(需要和函数返回值的类型对应)
}

对应的,main函数是C语言中程序的入口,长下面这样。

1
2
3
4
5
int main()
{
// 函数体
return 0; // 返回值是0
}

在这个main函数中:

  • 函数的返回值是int(整数类型)
  • main()括号内为空,代表main函数没有参数
  • 函数体自行编写
  • return 0;代表main函数的返回值是0

运行程序的时候,编译器会自动找到你的main函数,开始执行这个代码。这也是为什么我们的main函数可以不写参数。

而其他函数,都需要我们手动调用。

关于main函数的参数和返回值,还有很多可以聊的。后续的学习中再深入学习吧。

3.2. 函数注释

在C/C++中,函数注释是以//开头,在这之后的文字都是函数注释,和实际代码没有任何关系。

所谓注释,就是对函数这部分功能的描述,或函数参数的描述。避免其他人看不懂你代码的意图,亦或者是自己以后回来也看不明白自己写的是啥了。

虽然网上有段子说:“最讨厌不写注释的人,和让我写注释的人”,但实际上,该写的注释还是得写的。段子图一乐就行了。

对于初学者来说,多写注释能让后续你复习代码的时候,明确这部分学习的内容。注释并不需要做到行行都有,但是基本流程还是要写上的。

3.3. #include

在上面的打印代码中,第一行如下

1
#include <stdio.h>

这里的#include的作用是引用一个头文件,头文件一般以.h结尾,<>中的便是头文件的文件名。

你可以理解为,头文件是另外一个包含C语言代码的文本文件。

当我们引用了这个头文件后,这个头文件的内容会在当前文件被展开

假设我们将下面的代码写入到Add.h文件

1
2
3
4
int Add(int a,int b)
{
return a+b;
}

再在test.c文件中写如下代码

1
2
3
4
5
6
7
#include "Add.h" // 引用我们自己的头文件

int main()
{
int num = Add(1,3);
return 0;
}

最终,编译器会在预编译阶段(这是编译的阶段之一)展开Add.h头文件,整个代码会自动变成如下模样。即#include "Add.h"被替换成了Add.h头文件中的内容。

1
2
3
4
5
6
7
8
9
10
int Add(int a,int b)
{
return a+b;
}

int main()
{
int num = Add(1,3);
return 0;
}

3.3.1. include中<>和””的区别

细心的你应该发现了,#include后面出现了两种不同的符号,分别是<>"";区别如下

  • <> 只能引用官方的头文件,比如stdio.h就是C语言本身提供的头文件;
  • "" 引用我们自己写的头文件,比如上文中的Add.h就是我们自己写的一个头文件。

""既可以引用官方的头文件,又可以引用自己写的头文件。它的查找逻辑不同,假设我们有如下的一个文件路径:

1
2
3
测试代码(文件夹)
- Add.h
- test.c

依照上面的代码,我们运行代码是从test.c开始的,执行到第一行的#include "Add.h"时,因为我们使用的是"",所以编译器会执行如下流程:

  • 从当前test.c文件夹所在的路径,开始找这个Add.h,找到了,就将头文件中的内容展开(本例子中存在Add.h头文件,所以可以正常找到头文件并展开);
  • 如果找不到,就会去编程语言的安装路径中,找C语言官方提供的头文件;
  • 如果还找不到,代表这个头文件不存在,报错退出编译流程,编译失败。

而如果使用<>引用头文件,则只会去编程语言的安装路径中找官方提供的头文件。找不到,则编译失败。比如我们用#include <Add.h>来引用上文我们自己写的头文件,就会编译出错。

现在讲这部分,可能会有些抽象。你可以只记住两者的区别即可,最终头文件的引用这件事,还会在学习Linux的时候再谈,到时候就能给大家演示二者区别的实际例子,更方便理解。

在日常编写代码中,我们应该做好区分,这也是代码规范的一部分:

  • C语言内置的头文件,使用<>来引用,告知别人这是内置的头文件;
  • 自己编写的头文件,使用""来引用,告知别人这是我们自己写的头文件;

保持良好的代码规范,是我们学习编程中不可或缺的一部分。

3.4. printf函数

3.4.1. 善用cplusplus.com文档网站

cplusplus.com这个网站的作用是查找一些C/C++库函数的定义和用法,现在就以printf函数为例,带大家来用用这个网站,并学习printf函数的使用。

进入网站后,在最上方的搜索框查找printf函数

https://legacy.cplusplus.com/reference/cstdio/printf/?kw=printf

cplusplus.com的结果都是全英文的,但不要有畏难情绪,基本的介绍是不难读懂的。实在不行我们可以用翻译软件嘛!

下图中标注出了该网站搜索到的结果中的基本格式。包含函数的定义、说明、函数参数、返回值,最底部还有函数的使用示例

3.4.2. 函数声明

printf的函数的声明如下

1
int printf ( const char * format, ... );

3.4.2.1. 参数

依照前文提到的函数的基本格式进行解读:

  • 返回值是int
  • 函数名是printf
  • 函数的第一个参数是const char *类型,参数名为format,意义是函数的打印格式
  • 函数的后续参数用了...,这一点就比较深入了,是C语言中支持的可变参数列表(文档很后面才会深入介绍这个)

printf函数会根据我们设置的第一个参数打印格式,将后续传入的可变参数列表中的参数,以我们传入的打印格式写入到stdout中;

stdout是C语言的标准输出流,在默认情况下,就是往控制台中输出。

3.4.2.2. 返回值

在网站中,对返回值的介绍如下

text
1
2
3
4
5
On success, the total number of characters written is returned.

If a writing error occurs, the error indicator (ferror) is set and a negative number is returned.

If a multibyte character encoding error occurs while writing wide characters, errno is set to EILSEQ and a negative number is returned.

翻译过来是这样的:

  • 函数执行成功,会返回成功打印了多少个字符
  • 函数执行失败,会返回一个负数(更多的暂时不需要了解)

3.4.2.3. 调用示例

下面是几个printf的调用示例

1
2
3
4
5
6
7
8
9
printf("%d",10); // 打印整形 (int)
printf("%x",10); // 以十六进制打印整形 (int)
printf("%lld",100000000000000); // 打印长整形 (long long)
printf("%a",'C'); // 打印单个字符 (char)
printf("hello world"); // 直接打印字符串 (char *)
printf("%s","hello world"); // 通过%s打印字符串 (char *)
printf("%f",3.14); // 打印浮点数,即分数和小数 (float/double)
int ptr = 30; // 定义一个int类型的变量ptr
printf("%p",&ptr); // 打印地址(需要打印的是指针,后续会讲解指针类型)

更多的格式化控制方式,可以在cplusplus.com的参数介绍中找到,常用的上面基本都提到了。

后续会在输入输出操作的时候,更详细地介绍这个函数。

4. 结语

看到这里,想毕你已经对我们刚刚编写的第一个C语言程序,有基本的理解啦!