计算机编程语言有很多种,每种语言都有其独特的特点和用途。
Python需要运行一个解释器——人输入一句,它就执行一句,这叫交互模式;也可以一次性写好大量语句,打包交给解释器统一执行,这就是脚本模式(批量处理)。
而C语言则必须先将源代码编译成二进制可执行文件,然后才能运行,只有这一种运行方式。从运行机制来看,Python属于解释型语言,C语言则是编译型语言。Java又有所不同:它先被编译成字节码,再由Java虚拟机(JVM)解释执行。可以粗略理解为“先编译、后解释”。由于JVM是跨平台的,因此Java程序可以在不同操作系统上运行。
C语言直接编译为机器码,没有解释器或虚拟机带来的额外开销,理论上性能优于解释型语言。但在某些场景下,开发效率比极致性能更重要——此时Python或Java可能更合适。例如,Python广泛用于数据分析与AI模型调用,Java则常见于企业级应用开发。
世上没有“完美”的语言,只有不断演进的语言,以及更适合特定场景的语言。近年来兴起的Rust和Golang都是高性能的代表:Rust兼顾内存安全与性能,但学习曲线陡峭;Golang并发能力强、性能优秀,但在业务逻辑开发上的体验未必优于Java或Python。
未来大家可能会接触多种语言,应避免成为某一种语言的“信徒”或“粉丝”,而要以工具使用者的心态,在合适的场景选择合适的语言。
无论是开车、骑车、跑步,还是坐船、乘飞机,达成目标才是核心。下楼买菜步行即可,无需高铁;跨国贸易用飞机更高效;日常通勤骑自行车或许最舒服。所有工具都应服务于“以合理成本达成目的”,而非让人陷入盲目崇拜——比如无论去哪儿都说“高铁天下第一”,那就本末倒置了。
C标准库简介
然后说C语言的诞生吧。
C语言的诞生过程和Unix操作系统的诞生过程密切相关。
其实最早的计算机,简单来说和现在的计算器差不多,甚至很多年前的各种证券投资行业之类的都要学习复杂的计算器如何使用,那时候就是有两种方式,一个是一句一句输入算式来计算,这就像是上面的交互模式;另一种就是一下子给好方程边界条件等等,全都输入好,让计算机彻底计算完,这就像是上面的批量处理模式。
后来就有了一些结果保存的需求,比如中间计算的某个中间变量,如果不保存,下一次可能这个步骤还要重算,这就很不划算,所以就有了变量的概念,用来保存中间结果。
逐渐按照这个思路,有了更复杂的控制结构,比如循环、条件判断等。然后计算的结果也要存储了,所以就有了各种数据类型。
再后来,有限的内存范围不够用了,所以就有了指针的概念,用来指向内存中的某个地址,从而可以访问到那个地址上的数据,然后形成链表之类的复杂数据结构。
后来数据越来越多也越来越复杂,就需要有文件系统了,然后还有一些设备接入的支持,再加上命令行接口,这个过程中,Unix操作系统和C语言就这么着差不多一起诞生了。
C语言最开始的发展过程中,大家就都会发现有很多时候,"重新发明轮子"是非常麻烦的事情,要是有一些常用的基础的函数,能够有一套统一标准,就非常方便,于是就逐渐有了C标准函数库。
| 头文件 | C标准版本 | 描述 |
|---|---|---|
<assert.h> |
包含断言,被用来在程序的调试版本中帮助检测逻辑错误以及其他类型的bug。 | |
<complex.h> |
C99 | 一组操作复数的函数。 |
<ctype.h> |
定义了一组函数,用来根据类型来给字符分类,或者进行大小写转换,而不关心所使用的字符集(通常是ASCII或其扩展字符集,也有EBCDIC)。 | |
<errno.h> |
用来测试由库函数报的错误代码。 | |
<fenv.h> |
C99 | 定义了一组用来控制浮点数环境的函数。 |
<float.h> |
定义了用于浮点数库特定实现的宏常量。 | |
<inttypes.h> |
C99 | 定义精确的宽度整数类型。 |
<iso646.h> |
NA1 | 定义几个等效于C中某些运算符的宏。用于使用ISO 646变体字符集进行编程。 |
<limits.h> |
定义了用于整数库特定实现属性的宏常量。 | |
<locale.h> |
定义C语言本地化函数。 | |
<math.h> |
定义C语言数学函数。 | |
<setjmp.h> |
定义了巨集setjmp和longjmp,在非局部跳转的时候使用。 | |
<signal.h> |
定义C语言信号处理函数。 | |
<stdalign.h> |
C11 | 用于查询和指定对象的数据结构对齐方式。 |
<stdarg.h> |
用于处理可变参数列表的函数。 | |
<stdatomic.h> |
C11 | 用于原子操作和线程同步的函数和类型。 |
<stdbool.h> |
C99 | 定义布尔数据类型。 |
<stddef.h> |
定义了几个常见的类型与巨集。 | |
<stdint.h> |
C99 | 定义精确的宽度整数类型。 |
<stdio.h> |
定义输入输出函数。 | |
<stdlib.h> |
定义数值转换函数,伪随机数生成函数,动态内存分配函数,过程控制函数。 | |
<stdnoreturn.h> |
C11 | 用于指定非返回函数。 |
<string.h> |
定义C语言字符串处理函数。 | |
<tgmath.h> |
C99 | 定义类型通用数学函数。 |
<threads.h> |
C11 | 定义用于管理多个执行线程以及互斥体和条件变量的函数。 |
<time.h> |
定义日期和时间处理函数。 | |
<uchar.h> |
C11 | 用于操作Unicode字符的类型和函数。 |
<wchar.h> |
NA1 | 定义宽字符串处理函数。 |
<wctype.h> |
NA1 | 定义一组函数,用于按类型对宽字符进行分类或在大小写之间进行转换。 |
POSIX标准与C语言
最开始的时候,Unix系统和C语言要面对一些问题,比如处理器一换了,硬件指令可能就有变化了,然后Unix也有不同分支,之间也有差异,做跨平台的支持就麻烦了。
于是就有了一个可移植操作系统接口(英语:Portable Operating System Interface,缩写为POSIX),这是IEEE为要在各种类UNIX操作系统上运行软件,而定义API的一系列互相关联的标准的总称,其正式称呼为IEEE Std 1003,而国际标准名称为ISO/IEC 9945。POSIX这个名称是由理查德·斯托曼(RMS)提出的缩写,最后多加上的那个X则表明其对Unix API的传承。
后来的操作系统也都基本兼容POSIX接口,C语言就有一个C POSIX library是C语言的POSIX系统下的标准库,这里面就包含了一些在C语言标准库之外的函数,POSIX接口成为了跨平台开发的基础。
不在C语言标准库之内的POSIX标准的头文件:
| 头文件 | 描述 | 首次发布 |
|---|---|---|
<aio.h> |
异步I/O | Issue 5 |
<arpa/inet.h> |
操纵数值IP地址的函数(部分Berkeley套接字) | Issue 6 |
<cpio.h> |
用于cpio的文件格式Magic number | Issue 3 |
<dirent.h> |
打开与列出目录 | Issue 2 |
<dlfcn.h> |
动态链接 | Issue 5 |
<fcntl.h> |
文件打开、加锁等操作 | Issue 1 |
<fmtmsg.h> |
Message显示结构 | Issue 4 |
<fnmatch.h> |
文件名匹配 | Issue 4 |
<ftw.h> |
文件树遍历 | Issue 1 |
<glob.h> |
路径名模式匹配Glob | Issue 4 |
<grp.h> |
用户Group identifier (Unix)信息与控制 | Issue 1 |
<iconv.h> |
字符编码转换 | Issue 4 |
<langinfo.h> |
语言信息常量。建于Locale.h之上 | Issue 2 |
<libgen.h> |
路径名操作 | Issue 4 |
<monetary.h> |
货币单位的字符串格式化 | Issue 4 |
<mqueue.h> |
消息队列 | Issue 5 |
<ndbm.h> |
NDBM数据库操作 | Issue 4 |
<net/if.h> |
本地网络接口列表 | Issue 6 |
<netdb.h> |
把本地协议与主机名翻译为数值地址。是Berkeley套接字的一部分 | Issue 6 |
<netinet/in.h> |
定义互联网协议与地址族。是Berkeley套接字的一部分 | Issue 6 |
<netinet/tcp.h> |
额外的TCP的控制选项。是Berkeley套接字的一部分 | Issue 6 |
<nl_types.h> |
本地化消息分类函数 | Issue 2 |
<poll.h> |
异步多工文件描述符 | Issue 4 |
<pthread.h> |
POSIX线程 | Issue 5 |
<pwd.h> |
passwd文件访问与控制 | Issue 1 |
<regex.h> |
正则表达式 | Issue 4 |
<sched.h> |
执行调度 | Issue 5 |
<search.h> |
搜索表 | Issue 1 |
<semaphore.h> |
POSIX信号量 | Issue 5 |
<spawn.h> |
spawning子进程 | Issue 6 |
<strings.h> |
大小写不敏感字符串比较 | Issue 4 |
<stropts.h> |
流操作,包括ioctl | Issue 4 |
<sys/ipc.h> |
进程间通信 (IPC) | Issue 2 |
<sys/mman.h> |
内存管理,包括POSIX共享内存 (进程间通信)与内存映射文件 | Issue 4 |
<sys/msg.h> |
POSIX消息队列 | Issue 2 |
<sys/resource.h> |
资源使用,优先级与限制 | Issue 4 |
<sys/select.h> |
Select (Unix) | Issue 6 |
<sys/sem.h> |
XSI (SysV风格的)信号量 | Issue 2 |
<sys/shm.h> |
XSI (SysV风格的)共享内存 (进程间通信) | Issue 2 |
<sys/socket.h> |
Berkeley套接字主要头文件 | Issue 6 |
<sys/stat.h> |
文件信息(stat (Unix)等) | Issue 1 |
<sys/statvfs.h> |
文件系统信息 | Issue 4 |
<sys/time.h> |
时间与日期函数与结构 | Issue 4 |
<sys/times.h> |
文件访问与修改时间 | Issue 1 |
<sys/types.h> |
不同的数据类型 | Issue 1 |
<sys/uio.h> |
向量I/O操作 | Issue 4 |
<sys/un.h> |
Unix域套接字 | Issue 6 |
<sys/utsname.h> |
操作系统信息,包括uname | Issue 1 |
<sys/wait.h> |
终止子进程的状态(见wait (Unix)) | Issue 3 |
<syslog.h> |
syslog系统日志 | Issue 4 |
<tar.h> |
tar文件格式的Magic number | Issue 3 |
<termios.h> |
允许串口界面 | Issue 3 |
<trace.h> |
运行时行为追踪(过时) | Issue 6 |
<ulimit.h> |
资源限制(被<sys/resource.h>代替) |
Issue 1 |
<unistd.h> |
多种必要的POSIX函数与常量 | Issue 1 |
<utime.h> |
inode访问与修改时间 | Issue 3 |
<utmpx.h> |
用户账号数据库函数 | Issue 4 |
<wordexp.h> |
子扩展,类似于shell被执行 | Issue 4 |
图形与GUI开发
要让C语言能够进行图形图像绘制什么的,比如想写游戏啥的,还需要用OpenGL或者更新的Vulkan等等。
OpenGL(英语:Open Graphics Library,译名:开放图形库或者「开放式图形库」),是用于渲染2D、3D向量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来从简单的图形位元绘制复杂的三维景象。
Vulkan发表于本世纪,距今大概就是十多年,比OpenGL更先进,而且有更好的多核心CPU支持等等,最开始发布的时候曾经被称作是下一代的OpenGL行动」(next generation OpenGL initiative)或「glNext」,但后来被改为Vulkan。
注意,上面的OpenGL和Vulkan都是用C语言来绘制图形图像的,就比如游戏引擎之类的,而不是写图形界面GUI的。
要是想用C语言来开发GUI,得用GTK或者Qt等等库。
这些大家自己去搜索就可以了,这里先不介绍了。
要注意不同细分领域都已经有很多深入对应垂直领域的技术栈了,用得到的时候再去了解和学习使用就可以,现在有了各种AI帮忙,阅读文档和写代码都简单了不少。不过这时候就得注意避免贪多求全,人的精力有限,不可能什么都学了,如果学的面比较广,就可能像我一样有很多个瓶子但是每个瓶子里面都没有多少水,驳杂而不够专精,不足取也。
学习建议
对于新手来说,学习一门编程语言,掌握以下两部分足矣:
- 基础工具链部署:如何安装编译器、配置环境、构建项目;
- 语法与生态认知:核心语法、标准库、常用第三方库及社区资源。
之后,便是动手实践——根据实际需求去写、去调试、去优化。唯有如此,才能真正掌握一门语言。
CycleUser