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

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

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

  • 💭 写在前面:这是一套 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 的由来

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

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

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

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

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

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

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

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

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

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

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

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

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

Hello World 究竟从何而来?

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

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

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

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

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

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

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

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

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

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

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

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

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

学习阶段我们使用免费的 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 的安装教程的。

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

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

0x02 创建新项目

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

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

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

 然后点击 空项目

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

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

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

​ 这里的 .c 文件 和 .h 以及 VS 默认的 .cpp 后缀是什么?我们来简单介绍一下:

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

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

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

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

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

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

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

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

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

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

#include <stdio.h>int main()
{printf("Hello, World!\n");return 0;
}

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

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

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

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

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

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

#include <stdio.h>int main()
{printf("Hello, World!\n");  👈 打印 Hello,Worldreturn 0;
}

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

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

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

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

#include <stdio.h>int main()
{printf("Hello, World!\n");  return 0;
}

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

Ⅱ. 头文件(Header)

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

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

​下面是 HelloWorld 示例程序的第一行代码:

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

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

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

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

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

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

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

关于头文件的一些细节


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

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

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

我们使用 #include 来引用头文件,这是一个 C 预处理指令,对于预处理的知识我们后期会讲解。

#include         引入头文件

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

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

0x01 标准输入输出库 stdio

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

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

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

​ 之所以叫做 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 引入头文件的方式

​ 我们前面讲了,我们使用 #include 来引用头文件,这个 # 是预处理指令。

预处理指令 # 可用于宏定义、文件包含 和 条件编译。

刚才我们介绍了 stdio.h 库,现在我们再来观察 HelloWorld 示例程序的第一行代码:

#include <stdio.h>

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

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

这行代码中,我们通过 #include <> 去引入了标准输入输出库 stdio。

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

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

​首先,< >" " 包含头文件的本质区别是查找策略区别。

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

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

双引号 " " 的查找策略:先在源文件所在的目录下查找。如果该头文件未找到,则在库函数的头文件目录下查找。如果仍然找不到,则提示编译错误。用来引用非标准库的头文件。

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

❓ 思考:那可不可以用双引号 " " 包含库文件?

#include "stdio.h"

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

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

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

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

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

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

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

/usr/include

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

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

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

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

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

VS 下,在解决方案资源管理器中:

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

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

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

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

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

​ 比如下面这个代码,仔细观察你会发现不小心引入了两次 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 块内;

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

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

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

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

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

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

* 0x05 条件引用技术

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

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

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

#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 展开的问题

​ #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

​所以我们需要修改一下 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);                     //求队列大小

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

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

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

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

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

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

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

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

test.c 文件中 #include "Queue.h" ,相当于把这里的代码拷贝过去了。

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

❓ 思考:我把 #include 移到 定义类型的代码 的后面,可以解决问题吗?

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

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

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

💬 代码演示:使用 "前置声明" 修改后的 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);                     //求队列大小

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

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

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

​如果一个工程中有大量的 .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>
...

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

📃 专栏阅读贴士

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

对 stdio 有过一些了解的读者可能会知道 stdio.h 定义了 3 个变量类型,以及一些宏和各种函数。

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

库变量 (Library Variable):

头文件在 stdio.h 中定义了 3 个变量类型,分别是 FILEsize_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 类型的指针,标准错误、标准输出和标准输出流。

库函数 (Libary Funciton):共有 42 种函数,我们会在后续章节将一些常用的函数进行介绍。

Ⅲ.  main 函数(Main Function)

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

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

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

#include <stdio.h>int main()
{printf("Hello, World!\n");return 0;
}

首先,int 表示整型,这里表示 main 函数的返回值为整型。(下一章我们会讲解数据类型)

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

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

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

0x01 什么是 main 函数?

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

#include <stdio.h>int main()       👈 程序从这里执行
{return 0;
}

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

一个工程中可以有多个 .c 文件,但多个 .c 文件中只能有一个 main 函数,main 函数具有唯一性。

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

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

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

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

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

int main() 
{return 0;
}

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

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

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

​ 我们可以在括号里面加上一个 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 函数的位置可以任意。

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

约定俗成型(目前主流的写法):

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()。

直接报错型(相信 90% 的程序员都犯过这个错误):

int mian()

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

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

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

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

