汇编语言是一门怎样的语言(计算机专业)
admin
2023-08-08 22:44:20

对于理工科大一新生来说,C语言是一道绕不过去的坎。因为很多人在进入大学之前从未接触过编程相关的内容,所以对这门课程的接受度普遍较低,学习起来也很吃力。下面总结一些老师上课可能不会详细讲解,但对理解C语言个人感受很重要的东西,供大家参考和讨论。

首先,我们来讨论一些基本内容。因为学校教学时间有限,每节课的时间比较短,所以在进入具体教学的绪论部分之前,不会花费太多的笔墨。很多时候,即使是学生,也只会被告知如何安装IDE,如何新建文件,保存,编译,运行,却不会被告知为什么要这么做,每一步背后都做了什么。要理解这些问题,我们需要先了解一些编程语言的基础知识。先说分类。

编程语言怎么分类?

编程语言其实有很多种分类方式。第一,可以分为高级语言和低级语言,高级语言有很多种,下面会介绍。

高级语言与低级语言

首先分为高级和低级,可以分为机器语言(汇编语言)和其他。高级语言的分类有很多,这里简单说一下低级语言。

机器语言只由0和1组成,是计算机硬件可以直接理解的语言。ARM、x86、RISC-V等不同架构都有不同的机器语言。机器语言可以直接操作处理器,其操作码由计算机中相应的电路完成。

汇编语言是在机器语言基础上诞生的语言。每一句都与机器语言中的操作码一一对应,可以直接翻译成机器语言。它存在的意义在于,它能被程序员容易理解。

上图:汇编语言和机器语言的比较

举个简单的例子:如果你要加两个数,比如计算2/3,那么机器语言可能是0000000110000001000000011(这里加空格是为了方便,但实际机器语言中没有空格,只有0和1,机器代码是个人发明的,仅作为例子使用,不是某个架构实际使用的机器代码),用的是汇编语言。当然,汇编语言可以不止这些,但由于本文主要不是介绍汇编语言,所以就不深入讨论了。

尽管如此,汇编语言仍然非常复杂,并且有许多限制。一旦需要写一些复杂的函数或者算术运算,写完之后甚至很容易读出来。我在学习微机原理的时候,写过一个课程项目。要求是为89C51单片机写一个摇号计数的代码,不包括驱动液晶屏的代码。总数只有四五百行,但为了理解,我写了近三百行评论。虽然有我不精通汇编的一些原因,但其复杂程度可见一斑。

可见,用机器语言或者汇编语言写复杂的代码显然是不可能的。这时,高级语言应运而生。下面简单介绍一下高级语言及其分类。

编译型、解释型与混合型

接下来介绍通过语言翻译的分类,以三种非常流行的语言为例,分别是C语言、Python和Java。

因为所有写出来的代码最终都会变成机器语言才能执行,不同的语言最终会达到同一个目的,都会被翻译回汇编和机器语言,只是不同类型的语言会有不同的翻译方式。这里首先介绍以C语言为代表的编译语言。

