大家好,我是程序员 cxuan!明天和大家一起学习 C 语言根底!
前言
C 语言是一门 形象的
、 面向过程
的语言,C 语言广泛应用于 底层开发
,C 语言在计算机体系中占据着不可代替的作用,能够说 C 语言是编程的根底,也就是说,不论你学习任何语言,都应该把 C 语言放在 首先要学
的地位上。上面这张图更好的阐明 C 语言的重要性
<img src=”https://s1.ax1x.com/2020/09/10/wG83gx.png” alt=”01″ border=”0″ style=”zoom:50%;” >
能够看到,C 语言是一种底层语言,是一种零碎层级的语言,操作系统就是应用 C 语言来编写的,比方 Windows、Linux、UNIX。如果说其余语言是光鲜亮丽的表面,那么 C 语言就是灵魂,永远那么朴实无华。
原文链接:C 语言根底,来喽!
C 语言个性
那么,既然 C 语言这么重要,它有什么值得咱们去学的中央呢?咱们不应该只因为它重要而去学,咱们更在意的是学完咱们能学会什么,能让咱们取得什么。
C 语言的设计
C 语言是 1972 年,由贝尔实验室的 丹尼斯·里奇 (Dennis Ritch)
和肯·汤普逊 (Ken Thompson)
在开发 UNIX 操作系统时设计了 C 语言。C 语言是一门风行的语言,它把计算机科学实践和工程实际实践完满的交融在一起,使用户可能实现模块化的编程和设计。
计算机科学实践:简称 CS、是系统性钻研信息与计算的实践根底以及它们在计算机系统中如何实现与利用的实用技术的学科。
C 语言具备高效性
C 语言是一门高效性语言,它被设计用来充分发挥计算机的劣势,因而 C 语言程序运行速度很快,C 语言可能正当了应用内存来取得最大的运行速度
C 语言具备可移植性
C 语言是一门具备可移植性的语言,这就意味着,对于在一台计算机上编写的 C 语言程序能够在另一台计算机上轻松地运行,从而极大的缩小了程序移植的工作量。
C 语言特点
- C 语言是一门简洁的语言,因为 C 语言设计更加凑近底层,因而不须要泛滥 Java、C# 等高级语言才有的个性,程序的编写要求不是很严格。
- C 语言具备结构化管制语句,C 语言是一门结构化的语言,它提供的管制语句具备结构化特色,如 for 循环、if⋯ else 判断语句和 switch 语句等。
- C 语言具备丰盛的数据类型,不仅蕴含有传统的 字符型、整型、浮点型、数组 类型等数据类型,还具备其余编程语言所不具备的数据类型,比方指针。
- C 语言可能间接对内存地址进行读写,因而能够实现汇编语言的次要性能,并可间接操作硬件。
- C 语言速度快,生成的指标代码执行效率高。
上面让咱们通过一个简略的示例来阐明一下 C 语言
入门级 C 语言程序
上面咱们来看一个很简略的 C 语言程序,我感觉工具无所谓大家用着棘手就行。
第一个 C 语言程序
#include <stdio.h>
int main(int argc, const char * argv[]) {printf("Hello, World!\n");
printf("my Name is cxuan \n")
printf("number = %d \n", number);
return 0;
}
你可能不晓得这段代码是什么意思,不过别着急,咱们先运行一下看看后果。
这段程序输入了 Hello,World!
和 My Name is cxuan
,上面咱们解释一下各行代码的含意。
首先,第一行的 #include <stdio.h>
,这行代码蕴含另一个文件,这一行通知编译器把 stdio.h
的内容蕴含在以后程序中。stdio.h
是 C 编译器软件包的规范局部,它可能提供键盘输入和显示器输入。
什么是 C 规范软件包?C 是由 Dennis M 在 1972 年开发的通用,过程性,命令式计算机编程语言。C 规范库是一组 C 语言内置函数,常量和头文件,例如 <stdio.h>,<stdlib.h>,<math.h> 等。此库将用作 C 程序员的参考手册。
咱们前面会介绍 stdio.h,当初你晓得它是什么就好。
在 stdio.h 上面一行代码就是 main
函数。
C 程序可能蕴含一个或多个函数,函数是 C 语言的基本,就和办法是 Java 的根本形成一样。main()
示意一个函数名,int
示意的是 main 函数返回一个整数。void 表明 main() 不带任何参数。这些咱们前面也会具体阐明,只须要记住 int 和 void 是规范 ANSI C
定义 main() 的一部分(如果应用 ANSI C 之前的编译器,请疏忽 void)。
而后是 /* 一个简略的 C 语言程序 */
示意的是正文,正文应用 /**/
来示意,正文的内容在两个符号之间。这些符号可能进步程序的可读性。
留神:正文只是为了帮忙程序员了解代码的含意,编译器会疏忽正文
上面就是 {
,这是左花括号,它示意的是函数体的开始,而最初的右花括号 }
示意函数体的完结。{}
两头是书写代码的中央,也叫做代码块。
int number
示意的是将会应用一个名为 number 的变量,而且 number 是 int
整数类型。
number = 11
示意的是把值 11 赋值给 number 的变量。
printf(Hello,world!\n);
示意调用一个函数,这个语句应用 printf()
函数,在屏幕上显示 Hello,world
,printf() 函数是 C 规范库函数中的一种,它可能把程序运行的后果输入到显示器上。而代码 \n
示意的是 换行
,也就是另起一行,把光标移到下一行。
而后接下来的一行 printf() 和下面一行是一样的,咱们就不多说了。最初一行 printf() 有点意思,你会发现有一个 %d
的语法,它的意思示意的是应用整形输入字符串。
代码块的最初一行是 return 0
,它能够看成是 main 函数的完结,最初一行是代码块 }
,它示意的是程序的完结。
好了,咱们当初写完了第一个 C 语言程序,有没有对 C 有了更深的意识呢?必定没有。。。这才哪到哪,持续学习吧。
当初,咱们能够演绎为 C 语言程序的几个组成因素,如下图所示
<img src=”https://s1.ax1x.com/2020/09/10/wG8D2t.png” alt=”03″ border=”0″>
C 语言执行流程
C 语言程序成为高级语言的起因是它可能读取并了解人们的思维。然而,为了可能在零碎中运行 hello.c
程序,则各个 C 语句必须由其余程序转换为一系列低级机器语言指令。这些指令被打包作为 可执行对象程序
,存储在二进制磁盘文件中。目标程序也称为可执行指标文件。
在 UNIX 零碎中,从源文件到对象文件的转换是由 编译器
执行实现的。
gcc -o hello hello.c
gcc 编译器驱动从源文件读取 hello.c
,并把它翻译成一个可执行文件 hello
。这个翻译过程可用如下图来示意
<img src=”https://s1.ax1x.com/2020/09/10/wG80PA.png” alt=”04″ border=”0″>
这就是一个残缺的 hello world 程序执行过程,会波及几个外围组件:预处理器、编译器、汇编器、连接器,上面咱们一一击破。
预处理阶段 (Preprocessing phase)
,预处理器会依据开始的#
字符,批改源 C 程序。#include <stdio.h> 命令就会通知预处理器去读零碎头文件stdio.h
中的内容,并把它插入到程序作为文本。而后就失去了另外一个 C 程序hello.i
,这个程序通常是以.i
为结尾。- 而后是
编译阶段 (Compilation phase)
,编译器会把文本文件hello.i
翻译成文本hello.s
,它包含一段汇编语言程序(assembly-language program)
。 - 编译实现之后是
汇编阶段 (Assembly phase)
,这一步,汇编器 as
会把 hello.s 翻译成机器指令,把这些指令打包成可重定位的二进制程序 (relocatable object program)
放在 hello.c 文件中。它蕴含的 17 个字节是函数 main 的指令编码,如果咱们在文本编辑器中关上 hello.o 将会看到一堆乱码。 - 最初一个是
链接阶段 (Linking phase)
,咱们的 hello 程序会调用printf
函数,它是 C 编译器提供的 C 规范库中的一部分。printf 函数位于一个叫做printf.o
文件中,它是一个独自的预编译好的指标文件,而这个文件必须要和咱们的 hello.o 进行链接,连接器(ld)
会解决这个合并操作。后果是,hello 文件,它是一个可执行的指标文件(或称为可执行文件),已筹备好加载到内存中并由零碎执行。
你须要了解编译系统做了什么
对于下面这种简略的 hello 程序来说,咱们能够依赖 编译系统 (compilation system)
来提供一个正确和无效的机器代码。然而,对于咱们下面讲的程序员来说,编译器有几大特色你须要晓得
优化程序性能(Optimizing program performance)
,古代编译器是一种高效的用来生成良好代码的工具。对于程序员来说,你无需为了编写高质量的代码而去了解编译器外部做了什么工作。然而,为了编写出高效的 C 语言程序,咱们须要理解一些根本的机器码以及编译器将不同的 C 语句转化为机器代码的过程。了解链接时呈现的谬误(Understanding link-time errors)
,在咱们的教训中,一些非常复杂的谬误大多是由链接阶段引起的,特地是当你想要构建大型软件我的项目时。防止安全漏洞 (Avoiding security holes)
,近些年来,缓冲区溢出 (buffer overflow vulnerabilities)
是造成网络和 Internet 服务的罪魁祸首,所以咱们有必要去躲避这种问题。
零碎硬件组成
为了了解 hello 程序在运行时产生了什么,咱们须要首先对系统的硬件有一个意识。上面这是一张 Intel 零碎产品的模型,咱们来对其进行解释
<img src=”https://s1.ax1x.com/2020/09/10/wG8UVe.png” alt=”05″ border=”0″>
总线 (Buses)
:在整个零碎中运行的是称为总线的电气管道的汇合,这些总线在组件之间来回传输字节信息。通常总线被设计成传送定长的字节块,也就是字(word)
。字中的字节数(字长)是一个根本的零碎参数,各个系统中都不尽相同。当初大部分的字都是 4 个字节(32 位)或者 8 个字节(64 位)。
<img src=”https://s1.ax1x.com/2020/09/10/wG8tbD.png” alt=”06″ border=”0″>
-
I/O 设施(I/O Devices)
:Input/Output 设施是零碎和内部世界的连贯。上图中有四类 I/O 设施:用于用户输出的键盘和鼠标,用于用户输入的显示器,一个磁盘驱动用来长时间的保留数据和程序。刚开始的时候,可执行程序就保留在磁盘上。每个 I /O 设施连贯 I/O 总线都被称为
控制器 (controller)
或者是适配器(Adapter)
。控制器和适配器之间的次要区别在于封装形式。控制器是 I/O 设施自身或者零碎的主印制板电路(通常称作主板)上的芯片组。而适配器则是一块插在主板插槽上的卡。无论组织模式如何,它们的最终目标都是彼此替换信息。 主存 (Main Memory)
,主存是一个长期存储设备
,而不是永久性存储,磁盘是永久性存储
的设施。主存既保留程序,又保留处理器执行流程所解决的数据。从物理组成上说,主存是由一系列DRAM(dynamic random access memory)
动态随机存储形成的汇合。逻辑上说,内存就是一个线性的字节数组,有它惟一的地址编号,从 0 开始。一般来说,组成程序的每条机器指令都由不同数量的字节形成,C 程序变量绝对应的数据项的大小依据类型进行变动。比方,在 Linux 的 x86-64 机器上,short 类型的数据须要 2 个字节,int 和 float 须要 4 个字节,而 long 和 double 须要 8 个字节。-
处理器 (Processor)
,CPU(central processing unit)
或者简略的处理器,是解释(并执行)存储在主存储器中的指令的引擎。处理器的外围大小为一个字的存储设备(或寄存器),称为程序计数器(PC)
。在任何时刻,PC 都指向主存中的某条机器语言指令(即含有该条指令的地址)。从零碎通电开始,直到零碎断电,处理器始终在一直地执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令。处理器依据其指令集体系结构定义的指令模型进行操作。在这个模型中,指令依照严格的程序执行,执行一条指令波及执行一系列的步骤。处理器从程序计数器指向的内存中读取指令,解释指令中的位,执行该指令批示的一些简略操作,而后更新程序计数器以指向下一条指令。指令与指令之间可能间断,可能不间断(比方 jmp 指令就不会程序读取)
上面是 CPU 可能执行简略操作的几个步骤
加载(Load)
:从主存中拷贝一个字节或者一个字到内存中,笼罩寄存器先前的内容存储(Store)
:将寄存器中的字节或字复制到主存储器中的某个地位,从而笼罩该地位的先前内容操作(Operate)
:把两个寄存器的内容复制到ALU(Arithmetic logic unit)
。把两个字进行算术运算,并把后果存储在寄存器中,重写寄存器先前的内容。
算术逻辑单元(ALU)是对数字二进制数执行算术和按位运算的组合数字电子电路。
跳转 (jump)
:从指令中抽取一个字,把这个字复制到程序计数器(PC)
中,笼罩原来的值
分析 hello 程序的执行过程
后面咱们简略的介绍了一下计算机的硬件的组成和操作,当初咱们正式介绍运行示例程序时产生了什么,咱们会从宏观的角度进行形容,不会波及到所有的技术细节
刚开始时,shell 程序执行它的指令,期待用户键入一个命令。当咱们在键盘上输出了 ./hello
这几个字符时,shell 程序将字符逐个读入寄存器,再把它放到内存中,如下图所示
<img src=”https://s1.ax1x.com/2020/09/10/wG8d5d.png” alt=”07″ border=”0″>
当咱们在键盘上敲击 回车键
的时候,shell 程序就晓得咱们曾经完结了命令的输出。而后 shell 执行一系列指令来加载可执行的 hello 文件,这些指令将指标文件中的代码和数据从磁盘复制到主存。
利用 DMA(Direct Memory Access)
技术能够间接将磁盘中的数据复制到内存中,如下
<img src=”https://s1.ax1x.com/2020/09/10/wG8aUH.png” alt=”08″ border=”0″>
一旦指标文件中 hello 中的代码和数据被加载到主存,处理器就开始执行 hello 程序的 main 程序中的机器语言指令。这些指令将 hello,world\n
字符串中的字节从主存复制到寄存器文件,再从寄存器中复制到显示设施,最终显示在屏幕上。如下所示
<img src=”https://s1.ax1x.com/2020/09/10/wG8YDO.png” alt=”09″ border=”0″>
高速缓存是要害
下面咱们介绍完了一个 hello 程序的执行过程,零碎破费了大量工夫把信息从一个中央搬运到另外一个中央。hello 程序的机器指令最后存储在 磁盘
上。当程序加载后,它们会 拷贝
到主存中。当 CPU 开始运行时,指令又从内存复制到 CPU 中。同样的,字符串数据 hello,world \n
最后也是在磁盘上,它被复制到内存中,而后再到显示器设施输入。从程序员的角度来看,这种复制大部分是开销,这减慢了程序的工作效率。因而,对于零碎设计来说,最次要的一个工作是让程序运行的越来越快。
因为物理定律,较大的存储设备要比拟小的存储设备慢。而因为寄存器和内存的解决效率在越来越大,所以针对这种差别,零碎设计者采纳了更小更快的存储设备,称为 高速缓存存储器(cache memory, 简称为 cache 高速缓存)
,作为临时的集结区域,寄存近期可能会须要的信息。如下图所示
<img src=”https://s1.ax1x.com/2020/09/10/wG8JKK.png” alt=”10″ border=”0″>
图中咱们标出了高速缓存的地位,位于高速缓存中的 L1
高速缓存容量能够达到数万字节,访问速度简直和拜访寄存器文件一样快。容量更大的 L2
高速缓存通过一条非凡的总线链接 CPU,尽管 L2 缓存比 L1 缓存慢 5 倍,然而仍比内存要哦快 5 – 10 倍。L1 和 L2 是应用一种 动态随机拜访存储器 (SRAM)
的硬件技术实现的。最新的、处理器更弱小的零碎甚至有三级缓存:L1、L2 和 L3。零碎能够取得一个很大的存储器,同时访问速度也更快,起因是利用了高速缓存的 局部性
原理。
Again:入门程序细节
当初,咱们来探讨一下 入门级
程序的细节,由浅入深的来理解一下 C 语言的个性。
include<stdio.h>
咱们下面说到,#include<stdio.h>
是程序编译之前要解决的内容,称为 编译预处理
命令。
预处理命令是在编译之前进行解决。预处理程序个别以 #
号结尾。
所有的 C 编译器软件包都提供 stdio.h
文件。该文件蕴含了给编译器应用的输出和输入函数,比方 println() 信息。该文件名的含意是 规范输出 / 输入 头文件。通常,在 C 程序顶部的信息汇合被称为 头文件(header)
。
C 的第一个规范是由 ANSI 公布的。尽管这份文档起初被国际标准化组织 (ISO) 驳回并且 ISO 公布的修订版也被 ANSI 驳回了,但名称 ANSI C(而不是 ISO C) 仍被宽泛应用。一些软件开发者应用ISO C,还有一些应用 Standard C。
C 规范库
除了 <sdtio.h> 外,C 规范库还包含上面这些头文件
<img src=”https://s1.ax1x.com/2020/09/10/wG88v6.png” alt=”11″ border=”0″ style=”zoom:50%;” >
<assert.h>
提供了一个名为 assert
的关键字,它用于验证程序作出的假如,并在假如为假输入诊断音讯。
<ctype.h>
C 规范库的 ctype.h 头文件提供了一些函数,能够用于测试和映射字符。
这些字符承受 int 作为参数,它的值必须是 EOF
或者是一个无符号字符
EOF 是一个计算机术语,为 End Of File 的缩写,在操作系统中示意材料源无更多的材料可读取。材料源通常称为档案或串流。通常在文本的最初存在此字符示意材料完结。
<errno.h>
C 规范库的 errno.h 头文件定义了整数变量 errno,它是通过零碎调用设置的,这些库函数表明了什么产生了谬误。
<float.h>
C 规范库的 float.h 头文件蕴含了一组与浮点值相干的依赖于平台的常量。
<limits.h>
limits.h 头文件决定了各种变量类型的各种属性。定义在该头文件中的宏限度了各种变量类型(比方 char、int 和 long)的值。
<locale.h>
locale.h 头文件定义了特定地区的设置,比方日期格局和货币符号
<math.h>
math.h 头文件定义了各种数学函数和一个宏。在这个库中所有可用的性能都带有一个 double 类型的参数,且都返回 double 类型的后果。
<setjmp.h>
setjmp.h 头文件定义了宏 setjmp()、函数 longjmp() 和变量类型 jmp_buf,该变量类型会绕过失常的函数调用和返回规定。
<signal.h>
signal.h 头文件定义了一个变量类型 sig_atomic_t、两个函数调用和一些宏来处理程序执行期间报告的不同信号。
<stdarg.h>
stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。
<stddef.h>
stddef .h 头文件定义了各种变量类型和宏。这些定义中的大部分也呈现在其它头文件中。
<stdlib.h>
stdlib .h 头文件定义了四个变量类型、一些宏和各种通用工具函数。
<string.h>
string .h 头文件定义了一个变量类型、一个宏和各种操作字符数组的函数。
<time.h>
time.h 头文件定义了四个变量类型、两个宏和各种操作日期和工夫的函数。
main() 函数
main 函数听起来像是调皮捣蛋的孩子成心给办法名起一个 次要的
办法,来通知别人他才是这个世界的核心。但事实却不是这样,而 main()
办法的确是世界的核心。
C 语言程序肯定从 main() 函数开始执行,除了 main() 函数外,你能够随便命名其余函数。通常,main 前面的 ()
中示意一些传入信息,咱们下面的那个例子中没有传递信息,因为圆括号中的输出是 void。
除了下面那种写法外,还有两种 main 办法的示意形式,一种是 void main(){}
,一种是 int main(int argc, char* argv[]) {}
- void main() 申明了一个带有不确定参数的构造方法
- int main(int argc, char* argv[]) {} 其中的 argc 是一个非负值,示意从运行程序的环境传递到程序的参数数量。它是指向 argc + 1 指针数组的第一个元素的指针,其中最初一个为 null,而前一个(如果有的话)指向示意从主机环境传递给程序的参数的字符串。如果 argv [0]不是空指针(或者等效地,如果 argc> 0),则指向示意程序名称的字符串,如果在主机环境中无奈应用程序名称,则该字符串为空。
正文
在程序中,应用 /**/ 的示意正文,正文对于程序来说没有什么理论用途,然而对程序员来说却十分有用,它可能帮忙咱们了解程序,也可能让别人看懂你写的程序,咱们在开发工作中,都十分恶感不写正文的人,由此可见正文十分重要。
<img src=”/Users/mr.l/Library/Application Support/typora-user-images/image-20200830071857009.png” alt=”image-20200830071857009″ style=”zoom:50%;” />
C 语言正文的益处是,它能够放在任意中央,甚至代码在同一行也没关系。较长的正文能够多行示意,咱们应用 /**/ 示意多行正文,而 // 只示意的是单行正文。上面是几种正文的示意模式
// 这是一个单行正文
/* 多行正文用一行示意 */
/*
多行正文用多行示意
多行正文用多行示意
多行正文用多行示意
多行正文用多行示意
*/
函数体
在头文件、main 办法前面的就是函数体(正文个别不算),函数体就是函数的执行体,是你编写大量代码的中央。
变量申明
在咱们入门级的代码中,咱们申明了一个名为 number
的变量,它的类型是 int,这行代码叫做 申明
,申明是 C 语言最重要的个性之一。这个申明实现了两件事件:定义了一个名为 number 的变量,定义 number 的具体类型。
int 是 C 语言的一个 关键字(keyword)
,示意一种根本的 C 语言数据类型。关键字是用于语言定义的。不能应用关键字作为变量进行定义。
示例中的 number
是一个 标识符(identifier)
,也就是一个变量、函数或者其余实体的名称。
变量赋值
在入门例子程序中,咱们申明了一个 number 变量,并为其赋值为 11,赋值是 C 语言的基本操作之一。这行代码的意思就是把值 1 赋给变量 number。在执行 int number 时,编译器会在计算机内存中为变量 number 预留空间,而后在执行这行赋值表达式语句时,把值存储在之前预留的地位。能够给 number 赋不同的值,这就是 number 之所以被称为 变量(variable)
的起因。
<img src=”https://s1.ax1x.com/2020/09/10/wG8181.png” alt=”12″ border=”0″>
printf 函数
在入门例子程序中,有三行 printf(),这是 C 语言的规范函数。圆括号中的内容是从 main 函数传递给 printf 函数的。参数分为两种:理论参数 (actual argument)
和 形式参数(formal parameters)
。咱们下面提到的 printf 函数括号中的内容,都是实参。
return 语句
在入门例子程序中,return 语句是最初一条语句。int main(void)
中的 int 表明 main() 函数应返回一个整数。有返回值的 C 函数要有 return 语句,没有返回值的程序也倡议大家保留 return 关键字,这是一种好的习惯或者说对立的编码格调。
分号
在 C 语言中,每一行的结尾都要用 ;
进行完结,它示意一个语句的完结,如果遗记或者会略分号会被编译器提醒谬误。
关键字
上面是 C 语言中的关键字,C 语言的关键字一共有 32
个,依据其作用不同进行划分
数据类型关键字
数据类型的关键字次要有 12 个,别离是
char
: 申明字符型变量或函数double
: 申明双精度变量或函数float
: 申明浮点型变量或函数int
: 申明整型变量或函数long
: 申明长整型变量或函数short
: 申明短整型变量或函数signed
: 申明有符号类型变量或函数_Bool
: 申明布尔类型_Complex
:申明复数_Imaginary
: 申明虚数unsigned
: 申明无符号类型变量或函数void
: 申明函数无返回值或无参数,申明无类型指针
管制语句关键字
管制语句循环的关键字也有 12 个,别离是
循环语句
for
: for 循环,应用的最多do
:循环语句的前提条件循环体while
:循环语句的循环条件break
: 跳出以后循环continue
:完结以后循环,开始下一轮循环
条件语句
if
:条件语句的判断条件else
: 条件语句的否定分支,与 if 连用goto
: 无条件跳转语句
开关语句
switch
: 用于开关语句case
:开关语句的另外一种分支default
: 开关语句中的其余分支
返回语句
retur
:子程序返回语句(能够带参数,也看不带参数)
存储类型关键字
auto
: 申明主动变量 个别不应用extern
: 申明变量是在其余文件正申明(也能够看做是援用变量)register
: 申明寄存器变量static
: 申明动态变量
其余关键字
const
: 申明只读变量sizeof
: 计算数据类型长度typedef
: 用以给数据类型取别名volatile
: 阐明变量在程序执行中可被隐含地扭转
后记
这篇文章咱们先介绍了 C 语言的个性,C 语言为什么这么火,C 语言的重要性,之后咱们以一道 C 语言的入门程序讲起,咱们讲了 C 语言的根本形成因素,C 语言在硬件上是如何运行的,C 语言的编译过程和执行过程等,在这之后咱们又加深解说了一下入门例子程序的组成特色。
如果你感觉这篇文章不错的的话,欢送小伙伴们四连走起:点赞、在看、留言、分享。你的四连是我更文的能源。
C 中的数据
咱们在理解完下面的入门例子程序后,上面咱们就要全面认识一下 C 语言程序了,首先咱们先来认识一下 C 语言最根本的变量与常量。
变量和常量
变量和常量是程序处理的两种根本对象。
有些数据类型在程序应用之前就曾经被设定好了,在整个过程中没有变动(这段话形容不精确,然而为了通俗易懂,暂且这么形容),这种数据被称为 常量 (constant)
。另外一种数据类型在程序执行期间可能会产生扭转,这种数据类型被称为 变量(variable)
。例如 int number
就是一个变量,而3.1415
就是一个常量,因为 int number 一旦申明进去,你能够对其任意赋值,而 3.1415 一旦申明进去,就不会再扭转。
变量名
有必要在聊数据类型之前先说一说变量名的概念。变量名是由字母和数字组成的序列,第一个字符必须是字母。在变量名的命名过程中,下划线 _
被看作字母,下划线个别用于名称较长的变量名,这样可能进步程序的可读性。变量名通常不会以下划线来结尾。在 C 中,大小写是有区别的,也就是说,a 和 A 齐全是两个不同的变量。个别变量名应用小写字母,符号常量(#define 定义的)全都应用大写。抉择变量名的时候,尽量可能从字面上形容出变量的用处,切忌起这种 abc 毫无意义的变量。
还须要留神个别局部变量都会应用较短的变量名,内部变量应用较长的名字。
数据类型
在理解数据类型之前,咱们须要先理解一下这些概念 位、字节和字。
位、字节和字都是对计算机 存储单元 的形容。在计算机世界中,最小的单元是
位(bit)
,一个位就示意一个 0 或 1,个别当你的小伙伴问你的电脑是 xxx 位,常见的有 32 位或者 64 位,这里的位就指的是比特,比特就是 bit 的中文名称,所以这里的 32 位或者 64 位指的就是 32 bit 或者 64 bit。字节是根本的存储单元,根本存储单元说的是在计算机中都是依照字节来存储的,一个字节等于 8 位,即 1 byte = 8 bit。字是天然存储单位,在古代计算机中,一个字等于 2 字节。
C 语言的数据类型有很多,上面咱们就来顺次介绍一下。
整型
C 语言中的整型用 int
来示意,能够是正整数、负整数或零。在不同位数的计算机中其取值范畴也不同。不过在 32 位和 64 位计算机中,int 的取值范畴是都是 2^32,也就是 -2147483648 ~ +2147483647,无符号类型的取值范畴是 0 ~ 4294967295。
整型以二进制整数存储,分为 有符号数和无符号数 两种状态,有符号数能够存储正整数、负整数和零;无符号只能存储正整数和零。
能够应用 printf 打印进去 int 类型的值,如下代码所示。
#include <stdio.h>
int main(){
int a = -5;
printf("%d\n",a);
unsigned int b = 6;
printf("%d\n",b);
}
C 语言还提供 3 个从属关键字润饰整数类型,即 short、long 和 unsigned。
- short int 类型(或者简写为 short)占用的存储空间
可能
比 int 类型少,适宜用于数值较小的场景。 - long int 或者 long 占用的存储空间
可能
比 int 类型多,适宜用于数值较大的场景。 - long long int 或者 long long(C99 退出)占用的存储空间比 long 多,实用于数值更大的场合,至多占用 64 位,与 int 相似,long long 也是有符号类型。
- unsigned int 或 unsigned 只用于非负值的场景,这种类型的取值范畴有所不同,比方 16 位的 unsigned int 示意的范畴是 0 ~ 65535,而不是 -32768 ~ 32767。
- 在 C90 规范中,增加了 unsigned long int 或者 unsigned long 和 unsigned short int 或 unsigned short 类型,在 C99 中又增加了 unsigned long long int 或者 unsigned long long。
- 在任何有符号类型后面加 signed,可强调应用有符号类型的用意。比方 short、short int、signed short、signed short int 都示意一种类型。
比方下面这些形容能够用上面这些代码来申明:
long int lia;
long la;
long long lla;
short int sib;
short sb;
unsigned int uic;
unsigned uc;
unsigned long uld;
unsigned short usd;
这里须要留神一点,unsigned 定义的变量,依照 printf 格式化输入时,是可能显示负值的,为什么呢?不是 unsigned 润饰的值不能是负值啊,那是因为 unsigned 润饰的变量,在计算时会有用,输入没什么影响,这也是 cxuan 刚开始学习的时候踩的坑。
咱们学过 Java 的同学刚开始都对这些定义感觉莫名其妙,为什么一个 C 语言要对数据类型有这么多定义?C 语言真麻烦,我不学了!
千万不要有这种想法,如果有这种想法的同学,你肯定是被 JVM 爱护的像个孩子!我必须从当初开始纠正你的这个想法,因为 Java 有 JVM 的爱护,很多个性都做了优化,而 C 就像个没有伞的孩子,它必须本人和这个世界打交道!
下面在说 short int 和 long int 的时候,都加了一个可能,怎么,难道 short int 和 long int 和 int 还不一样吗?
这里就是 C 语言数据类型一个独特的格调。
为什么说可能,这是因为 C 语言为了适配不同的机器来设定的语法规定,在早起的计算机上,int 类型和 short 类型都占 16 位,long 类型占 32 位,在起初的计算机中,都采纳了 16 位存储 short 类型,32 位存储 int 类型和 long 类型,当初,计算机广泛应用 64 位 CPU,为了存储 64 位整数,才引入了 long long 类型。所以,个别当初集体计算机上常见的设置是 long long 占用 64 位,long 占用 32 位,short 占用 16 位,int 占用 16 位或者 32 位。
char 类型
char 类型个别用于存储字符,示意办法如下
char a = 'x';
char b = 'y';
char 被称为字符类型,只能用单引号 ” 来示意,而不能用双引号“”来示意,这和字符串的示意模式相同。
char 尽管示意字符,然而 char 实际上存储的是整数而不是字符,计算机个别应用 ASCII
来解决字符,规范 ASCII 码的范畴是 0 – 127,只需 7 位二进制数示意即可。C 语言中规定 char 占用 1 字节。
其实整型和字符型是相通的,他们在内存中的存储实质是相通的,编译器发现 char,就会主动转换为整数存储,相同的,如果给 int 类型赋值英文字符,也会转换成整数存储,如下代码
#include <stdio.h>
int main(){
char a = 'x';
int b;
b = 'y';
printf("%d\n%d\n",a,b);
}
输入
120
121
所以,int 和 char 只是存储的范畴不同,整型能够是 2 字节,4 字节,8 字节,而字符型只占 1 字节。
有些 C 编译器把 char 实现为有符号类型,这意味着 char 可示意的范畴是 -128 ~ 127,而有些编译器把 char 实现为无符号类型,这种状况下 char 可示意的范畴是 0 – 255。signed char 示意的是有符号类型,unsigned char 示意的是无符号类型。
_Bool 类型
_Bool 类型是 C99 新增的数据类型,用于示意布尔值。也就是逻辑值 true 和 false。在 C99 之前,都是用 int 中的 1 和 0 来示意。所以 _Bool 在某种程度上也是一种数据类型。示意 0 和 1 的话,用 1 bit(位)示意就够了。
float、double 和 long double
整型对于大多数软件开发我的项目而言就曾经够用了。然而,在金融畛域和数学畛域还常常应用 浮点数
。C 语言中的浮点数有 float、double 和 long double 类型。浮点数类型可能示意包含小数在内更大范畴的数。浮点数能示意小数,而且示意范畴比拟大。浮点数的示意相似于科学技术法。上面是一些迷信记数法示例:
数字 | 迷信记数法 | 指数记数法 |
---|---|---|
1000000000 | 1 * 10^9 | 1.0e9 |
456000 | 4.56 * 10^5 | 4.56e5 |
372.85 | 3.7285 * 10 ^ 2 | 3.7285e2 |
0.0025 | 2.5 * 10 ^ -3 | 2.5e-3 |
C 规定 float 类型必须至多能示意 6 位有效数字,而且取值范畴至多是 10^-37 ~ 10^+37。通常状况下,零碎存储一个浮点数要占用 32 位。
C 提供的另一种浮点类型是 double(双精度类型)。一般来说,double 占用的是 64 位而不是 32 位。
C 提供的第三种类型是 long double,用于满足比 double 类型更高的精度要求。不过,C 只保障了 long double 类型至多与 double 类型雷同。
浮点数的申明形式和整型相似,上面是一些浮点数的申明形式。
#include <stdio.h>
int main(){
float aboat = 2100.0;
double abet = 2.14e9;
long double dip = 5.32e-5;
printf("%f\n", aboat);
printf("%e\n", abet);
printf("%Lf\n", dip);
}
printf() 函数应用 %f 转换阐明打印十进制计数法的 float 和 double 类型浮点数,用 %e 打印指数记数法的浮点数。打印 long double 类型要应用 %Lf 转换阐明。
对于浮点数,还须要留神其 上溢
和下溢
的问题。
上溢指的是是指因为数字过大,超过以后类型所能示意的范畴,如下所示
float toobig = 3.4E38 * 100.0f;printf("%e\n",toobig);
输入的内容是 inf
,这示意 toobig 的后果超过了其定义的范畴,C 语言就会给 toobig 赋一个示意无穷大的特定值,而且 printf 显示值为 inf 或者 infinity。
下溢:是指因为数值太小,低于以后类型所能示意的最小的值,计算机就只好把尾数位向右移,空出第一个二进制位,然而与此同时,却损失了原来开端无效位下面的数字,这种状况就叫做下溢。比方上面这段代码
float toosmall = 0.1234e-38/10;printf("%e\n", toosmall);
复数和虚数类型
许多迷信和工程计算都须要用到复数和虚数,C99 规范反对复数类型和虚数类型,C 语言中有 3 种复数类型:float _Complex、double _Complex 和 long double _Complex。
C 语言提供的 3 种虚数类型:float _Imaginary、double _Imaginary 和 long double _Imaginary。
如果蕴含 complex.h 头文件的话,便可应用 complex 替换 _Complex,用 imaginary 代替 _Imaginary。
其余类型
除了上述咱们介绍过的类型之外,C 语言中还有其余类型,比方数组、指针、构造和联结,尽管 C 语言没有字符串类型,然而 C 语言却可能很好的解决字符串。
常量
在很多状况下咱们须要常量,在整个程序的执行过程中,其值不会产生扭转,比方一天有 24 个小时,最大缓冲区的大小,滑动窗口的最大值等。这些固定的值,即称为常量,又能够叫做 字面量。
常量也分为很多种,整型常量,浮点型常量,字符常量,字符串常量,上面咱们别离来介绍
整数常量
整数常量能够示意为十进制、八进制或十六进制。前缀指定基数:0x 或 0X 示意十六进制,0 示意八进制,不带前缀则默认示意十进制。整数常量也能够带一个后缀,后缀是 U 和 L 的组合,U 示意无符号整数(unsigned),L 示意长整数(long)。
330 /* 非法的 */315u /* 非法的 */0xFeeL /* 非法的 */048 /* 非法的:8 进制不能定义 8 */
浮点型常量
浮点型常量由整数局部、小数点、小数局部和指数局部组成。你能够应用小数模式或者指数模式来示意浮点常量。
当应用小数模式示意时,必须蕴含整数局部、小数局部,或同时蕴含两者。当应用指数模式示意时,必须蕴含小数点、指数,或同时蕴含两者。带符号的指数是用 e 或 E 引入的。
3.14159 /* 非法的 */314159E-5L /* 非法的 */510E /* 非法的:不残缺的指数 */210f /* 非法的:没有小数或指数 */
字符常量
C 语言中的字符常量应用单引号(即撇号)括起来的一个字符。如‘a’,‘x’,’D’,‘?’,‘$’等都是字符常量。留神,‘a’和‘A’是不同的字符常量。
除了以上模式的字符常量外,C 还容许用一种非凡模式的字符常量,就是以一个“\”结尾的字符序列。例如,后面曾经遇到过的,在 printf 函数中的‘\n’,它代表一个换行符。这是一种控制字符,在屏幕上是不能显示的。
罕用的以“\”结尾的特殊字符有
表中列出的字符称为“转义字符”,意思是将反斜杠(\)前面的字符转换成另外的意义。如‘\n’中的“n”不代表字母 n 而作为“换行”符。
表中最初第 2 行是用 ASCII 码(八进制数)示意一个字符,例如‘\101’代表 ASCII 码(十进制数)为 65 的字符“A”。‘\012’(十进制 ASCII 码为 10)代表换行。
须要留神的是‘\0’或‘\000’代表 ASCII 码为 0 的控制字符,它用在字符串中。
字符串常量
字符串常量通常用 “” 进行示意。字符串就是一系列字符的汇合。一个字符串蕴含相似于字符常量的字符:一般的字符、转义序列和通用的字符。
常量定义
C 语言中,有两种定义常量的形式。
- 应用
#define
预处理器进行预处理 - 应用
const
关键字进行解决
上面是应用 #define 预处理器进行常量定义的代码。
#include <stdio.h>#define LENGTH 5#define WIDTH 10int main(){ int area = LENGTH * WIDTH; printf("area = %d\n", area); }
同样的,咱们也能够应用 const 关键字来定义常量,如下代码所示
#include <stdio.h>int main(){ const int LENGTH = 10; const int WIDTH = 5; int area; area = LENGTH * WIDTH; printf("area = %d\n", area); }
那么这两种常量定义形式有什么不同呢?
编译器解决形式不同
应用 #define 预处理器是在预处理阶段进行的,而 const 润饰的常量是在编译阶段进行。
类型定义和查看不同
应用 #define 不必申明数据类型,而且不必类型查看,仅仅是定义;而应用 const 须要申明具体的数据类型,在编译阶段会进行类型查看。
文章参考:
https://www.zhihu.com/questio…
我本人写了四本 PDF,十分硬核,链接如下
cxuan 醉生梦死肝了四本 PDF。
最初给大家举荐一下我本人的 Github,外面有十分多的硬核文章,相对会对你有帮忙。