int main()
int main(void)

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

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

这两种写法中,更加建议加 void 的写法,因为是 C89/C99/C11 标准文档中提供的写法之一:

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

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

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

​该写法也是  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/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 语言里是可以编译运行 void main 的。

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

从汇编角度来看,返回值即函数返回时寄存器 EAX 的值,因此 EAX 中总是有值的。

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

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

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

—— 电脑博物馆站长

 ​ 当然,一些编译器 void main 是可通过编译的,比如 VC6.0 

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

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

哈哈哈哈,看来写代码也讲人情世故!return 什么无所谓,我就是要个态度 ~ 

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

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

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

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

main()

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

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

* 0x06 错误写法:mian()

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

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

int mian(void)    

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

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

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

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

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

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

待更新……

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

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

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

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

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

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

💬 代码演示:调用 main()

#include <stdio.h>int main()
{printf("柠檬叶子C\n");main();return 0;
}

🚩 运行结果如下:

Ⅳ. 深入浅出: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;
}

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

现在大家应该能理解 #include <stdio.h>int main() 的含义了。

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

再介绍一下 printf 函数,以及 return 语句,我们的 Hello World 示例程序就可以 "浅出" 了。

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

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

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

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

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

#include <stdio.h>int main(void)
{printf("Hello, World!\n");return 0;
}

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

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

0x02 简单介绍一下 printf 函数

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

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

这里我们就简单介绍一下,printf 后面跟着 () ,括号中的内容用双引号括出,

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

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

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

❓ 这里的 \n 为什么没有被打印出来?

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

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

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

而在 C 语言中,语句的结束用 ; 表示,分号是语句结束的标志,表示一条语句的结束。

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

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

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

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

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

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

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

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

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