编译语言,顾名思义,就是通过编译把代码翻译成机器语言,然后执行。因此,代码在执行前会先被编译。这一步将告诉学生,他们必须单击编译然后执行,或者单击编译并执行。就是这个原因。编译器会调用现成的编译器对代码进行分析、优化和处理,由于篇幅原因,这里不再详述过程。简而言之,书面的。c代码将被编译成一个以。Exe(在exe(Windows)下或者。Out(在Mac OS下)。

编译C语言之前

编译c语言后

这里可以控制编译器生成汇编语言文件,可以看看它们之间的差距。

上图:C语言和对应的汇编语言。

显然C语言的版本更容易理解。

虽然编译后的语言在执行前会进行分析和优化,而且运行速度很快,但是对于大型程序来说,编译的时间会比较长。所以可以不编译直接运行吗?答案显然是肯定的。这是一种解释型语言,比如Python。

对于解释型语言,不会使用编译器进行翻译,最后生成机器语言的可执行文件,然后执行。它会调用解释器,逐行翻译源文件,并实时将每一行翻译成机器码并执行。这样就不需要编译,执行前的准备时间大大减少。但是由于解释器没有对代码进行优化,每次运行都需要从头解释,所以执行效率不如编译语言。

编译语言还有另一个问题。如上所述,每种架构都有唯一的机器语言,编译的过程就是将代码翻译成机器语言的过程,这就导致每次编译生成的文件只能运行在特定的平台上。能不能编译一次,全平台运行?显然,这也是可能的。这是一种混合语言,比如Java。

这种语言也需要编译,但是编译后生成的不是机器码,而是字节码。通常,这样的语言会被转换成机器代码在运行时执行,或者直接由虚拟机解释和执行。因为是编译成字节码而不是机器码,所以编译后的执行文件是所有平台通用的。

指定数据类型

很多同学在学习C语言的时候可能会疑惑,数据类型有什么用?为了理解这个问题,我们先来看看数据是如何存储的。

数据类型的意义

在内存中,所有的数据都会以二进制存储,也就是以01001001的形式存储。这

些数据仅仅只是0和1而已,所表达的意义都是人为规定的。

通常,第一位会被视为符号位,即0位正,1为负。然而,如果我希望第二位来表示符号位,也完全是符合规定的,只是所有涉及到运算的代码都要重写罢了。而数据类型就是用来规定每一位所代表的意义。

举个例子,在32位系统中,对于int类型而言,第一位表示符号,后31位表示具体的值。而对于float类型而言,尽管第一位也表示符号,但剩下的31位与int类型所表示的意义就不同了。紧接着的8位是指数位,剩下的是尾数,即使用科学计数法表示为尾数*(2^指数)。这里是2的原因是计算机中所有数据都是二进制存储的,而非十进制。

上图:float类型存储方式

这里用01000001000010000000000000000000来展示一下float类型的具体计算。理解这段需要会一些简单的进制转换,如果不会建议自学一下。通过二进制计算器,可以很容易得到它对应的十进制数是1094975488。

对于float类型来说,其指数为10000010,即129,再根据规定减去127,最终得到其指数为2。对于尾数而言,由于一定由1开头,因此最开始的这位1会被省略,因此其尾数实际为100010000000000000000000,即为1.0001*(2^2),换算成十进制为4.25。

如果希望对这两个类型的数据进行简单的加法运算,而不指定数据类型,汇编中会直接进行对位相加,即对应的每一个0或1相加,并加上前一位的进位。这样计算会得到10000010000100000000000000000000,显然不是我们想要的1094975492.25。

如果不指定数据类型,计算就会得到错误的答案。由此可见,在内存中无意义的一串二进制数,我们可以通过规定每一位的意义,来得到不一样的结果。

为什么要指定这么多数据类型

很多同学可能也有这样的疑惑,为什么光一个整数就有short,int,lang三种,浮点数也有float,double两种,甚至还有与int类型对应的char类型呢?只需要int和double不就够用了么?

由于现在的计算机内存普遍充裕,不太会遇到内存空间不足的问题,因此可以直接选用高精度的数据类型进行存储与计算。然而,在多年以前,或是在嵌入式领域,这类存储空间非常紧张的条件下,不同数据类型的差距就显现出来了。

由于在这些条件下,每一个bit都显得弥足珍贵,因此程序员会想方设法地优化存储空间的使用,能够用低精度的就不会用高精度。

而浮点数根据上文对存储方式的解释可以看出,精度越高,其所能表示的大小越小,因此在表示较大的,对精度要求高的数据时,就必须使用高精度的数了,反之则可以用低精度的节省空间。由于float所能表示的精度实在是非常低,因此建议在学校编写C程序时,如无特殊要求,一律使用double类型。

而char类型则较为特殊,可以与整数类型进行相互转换。在单片机等环境中,由于存储空间有限,因此更倾向于使用char这一只消耗一字节的数据类型,而不是int等更大的。另外,char一般用来表示字符,因此如果要表示例如'A'这种字符型的数据时,一般用char类型。char类型在后文有关字符串的部分还会提到。

然而,short类型不一定就比int类型消耗的空间少,long也不一定就比int表示的精度高,一切由编译器决定(只需要遵守2<=short<=int<=long就是符合规定的)。因此如果真的有需要,可以用char来降低消耗,而不是使用short。

上图:数据类型在不同操作系统下的大小

由此可见,虽然常用的数据类型就这么几个,但是其他的类型也都有其存在的意义,可以不用,但不能没有。数据类型一旦确定,该变量在内存中所分配的大小,以及每一位所代表的意义,也就随之确定下来了。

数组与指针

明白数据类型,接下来就可以定义数组了。一个数组是由一定数量的,相同数据类型的变量组成的一种数据结构,也就是说,一个数组可以由一定数量的其他数组组成,而这些数组也可以由数组组成,形成套娃。

在C语言中,数组在定义时必须显式指定其长度与数据类型,而在一些其他语言——如Java、Python中——可以不断扩展数组的长度,但C语言中却不能这样做。这又是为什么呢?这需要从如何在内存中生成一个数组说起。

数组的生成

我们在C语言中创建数组时,会指定数组的数据类型和长度,而编译器可以根据数据类型*sizeof(数据类型)推算出这一数组具体需要占据多大的内存空间,进而在程序运行到这一步,需要创建数组时,为其在内存中申请符合要求的,连续的一段空间进行数组的生成。但为什么要连续的空间,而不能断断续续呢?

数组在访问时,会首先找到其内存地址。数组在创建时的变量名,实际也是一个指向数组第一项的一个指针(后面会讲到)。随后,根据具体访问哪一项,如第n项,就会将这一地址加上n*sizeof(数据类型),就能直接找到这一项的内存地址。因此数组在生成时需要申请连续的内存地址,否则就无法做到这么高效的访问速度。

C语言中的数组与其他语言的数组

那么问题来了:为什么别的语言能做到扩展数组长度,通过变量来初始化数组,而不是通过常数来指定呢?

事实上,在最底层的实现中,它们也是会指定一个具体的值来生成数组,其原理与C语言完全相同。但是在需要更长的数组时,会申请一段更长的连续内存空间来存放新数组,并将原来的旧数组完全复制一份过去。当然,各种语言会存在一定的优化,申请的空间会比所需的空间略大一些,防止重复不断的复制降低运行效率。

上图:数组动态扩容

由于C语言的数组是最原始的数组,语言本身不会自行进行申请新地址,复制旧数组等操作,因此需要在初始化时就指定好长度。

指针的作用

另外一个初学时难以理解的概念就是指针了。先来看下指针到底是什么。指针是一个存放内存地址的变量,也就是说可以直接访问并操作内存。

上图:指针示意图

图中a表示一个整数类型的变量,值为100,在内存中存放在0x0010这一地址中。因此可以定义一个指针x指向这一地址。可以理解为x中存放了0x0010这一地址,访问这一指针就相当于访问这一地址。这就引出了一个问题:既然指针存放的是地址,访问的也是地址,那么为什么还要定义一个类型呢?

原因很简单,因为要取出该地址具体存放的值。前面说过,数据类型决定了该数据所占的大小,以及每一位具体所代表的内容。因此,要取出该地址具体存放的值,必须要知道其数据类型才行。这就是为什么C语言中定义指针时要指定数据类型,指明该内存地址存放数据的具体数据类型。

指针与数组的关系

指针与数组的关系也非常紧密。定义数组时取的名称就是指向数组第一个元素的指针,也就是说,要访问数组a中的a[0],可以直接访问*a。以此类推,可以通过访问*(a+1),*(a+2)来访问a[1],a[2]。这是因为在定义数组时已经指定了数据类型,因此这里的+1就不是简单的加法,而是在指针存储的地址的基础上,加上sizeof(a[0])(这里的sizeof用来获取某一变量在存放时使用的内存大小)。从上图可以看出,每个int类型如果占了4个字节,那么每次+1都会将内存地址+4再访问。

需要注意的是,通过这种方式访问数组会有数组越界的问题。也就是说,如果定义了一个长为n的数组,但是通过*(a+n)来访问第n+1位,C语言并不会有任何的错误提示,只会返回一个存储在该内存地址的,根据定义的数据类型来计算得出的值。很多情况下无法分辨到底是否越界,因此使用这种方式访问需要小心谨慎。

另外,虽然数组名是一个指针,但是是一个常量,因此不能给其赋值。

字符与字符串

之前提到,char类型多用于表示字符。字符串是由字符组成的,其底层是一串由char类型的变量组成的数组,因此可以通过char*或是char[]来生成字符串。赋值时,可以通过数组一个一个字符赋值,也可以通过双引号直接赋值。

在一些其他编程语言中,会专门有一个数据类型String来表示字符串,但在C语言中并没有。因此对字符串的处理就等价于对字符数组的处理。

在处理字符串时需要注意,数组长度是包含最后的\0的,而strlen函数则不会。另外,如果通过数组的方式一个个添加字符,且在最后没有加上\0,那么则由于数组越界进而使得字符串中的数据出现错误。为了防止出现这一错误,最好直接通过双引号进行赋值。另外,不论字符数组有多长,第一次出现\0就代表着字符串的结束。

由于char实际就是一个数字,因此在解决如大小写转换之类的问题时,可以通过+-32来解决。这里的32来自于ASCII码表,每一个数字都对应着一个字符。码表可以在网上轻松找到。如果不记得具体的大小,可以通过格式化输出%d直接查看对应的数字,如果记不得大小写间差了32,可以用'a'-'A'来临时凑合使用一下。

一些要注意的语法格式

老师可能不会着重提语法格式,但是实际上良好的格式能够显著提升代码的可读性,方便理解与找错。

main函数

首先,根据C99标准,main函数应当定义为intmain(void){...}或是intmain(intargc,char*argv[]){...}。前一种在现在学习的阶段更为常用,其中的void一般是可以省略的。但是,最后的return0;是可以被省略的,如果不写将会默认返回0。有些老师或者书上可能会写成voidmain(){...},或是说一定要显示地写出return0;。这些都是错误的。具体标准可以参考标准文档。

main函数定义

上图:main函数返回值

缩进

缩进与换行的使用也是很重要的。{与}应当独占一行,其中所包裹的内容应当进行一次缩进。另外,尽管if语句或是for语句等,如果大括号内只包含一条语句,很多老师会去掉大括号,并写在一行内。这并不是一个好习惯,应当照样换行,加上大括号与缩进,方便阅读与之后的修改。

可以使用在线格式化,或是astyle等格式化软件来进行代码格式化。

其他一些小tips

除了以上的这些老师可能会一笔带过的内容外,还有一些我在编程中所学到的一些小tips:

Warning(敲代码是不可忽略的警告)

希望所有看到这篇文章的、需要学习C语言的同学们能够顺利学好这门课,取得一个好成绩。


对于准备成为一名优秀程序员的朋友,如果你想更好的提升你的编程核心能力(内功),让自己成为一个具有真材实料的厉害的程序员,不妨从现在开始!C/C++,永不过时的编程语言~

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

相关内容

热门资讯

金花创建房间/微信金花房卡怎么... 1.微信渠道:(荣耀联盟)大厅介绍:咨询房/卡添加微信:88355042 2.微信游戏中心:打开微...
金花房间卡/金花房卡如何购买/... 金花房间卡/金花房卡如何购买/新超圣金花房卡正版如何购买新超圣是一款非常受欢迎的游戏,咨询房/卡添加...
牛牛创建房间/金花房卡批发/神... 微信游戏中心:神牛大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...
链接牛牛/牛牛房卡游戏代理/鸿... 鸿运大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
科技实测!牛牛房卡怎么获得/乐... 微信游戏中心:乐酷大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...