当我们第一次学习Java时,我们的老师总是告诉我们Java的优势之一:跨平台特性。相反,为什么C/CPP语言不能跨平台?作者查阅了以下内容,印象深刻。本文由两部分组成:
1.C语言程序不是跨平台的原因
2. 简要了解 Java 的字节码文件结构
作者对计算机系统和原理的理解有限,因此理解存在错误。同时感谢线上高手们的无私经验分享~
从类文件中深入了解 Java 字节码结构
汇编语言和高级语言
为什么C不能跨平台
深入了解 Java 虚拟机 (3) – 字节码文件格式和类加载
可执行文件的诞生
众所周知,无论你用什么语言实现Hello World程序,计算机能理解的最后一件事一定是由纯二进制代码组成的机器语言。包括我们此刻的QQ聊天,或者腾讯发布会,这些程序都只是CPU级别的一系列0101码。如果深入硬件级别,它是一个非门电路。
感谢这么多很棒的IDE(例如Visual Studio,Code Blocks),使编译.c文件变得容易和愉快,我们只需单击运行按钮,源文件就会被手动编译和执行,例如hello.c:one
#include
int main(){printf("hello,c!");return 0;}
IDE 实际上为我们执行了此过程:
总之,这个Hello.c经历了预处理、编译、汇编、链接的过程,最后生成出一个可以直接执行的二进制文件。
汇编语言
汇编语言是用于电子计算机、微处理器、微控制器或其他可编程组件的低级语言。在不同的设备中,汇编语言对应于不同的机器语言指令集。汇编语言专用于某种计算机系统结构,不像许多高级语言,它可以在不同的系统平台之间移植。
在计算机诞生之初(直到今天),计算机只知道 0 或 1。那个时代的科学家只能通过在纸带上打孔来告诉计算机:孔代表1,反之亦然是0。这样,人机交互终于实现了,但效率却催人泪下。
科学家们已经意识到,无论程序多么复杂,计算机指令的数量都是有限的,唯一的区别是数据的差异。该命令需要由芯片固定和识别,芯片是使用由管组成的非栅极电路的组合来实现的。
所以科学家后来发明了汇编语言,针对这种固定的指令,用符号代替繁琐的01代码,代码的可读性得到了很大的提高。
在汇编程序之后
完成后,需要一个特殊的程序(汇编程序)将编译后的汇编程序编译为 0 和 1。
但一个新的问题出现了:不同公司生产的CPU芯片有不同的指令集(比如以Intel为代表的复杂指令集阵营,以ARM为代表的简化指令集阵营,不同公司设计指令集的思路完全不同)。
高级语言
随着程序变得越来越多
复杂,当时的程序员越来越渴望一种新的编程语言:它不依赖于计算机硬件,人们可以使用统一的书写方法来实现编程,而不管计算机模型如何。
1954年,第一个成熟的中级编程语言FORTRAN诞生了。从那时起,编程已经从特定机器的限制中移除。自第一种编程语言诞生以来,已经诞生了数百种中间语言,如C,BASIC,CPP,包括Java。
当我们使用C进行编程时,我们首先使用编译器将源文件编译成汇编器,然后使用汇编器将其编译为0,1。一个可执行文件诞生了。
交叉硬件是不够的
尽管中间语言帮助程序员实现了跨硬件的飞跃,但随着操作系统的普及,问题出现了:相同的.c文件在不同的平台上可能以不同的方式运行。
原因是不同平台使用的编译器并不完全相同。例如,一些编译器为相同 int 类型的数据分配 2 个字节的空间,而另一些编译器为其分配 4 个字节的空间。因此,在平台 A 中正常运行的程序在平台 B 上可能会出现内存溢出错误。
在不同的平台上,同一个Hello.c的最终编译的文件格式是不同的:
我们在Windows上使用的应用程序是编译程序
大多数网民都在使用或使用Microsoft的Windows系统。俗话说,需求是有市场的:软件开发人员在Windows平台上开发软件,最后向用户交付一个包含可执行文件.exe的文件夹。
这个.exe文件其实是从Windows平台下的源文件编译生成的可执行二进制文件,Windows用户运行它们自然没有问题。
为了用户体验,用户只关心一件软件就可以正常使用。出于盈利原因,一些供应商不发布他们的源代码,因为这相当于向其他人宣布了所有技术细节。
但是,当放置在 Linux 平台下时,此.exe及其依赖项能否正常工作?恐怕不是。
有时需要自动编译源代码
对于开源软件,官网通常会提供软件的源码文件下载包,或者留下GitHub链接。
开发人员有时会从官方网站下载源文件,并在执行之前通过其平台的本机 C 编译器(例如 gcc 等工具)编译二进制文件,这些文件需要花费平台可执行文件,特别是对于那些使用 Linux 作为开发环境的人。这就好比去市场卖菜
:市场卖的是一样的菜,根据大家的口味,这些同样的菜是出手做的,做的菜也不一样。
在计算机中,这种差异突出了平台或处理器型号,因此用同一 C 语言编译的代码在不同的环境中可能会以不同的方式运行。由于这种差异,C/CPP 语言不是跨平台的。在这些情况下,我们必须下载源代码并使用相应的编译工具自动编译它,使其适应我们的操作环境。
例如,如果我们想在 Linux 环境中使用 Redis 工具,我们需要先使用 make 命令编译它,然后才能使用它。
Hadoop还建议下载源代码并在使用前使用编译器进行编译。
考虑到性能问题和缺少单独的Java泛型,Hadoop为各个组件提供了自己的本机实现。这些组件存储在Hadoop中单独的动态链接存储库中。这个库在 *nix 平台上被称为 libhadoop.so
Hadoop是用Java开发的,但是有一些要求和操作不适合Java,因此引入了原生库的概念。坦率地说,Hadoop的各个功能必须与Java类文件和通过JNT的本机代码生成的库文件一起工作。要在 Linux 系统上运行本机代码,必须首先将本机编译为目标 CPU 体系结构的 [.so] 文件。不同的处理器架构需要编译对应平台的动态库[.so]文件才能正确执行,所以最好重新编译一次Hadoop源代码,让[.so]文件对应自己的处理器。
本地库,
通常翻译为本机库或本机库简易C语言编译器源码,是用 C/C++ 编译的动态库 [.so],通过 JNI(Java 原生接口)机制为 Java 层提供了一个出口。应用程序通常提供套接字,用于在 C/C++ 中实现相关逻辑并编译到较低层或其他模块的库中,以调用性能和安全考虑。
链接到此声明
Hadoop的官方网站解释了原生库
跨平台语言Java诞生了
通过前三节,我们了解到 C/C++ 语言一直受到跨平台的限制。有没有办法让高级语言跨平台运行?
我们考虑实际应用环境;如果后端正在传输 XML 格式的文件,而后端最终需要以 JSON 格式存储数据,该怎么办?让我们创建一个适配器适配器并在数据传输之前转换XML和json文件!这是典型的适配器设计模式。
Java伴随着它的虚拟机JVM而来。它与C/CPP语言的最大区别在于,首先,Java代码在JVM虚拟机上运行。其次,编译此 Java 代码不是生成纯二进制机器指令,而是生成非二进制字节码文件.class。并且此文件与计算机的硬件或平台无关,仅与JVM有关。
真正赋予Java跨平台特性的是JVM虚拟机。
只有JVM才能理解.class字节码文件的真正含义,将其正确转换为机器代码并执行。换句话说,无论是在Linux还是Windows上,只要安装了适配的JVM虚拟机,Java代码就可以顺利执行。
对于Java程序员来说,他不再需要考虑javac编译的字节码文件应该运行哪个平台,反正他只需要安装相应平台的JDK环境:不管那个平台的JVM来执行代码,都要求字节码文件的结构是相同的格式。
笔者认为,虚拟机和虚拟容器技术后续发展的核心思想与JVM没有什么不同,即:适配器设计模式。
JVM 机器如何理解类文件?
井。。。。。。研究JVM也是一种形而上学。由于作者的水平和精力有限,笔者决定在随后的JVM研究中补充这部分的详细内容。
在我的第一篇博客中简易C语言编译器源码,我简要提到了.class文档的内容。(为什么用txt打开.class文件会乱码,而打开.java文件却没有?这一次,让我们从不同的角度看一下.class文件的内部:
首先我们需要安装 NotePad++,然后安装 HexEditor_0.9.6_x64 插件,以确保我们以十六进制格式读取.class文件。具体安装很简单,笔者就不赘述了。
我们使用NotePad++编译一个简单的JavaClass .java文件:
class JavaClass {
public static void main(String[] args){
System.out.println("Hello Java");
}
}
然后通过javac生成相应的Java .class文件并打开它:
一个简单的代码句子被压缩成 16 个符号中的一大串数字和字母。 其中 xx 表示 1 个字节。由于 JVM 也可以基于此符号将 Hello Java 输出到控制台,因此意味着此符号必须违反特定规则:即 Java 虚拟机规范。
NotePad++添加了HEX编辑器插件
字节码结构
我们可以参考这个表结构来翻译我们的 HelloJava .class文件。
类型名称描述宽度
U4
魔法
幻数,识别类文件格式
4 字节
U2
minor_version
次要版本号
2 字节
U2
major_version
主版本号
2 字节
U2
constant_pool_count
恒池计算器
2 字节
cp_info
constant_pool
恒定池
n 字节
U2
access_flags
访问标志
2 字节
U2
this_class
类索引
2 字节
U2
super_class
父类索引
2 字节
U2
interfaces_count
接口计数器
2 字节
U2
接口
接口索引集合
2 字节
U2
fields_count
字段数
2 字节
field_info
领域
字段集合
n 字节
U2
methods_count
方法计数器
2 字节
method_info
方法
方法的集合
n 字节
U2
attributes_count
其他属性计数器
2 字节
attribute_info
属性
附加属性的集合
n 字节
根据 Java 字节码结构表,我们可以解析 NotePad++ 显示的十六进制文件中包含的信息。此外,我们可以从这个表中看到.class文件只有两种数据类型:无符号数字和表。
数据类型定义说明
无符号号码
无符号数字可用于描述数字、索引引用、数量值或根据 UTF-8 编码的字符串值。
其中无符号数字是基本数据类型。U1、U2、U4 和 U8 分别表示 1 字节、2 字节、4 字节和 8 字节
桌子表
是由多个无符号数字或其他表组成的复合数据结构。
所有表都以“_info”结尾。由于表格没有固定的宽度,因此它们通常后跟数字说明。
从这两个表中,我们可以看到,即使是 HelloWorld 级别的.class也包含如此多的内容。表中的每个结构都值得讨论,当我即将研究.class和 JVM 原则时,我将仔细研究它。
当然,我们也可以使用javap命令来剖析。 javap 是 JDK 自带的反编译工具,可以解析当前类对应的代码区(汇编指令)、局部变量表、异常表和代码行偏移映射表、常量池等信息。
$ javap -verbose JavaClass.class
该程序最终将返回:
Classfile /C:/Users/liJunhu/Desktop/x学习笔记/Scala分支/scalaTest/JavaClass.class
Last modified 2020-5-28; size 422 bytes
MD5 checksum 75803102330020b05d871810e7dbe63c
Compiled from "JavaClass.java"
class JavaClass
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // Hello Java
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // JavaClass
#6 = Class #22 // java/lang/Object
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 JavaClass.java
#15 = NameAndType #7:#8 // "":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 Hello Java
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 JavaClass
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
JavaClass();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello Java
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
}
SourceFile: "JavaClass.java"
一个小小的复活节彩蛋
让我们仔细看看这个十进制 16 文件的前 4 个字节:
嗯,它的价值0xCAFE宝贝。它的价值和名字一样有趣。幻数是用于区分文件类型的符号,通常由文件的前几个字节表示。例如0xCAFE BABE 表示类文件,那么为什么不使用文件名后缀呢?由于文件名后缀很容易更改,为了保证文件的安全性,在文件内部写入文件类型可以保证它不被篡改。至于为什么类文件使用0xCAFE宝贝...
文章永久链接:
简介 1.1 什么是C语言?
C是一种面向过程的抽象通用编程语言,广泛用于低级开发。
C语言可以以简单的形式编译和处理低级内存。C 是一种高效的编程语言,仅形成少量的机器语言,可以在没有任何运行时环境支持的情况下运行。虽然C提供了许多低级处理功能,但它仍然是跨平台的,以标准大小编写的C程序可以在许多计算机平台上编译,包括嵌入式处理器和超级计算机等操作平台。
冯.诺依曼在1945年提出了几个现代计算机的想法,后来被称为冯曼。诺依曼思想,这是计算机发展史上的一个里程碑。自1945年至今,大部分采用其结构,因此冯.诺依曼被称为计算机之父。他的建筑计算机由五个组件组成:运算器、控制器、存储器、输入设备和输出设备。C语言拥有完整的理论体系,经过漫长的发展历史,在编程语言中具有举足轻重的地位。
C 是
出生于日本贝尔实验室,由D.M. Ritchie基于B语言开发,在其主要设计完成后,Thompson和Ritchie完全重绘了UNIX,随着UNIX的发展,C语言也不断建立。为了便于C语言的全面推广,众多专家学者和硬件厂商共同组成了C语言标准委员会,并于1989年诞生了第一个完整的C标准,简称“C89”,即“ANSIc”。
1.2 C语言与其他编程语言的比较
C 不同于面向对象的编程语言,如 C++ 和 Java。C 的目标是提供一种编程语言,该语言可以以简单形式编译,处理低级内存,形成少量机器代码,并在没有任何运行时支持的情况下运行。C语言描述问题的速度比汇编语言快简易C语言编译器源码,工作量少,可读性强,易于调试、修改和移植,代码质量堪比汇编语言。C语言通常只比汇编语言代码生成的目标程序低10%~20%。为此,C可以对系统软件进行编程。
现阶段,在编程领域,C语言使用非常多,具有中间语言汇编语言的优点,与其他编程语言相比有很大的优势。计算机系统设计和应用程序编程是C语言应用的两个主要领域。同时,C语言的普遍适用性强,可以应用于许多计算机操作系统,效率明显。
一般来说,每种编程语言都有自己的优点和价值;C是一种硬件友好的语言,可用于对操作系统进行编程,因此C适合开发追求运行速度并充分发挥硬件性能的程序。
记住:毕竟语言只是工具,算法是核心,思想是灵魂。
用任何编程语言开发程序都是让计算机做指定的事情,比如:删除单个文件、下载文件、编译文档等;计算机的CPU只知道机器指令,所以虽然不同的编程语言差别很大,但都要“翻译”成CPU可以执行的机器指令。而不同的编程语言,做同样的事情,编译的代码量,也大不相同。
1.3 C语言的特点
(1)语言简洁,紧凑,使用方便灵活。C共有32个关键词,9个控制句,程序编写自由,压缩了所有不必要的组件。
(2)运营商丰富。
(3)数据类型丰富,具有现代语言的各种数据结构。
(4)有结构化的控制句。
(5)语法限制不严格,程序设计自由度大。
(6)C语言允许直接访问数学地址、位运算,可以实现汇编语言的大部分功能,可以直接在硬件上运行。
(7)生成的目标代码质量高,程序执行效率高。C 的效率通常仅比汇编程序生成的目标代码低 10%-%20。
(8)用C语言编写的程序是可移植的,可以在各种平台上运行,无需太多更改。
1.4 C语言应用的现状
(1)C语言几乎是操作系统内核开发领域唯一的开发工具,大多数操作系统都是由C语言加上少量汇编语言开发的;例如:Linux,Windows,Vxworks,Unix。
(2)在嵌入式领域具有绝对优势。
(3)Apache、Oracle在网络服务器类方面有相当的优势。
(4)使用C的GUI应用和大规模商业程序的应用很多。例如:Offices,SPSS,AutoCAD。
(5)大规模、高性能的评估、游戏开发,以及一些传统的客户端软件和预制组件。
1.5 编译C代码的推荐编辑器
C语言
是一种跨平台的编程语言,在Windows系统中,Linux系统是可以学习的,下面介绍学习C语言,编译C语言代码是比较方便的一些工具软件。
(1)可视化工作室代码
Microsoft 2015 年 4 月 30 日的 Build 开发者大会上宣布了 Visual Studio Code 项目:一个跨平台源代码编辑器,用于编写在 MacOSX、Windows 和 Linux 上运行的现代 Web 和云应用程序。
下载地址:
图 1-5-1
图 1-5-2
(2)记事本++
记事本(Notepad)是Windows中用于文本编辑的代码编辑器或小程序,在文本编辑方面可与Windows写字板相媲美。 是一个开源的,小型的,免费的纯文本编辑器。
下载地址:
图 1-5-3
(4)崇高文字
SublimeText3是一款流行的代码编辑器软件和高级文本编辑器,适用于在Linux,Windows和MacOSX上运行的HTML和诗歌。它也是许多程序员喜欢使用的文本编辑器软件。
下载地址:
图 1-5-4
二、在Windows系统下构建C语言学习环境2.1,安装VSCode代码编辑器
Visual StudioCode是Google的跨平台源代码编辑器,它可以轻松地用各种编程语言编写代码。
下载地址:
图 2-1-1
图 2-1-2
下载安装包后,直接双击键盘运行。
图 2-1-3
图 2-1-4
图 2-1-5
图 2-1-6
图 2-1-7
图 2-1-8
图 2-1-9
图 2-1-10
安装软件后简易C语言编译器源码,以下设置 Visual Studio 支持英语
首先打开Visual Studio软件并按F1或Shift+Ctrl + P:
然后在命令行上键入配置显示语言
图 2-1-11
选择安装语言选项。
图 2-1-12
安装完成后,右下角有重启提示,单击重启。
图 2-1-13
图 2-1-14
软件发布后安装
完毕后,创建一个新的.c文件并保存到指定目录下;此时,软件右下角会提示安装C/C++扩展支持,单击安装。
下面介绍修改颜色 vscode 的颜色主题。
图 2-1-15
图 2-1-16
2.2Mingw-w64编译器下载
VSCode 只是一个编辑器,而不是 IDE(集成开发环境);编译器(和许多其他功能)不包括在内,要编译 C/C++ 程序,您需要单独下载编译器。
在Windows下,Mingw-w64工具集通常用于构建C语言开发环境;
Mingw-w64 在 Windows 下提供了一个 C 开发环境,工具集包括头文件、库、运行时和一些工具,支持 64 位开发,是 MinGW 的升级项目。
虽然明·
和MinGW-w64只是子名称,它们是两个不同的项目;MinGW本身已经很久没有更新了,所以不推荐。
官方 Vscode 说明和使用文档:
窗口下的MinGW离线安装包下载地址:
提取代码:5m6k
注意:这是 win10_64 位系统的 MinGW。
图 2-2-1
下载的软件包的名称:i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z
2.3 为系统环境变量添加编译器路径建议
将压缩包解压到指定目录,建议存储在C盘上,在C盘创建名为“MinGW”的目录。
图 2-3-1
将 gcc/g++ 可执行文件的路径添加到系统环境变量中。
图 2-3-2
图 2-3-3
图 2-3-4
图 2-3-5
图 2-3-6
图 2-3-7
图 2-3-8
图 2-3-9
2.4 测试编译器
打开 vscode 编译代码,编写代码,在终端中编译运行。
图 2-4-1
图 2-4-2
2.5 vscode 终端中的 PowerShell 简介
Windows下的Vscode外部终端使用PowerShell。
图 2-5-1
PowerShell,作为
名称可以知道,它首先是一个shell,shell的意思与Linux bash相同,并且原始cmd是通过在其上键入命令(可执行文件)来使用的;
而力量意味着他是一个强大的外壳,从用户的角度来看,我个人认为它的力量突出在以下几个方面:
(1)谷歌心态。谷歌正在真正实现PowerShell,包括Office等更多的是自己的软件,底层叫做PowerShell来实现。
PowerShell包含前一个cmd的所有命令,原始命令的使用方式相同,但命令被添加到其之上。
(3)基准测试Linux。PowerShell使用LinuxShell的思想,即所有系统操作和配置都可以通过在shell中键入命令来实现。
(4)统一的命令格式和独立的文档。基于前3点,我们可以说PowerShell可以与LinuxBash等竞争,如果加上后来者的优势,你可以相信PowerShell能够成功。
其实PowerShell很好,但它也有它的缺点:
(1)Linux和Windows系统本身定位的差异。Linux的自由稳定性使其牢牢占据服务器领域,LinuxShell命令没有很多统一的格式;工程师们很难学会这一点,当Linux占用工程师的大部分精力并养成他们的习惯时,工程师似乎没有学习PowerShell的精神和动力。
(2)来自Windows GUI的竞争。GUI可以做Windows上的命令也可以更有效地完成的所有事情,但普通用户不想去一个昏暗的界面来键入感觉不受控制的命令。
2.6VSCode英语输出乱码解决方案
文件 >> 首选项 >> 设置
搜索:
"files.autoGuessEncoding": false
改为:
"files.autoGuessEncoding": true
然后出去将.c代码文件改为GB2312编码保存,再使用VSccode打开即可。
图 2-6-1
图 2-6-2