但是不用担心,在后续学习过程中慢慢就会掌握了。(比如 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,表示程序正常退出。

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

值得一提的是,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 的细节了,并且把其中的知识点都讲的差不多了。

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

#include <stdio.h>int main(void)
{printf("Hello, World!\n");return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

" 兴趣是最好的老师。"

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

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

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

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

最后,遇到问题要深入了解,要具备对编程知识的求知欲,对问题要有钻研精神。

  • 网制焦,实不严,没毕业,零经验,面八家,十offer,三万二,外包也,两万七,自研也,明日有,多面试。说寒冬,年年有,计算机,月过万,现不学,没机会,趁红利,狠赚笔。
📌 [ 笔者 ]   王亦优 | 雷向明
📃 [ 更新 ]   2023.6.29(初稿)| 2023.7.1(审稿结束)
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

📜 参考文献:

  • - C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.
  • - Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .
  • - 百度百科[EB/OL]. []. https://baike.baidu.com/.
  • - 维基百科[EB/OL]. []. https://zh.wikipedia.org/wiki/Wikipedia
  • - R. Neapolitan, Foundations of Algorithms (5th ed.), Jones & Bartlett, 2015.
  • - B. 比特科技. C/C++[EB/OL]. 2021[2021.8.31]
  • - 林锐博士. 《高质量C/C++编程指南》[M]. 1.0. 电子工业, 2001.7.24.
  • - 陈正冲. 《C语言深度解剖》[M]. 第三版. 北京航空航天大学出版社, 2019.
  • - 侯捷. 《STL源码剖析》[M]. 华中科技大学出版社, 2002.
  • - T. Cormen《算法导论》(第三版),麻省理工学院出版社,2009年。
  • - T. Roughgarden, Algorithms Illuminated, Part 1~3, Soundlikeyourself Publishing, 2018.
  • - J. Kleinberg&E. Tardos, Algorithm Design, Addison Wesley, 2005.
  • - R. Sedgewick&K. Wayne,《算法》(第四版),Addison-Wesley,2011
  • - S. Dasgupta,《算法》,McGraw-Hill教育出版社,2006。
  • - S. Baase&A. Van Gelder, Computer Algorithms: 设计与分析简介》,Addison Wesley,2000。
  • - E. Horowitz,《C语言中的数据结构基础》,计算机科学出版社,1993
  • - S. Skiena, The Algorithm Design Manual (2nd ed.), Springer, 2008.
  • - A. Aho, J. Hopcroft, and J. Ullman, Design and Analysis of Algorithms, Addison-Wesley, 1974.
  • - M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997.
  • - A. Levitin, Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003. - A. Aho, J. 
  • - E. Horowitz, S. Sahni and S. Rajasekaran, Computer Algorithms/C++, Computer Science Press, 1997.
  • - R. Sedgewick, Algorithms in C: 第1-4部分(第三版),Addison-Wesley,1998
  • - R. Sedgewick,《C语言中的算法》。第5部分(第3版),Addison-Wesley,2002

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/18842.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正确保护Macbook

MacBook该如何正确保护呢&#xff1f;不是各种键盘膜、保护壳通通用上就是最好的&#xff0c;那么该如何正确做呢&#xff1f;下面是macw小编带来的详细指导&#xff0c;快来学习&#xff01; 在接下来的文章中&#xff0c;笔者将展示哪些配件是可取的&#xff0c;哪些配件是坚…

从做产品的角度分析吕布为什么非死不可?

这是一篇小品文&#xff0c;作者是“产品家实战营3期”学员…… 马中赤兔&#xff0c;人中吕布&#xff0c;本意虽褒&#xff0c;但个人觉得将人与牲口类比&#xff0c;其段位貌似也没高到哪里&#xff0c;&#xff1a;&#xff09; 不过说起三国里的武将武力排名&#xff0c;吕…

中国撸串指北:13万家烧烤店的吃货最爱

戳蓝字“CSDN云计算”关注我们哦&#xff01; 数据分析&#xff1a;还是更爱火锅的朱小五 内容撰写&#xff1a;最爱干豆腐卷的王小九 来源|凹凸数读 对美食最大的肯定无疑就是那操着不同口音说出的“好吃&#xff01;”二字。 ——《人生一串》豆瓣短评 以美食慰藉夜归人&…

Github上这几个沙雕项目,够我玩三天!

点击上方“码农突围”&#xff0c;马上关注 这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包 真爱&#xff0c;请设置“星标”或点个“在看” 开源最前线&#xff08;ID&#xff1a;OpenSourceTop&#xff09; 猿妹综合整理 今天&#xff0c;猿妹再…

几个有趣的Github项目,够你玩一阵了...

点击上方“后端技术精选”&#xff0c;选择“置顶公众号” 技术文章第一时间送达&#xff01; 来源&#xff1a;开源最前线 今天&#xff0c;给大家整理一份有意思的沙雕项目&#xff0c;顺带分享了我的试用成果&#xff0c;说实话&#xff0c;这些项目够你玩三天了。 亲戚关系…

包浆网图分分钟变高清,伪影去除、细节恢复更胜前辈AI,下载可玩|腾讯ARC实验室出品...

丰色 发自 凹非寺量子位 报道 | 公众号 QbitAI 下面来欣赏一些高糊图片“整个世界都清晰了”的魔法时刻&#xff1a; 无论是动漫还是真实图像&#xff0c;是不是都清晰还原了&#xff1f; 以上就是由腾讯ARC实验室最新发表的图像超分辨率模型完成的。 与前人工作相比&#xff0…

爬虫入门实践 | 利用python爬取彩票中奖信息

系统环境&#xff1a;mac python版本&#xff1a;3.6.2(anaconda) 库&#xff1a;requests、BeautifulSoup 爬取一些简单的静态网站&#xff0c;一般采取的策略为&#xff1a;选中目标&#xff0c;也就是需要爬取的网站url&#xff1b;观察结构&#xff0c;查看网页结构&…

全网超详细的下载与安装VMware虚拟机以及为什么要安装VMware虚拟机

文章目录 1. 文章引言2. 下载VMware3. 安装VMware 1. 文章引言 我们使用最多的系统是windows系统&#xff0c;因为&#xff0c;国内电脑厂商的操作系统(os)基本是windows系统&#xff0c;比如华为、联想、华硕等电脑。 但线上的服务器大多是Linux系统&#xff0c;而我们经常使…

图灵奖得主LeCun:ChatGPT局限巨大,自回归模型寿命不超5年

作者 | 新智元 编辑 | 新智元 点击下方卡片&#xff0c;关注“自动驾驶之心”公众号 ADAS巨卷干货&#xff0c;即可获取 【导读】图灵奖得主Yann LeCun畅谈AI&#xff1a;未来是开源。 今年上半年&#xff0c;可谓是AI届最波澜壮阔的半年。 在急速发展的各类GPT甚至AGI的雏形背…

LeCun畅谈:ChatGPT局限巨大,自回归模型寿命不超5年

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【计算机视觉】微信技术交流群 转载自&#xff1a;新智元 | 编辑&#xff1a;拉燕 【导读】图灵奖得主Yann LeCun畅谈AI&#xff1a;未来是开源。 今年上半年&#xff0c;可谓…

ChatGPT正在改变一切但仍然有其局限性

人工智能聊天机器人已经被证明非常有能力完成技术任务&#xff0c;例如编写和编码。但它还不能做所有的事情。 自11月下旬发布以来&#xff0c;ChatGPT已经席卷全球。这款聊天机器人的高级人工智能能力允许它完全独立完成任务&#xff0c;如撰写论文、电子邮件和诗歌、编写和调…

从集异壁理解ChatGPT的成功与局限

终其一生&#xff0c;人类都在探寻认知这个世界的方式。 音乐、绘画和人工智能是三个看似无关的领域&#xff0c;但是它们都是人类这次伟大尝试的绚烂明珠。在这三个领域&#xff0c;追根溯源&#xff0c;底层的结构&#xff0c;都简洁且美丽。 图片由Midjourney生成&#xff0…

GPT虚拟直播Demo系列(二)|无人直播间实现虚拟人回复粉丝

摘要 虚拟人和数字人是人工智能技术在现实生活中的具体应用&#xff0c;它们可以为人们的生活和工作带来便利和创新。在直播间场景里&#xff0c;虚拟人和数字人可用于直播主播、智能客服、营销推广等。接入GPT的虚拟人像是加了超强buff&#xff0c;具备更强大的自然语言处理能…

今晚 12:30 RLHF: From Zero to ChatGPT 直播活动

本次演讲&#xff0c;我们将介绍一种称之为从人类反馈中强化学习 (RLHF, Reinforcement Learning from Human Feedback) 的基础知识&#xff0c;以及如何使用 RLHF 驱动实现 ChatGPT 这样的工具。我们将为大家介绍相关联的机器学习模型&#xff0c;涵盖自然语言处理 (NLP) 和强…

2-24财财的财经早报!

早评0224&#xff1a;震荡依旧 【市场判断】 外围方面&#xff0c;美股三大指数收涨&#xff0c;道指涨0.33%&#xff0c;纳指涨0.72%&#xff0c;标普涨0.53%。消息面上,美联储官员在会议纪要中重申了其强硬的立场——与会者都同意在通胀得到实质控制之前继续加息步伐。芝商所…

一个90后关于ZG足球的思考【卡塔尔世界杯】

前言 一个月的时间过得还是非常快的&#xff0c;卡塔尔世界杯马上也要迎来半决赛和决赛。自己一直想动手写写自己关于足球的思考&#xff0c;特别是ZG足球&#xff0c;说得再准确一点&#xff0c;就是ZG男足。 正文 第一次认识足球运动 作为一名90后&#xff0c;自己开始认…

数据可视化之中国足球队在国际足联及亚洲的历史排名看这儿

最近最热的体育盛事莫过于世界杯了&#xff0c;四年一届的足球盛事&#xff0c;正在卡塔尔激战正酣。这时候可能会有球迷疑问&#xff1a;怎么没看到中国队呢&#xff1f;也有网友调侃说中国队被分到工程队去了。 泱泱大国&#xff0c;难道中国十几亿人口&#xff0c;一个足球队…

【花雕学AI】我们如何才能避免被ChatGPT替代?——一个跨学科的视角

ChatGPT是一个由OpenAI开发的AI文本工具&#xff0c;它可以理解和生成自然语言&#xff0c;从而与用户进行对话。ChatGPT是基于GPT-3或者GPT-4模型的&#xff0c;这是目前最大和最先进的语言模型之一。ChatGPT通过在大量的互联网文本数据上进行预训练和强化学习&#xff0c;学习…

开箱即用,ChatGPT 复现方案开源

点击上方“Java基基”&#xff0c;选择“设为星标” 做积极的人&#xff0c;而不是积极废人&#xff01; 每天 14:00 更新文章&#xff0c;每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应…