level 15
看某些新手问题实在火大,但有些提问方式和态度似乎又没问题……总算受捕鸟了……
(即兴发挥,发贴框直接手打。估计还有后续所以先占个编号。
有问题楼中楼,
插楼的删。)
前面的不止适用于C/C++。
2012年12月02日 04点12分
1
level 15
1 前置条件
语文其实挺重要,这个没问题,但容易被忽视。当然,如果不是经常要折腾文档,要求不高;但起码要能说清楚话。
数学重要,主要是广度,作为快速学习相关领域知识的基础。深度上面可深可浅,
若只是学习语言,初中水平的基础足够。(不过要用标准库的complex什么的当然不止了。)而大部分人缺的要补的数学并不是学校里的内容,更偏向于类似小学奥数之类。专业点的说法是离散数学(其实也只是最开始的一些基础)。当然,数学基础好的有些东西(比如逻辑运算)理解起来会简单一点。
英语重要是对于长远发展,主要在于两点:一是非英文资料比较落后匮乏,二是质量上普遍是英文的高。翻译过来的质量参差不齐,名著还好说,稍微冷门点的东西就没底了。而且,很多用词不统一,对初学来说是个小麻烦。水平要求其实也不高,不用CET4,看熟了就行。
至于学习语言本身,分得清52个字母就行。(不过标准库的ctype和locale等的使用需要有这些常识。)
2012年12月02日 04点12分
2
回复 两仪离火 :很多情况下大小写字母不算是同一个
2012年12月02日 07点12分
说得到位且妥当
2012年12月02日 11点12分
level 15
2 语言和实现
一般意义上的语言包括文化等方面,这里不提。这里说的最一般的语言是指形式语言,是数学分支研究的内容,如果不是这方面专业或者要实现一个语言,可以先略过。但是至少应该清楚要学的是什么。作为编程语言,研究方法和一般的语言是不一样的,因为有很多特定于具体语言的内容,无法通用。但是,对于编写所有运行于计算机上的程序所需要的语言(主要的是编程语言)来说,有些公共的内容的常识性理解是必要的。这里首要的一个概念是语言的实现,简称实现。
所谓实现,按中文来说至少有两个含义。第一个含义是指过程(implementating),另一个是这种过程的结果(implementation)。通常单独的“实现”作为名词指后者。实现的过程粗略地概括就是让一个语言表达的意义在某个特定设备上体现出来的手段。广义来说,这里的“设备”可以指人脑,那么实现过程就是学习和理解的过程;实现的结果与之对应,即掌握——当然,一般只讨论对于计算机来说的狭义含义。对计算机来说,编程语言的典型实现分为编译型和解释型实现,实现的结果就是一个特定的环境(可能包括操作系统等),尤其是编译器/解释器等关键的程序;而实现作为过程就是指使环境从无到有。
特别应该注意的是
,
语言不是实现,尽管很多时候“语言”可以包含语言实现的含义——这仅是作为统称,且是在不会混淆的前提下。
2012年12月02日 04点12分
3
回复 candy76041820 :差异仍然存在,因为没有人能把语言规则全部塞到程序里。比如对于C++,什么时候ISO C++用C++而不是英语写了,就差不多没区别了。就算这样,让别人学某种语言总不能直接把实现的源码扔过去吧。
2012年12月02日 13点12分
level 15
3 计算机语言的一些简单分类
按用途来说,计算机使用的语言可以分为通用的(general-purposed)和特定于领域的(domain-specific)。
编程语言按抽象的能力通常可以笼统地分为低级语言和高级语言。低级语言就是所谓的1GL(1st-generation language)即机器语言,以及2GL即汇编(2nd-generation language)。从作为3GL(3rd-generation language)的
算法语言开始,就是通常所说的高级语言。4GL和5GL并不是常见的通用程序语言,更侧重于特定用途或问题的描述,而不是问题的解——由于目前技术的限制,4GL和5GL编程并不能用于直接通用地解决问题,因此3GL仍然大行其道,
所谓的“编程”通常也就是指编写3GL程序。
C和C++是通用编程语言,且是
两种不同
的3GL。不过因为无论是语言还是实现都有相似之处。
通用语言对于DSL(domain-specific language)而言,一般适合解决问题的范围更广,但对于特定领域的问题,往往不如DSL。例如,对关系数据库的操作一般使用的结构化查询语言(SQL)就是一种DSL。
2012年12月02日 04点12分
4
orz...漏了一句:不过因为无论是语言还是实现都有相似之处,之后本文在允许同时适用于C和C++语言的情况下统称为C/C++。
2012年12月02日 05点12分
回复 Padme0Amidala :这样不行。可以指出参考文献、可以提出〔需要继续理解〕、可以给出一个侧面的解释并说明这只是一个侧面,但不可以故意犯错。
2012年12月02日 05点12分
回复 Padme0Amidala :帝球的角度不是Lippman的角度,所以才有可看处。
2012年12月02日 05点12分
level 15
4 编程语言的实现方式
编程语言的实现方式可以分为编译型实现和解释型实现。两者的一个显著的直观区别就是,前者的实现包含编译器(compiler),而后者的实现包含解释器(interpreter)。编译器和解释的输入是源代码(source code),一般是被人类可读的文本。
编译器负责把源代码文本转换为目标代码(object code),在机器上直接加载运行。解释器则直接按字面上的顺序逐步加载源代码(一般就是文本)并立即执行,它在运行时直接根据这些代码执行相应操作以体现程序的行为。
两种实现方式各有优缺点。编译型实现适合深度优化,而解释型实现对语言特性的限制比较少。因此一个语言选择是编译型实现还是解释型实现,除了需要考虑环境外,还要看语言特性支持。
尽管C/C++本身
并不禁止解释型实现,但语言特性注定了编译型实现更有实用价值,实现技术也比较成熟。
一种效果介于编译型和解释型之间的流行的语言实现方式是进程虚拟机(process virtual machine),或应用程序虚拟机(application virtual machine),它在宿主环境(一般是一个操作系统上)作为一个进程运行,模拟物理机器,但它运行的代码不是物理机器的处理器(CPU/GPU/coprocessor等)原生支持的机器指令,一般是特定的字节码(bytecode)。进程虚拟机提供高级语言执行环境和宿主环境的隔离,因此运行的目标代码即是跨平台的。注意它和解释器的不同:加载的不是源代码文本。最早的著名的进程虚拟机是用于实现Java语言的JVM(Java virtual machine)。虚拟机运行的字节码经过JIT(just-in-time)编译后,性能可以接近编译型实现。
注意虚拟机作为程序,和语言类似,也具有对应的实现。虚拟机或者虚拟机实现都不保证和语言一一对应。例如Java语言可以在JVM的实现如
Java HotSpot VM上实现,也可以在Dalvik VM(Android所使用的就是这个,它不符合JVM Specification,因此不是JVM)上实现;而JVM可以也可以实现Scala等其它语言。微软提交标准化的公共语言运行时CLR则更明显,可以在上面实现C
#、C++/CLI、F#
等各种语言。
实现字节码表达的行为的程序称为字节码解释器(bytecode interpret)。有些字节码解释器虽然不强调环境隔离,某种意义上也可以算虚拟机,如Emacs Lisp VM。字节码并不只用于通用语言实现,还可能隐藏于特定应用的底层,如TrueType bytecode interpreter。
另外一种比较特殊的实现方式(其实可以算是使用方式)是借助宿主语言(host language)。语言实现对宿主语言提供接口,然后宿主语言中可以使用这些接口以字符串的方式对语言片段进行操作。C/C++作为宿主语言,一个简单的例子是stdlib的system()函数,它的参数字符串作为脚本语言交给命令行环境(如*NIX shell或Windows的命令行解释器)执行。其它可以以C/C++作为宿主的有SQL、Lua等。
2012年12月02日 05点12分
6
回复 candy76041820 :拼接查询字符串什么的也算。但不包括需要额外预处理器的嵌入式SQL、LINQ什么的,那直接是扩展得到的语言,倒真是实现而不只是实用方式了。
2012年12月02日 08点12分
回复 异次元代码 :看不懂就先跳过。这种死的玩意儿迟早会懂。
2012年12月02日 13点12分
回复
@幻の上帝 :我也是这样想的,所以我直接从指针跳到win32 去了,现在只能从新看书了。。。。
2012年12月02日 13点12分
level 15
续4
可以看出,无论是编译型实现和解释型实现都涉及到把一种代码(源代码)转换为另一种代码(目标代码)的过程。这种过程统称为
翻译。实现翻译的程序称为
翻译器(translator)。上述实现方式对应的代码转换程序是翻译器的特例:
若一个翻译器把高级语言翻译为低级语言,这个翻译器是编译器;
若一个翻译器把高级语言翻译并使之紧接被执行,这个翻译器是解释器;
若一个翻译器把低级语言翻译为高级语言,这个翻译器是反编译器(decompiler);
若一个翻译器把汇编语言翻译为机器语言,这个翻译器是汇编器(assembler);
若一个翻译器把机器语言翻译为汇编语言,这个翻译器是反汇编器(disassembler)。
此外也有实现逆向过程的与不直接用于计算机上实现语言(如把一种高级语言转换成直接供人类阅读的文本)的翻译器。
对于用户而言,平时所使用的编译器,实际上是包含一组翻译程序在内的程序集。这些程序的输入是文本的源代码,输出的是机器语言的目标代码,因此能被整体抽象为一个编译器。接收用户最初输入的程序(如GCC的gcc和g++,VC++的cl)往往被误称为编译器,这是不
正确的
。它的主要功能是调用编译及其它相关过程需要的程序,确切的说法是
编译器驱动器
(compiler driver)。
2012年12月02日 05点12分
7
也有叫编译驱动程序的 虽然觉得听上去就不知道在说什么的
2012年12月02日 06点12分
感觉没基础的会看的很懵懂,话说吧cl等看成输入c++输出二进制机器码的编译器不也行么,就不用管中间有多少遍了
2012年12月02日 06点12分
总觉得有点问题呢…比如clang有个driver,该二进制程序名字也是clang…那clang可以指它,不也可以指整个编译器?
2012年12月02日 07点12分
我说的是翻译C到LLVM Assembly的那一整块…
2012年12月02日 07点12分
level 15
续4 2
翻译器一次接受的输入被称为翻译单元(translation unit)。翻译单元往往也会在语言规范中被明确定义,并且可能在一个程序中允许出现多个翻译单元,如:
ISO C11(N1570)
5.1.1.1 Program structure
1 A C program need not all be translated at the same time. The text of the program is kept in units called source files, (or preprocessing files) in this International Standard. A source file together with all the headers and source files included via the preprocessing directive #include is known as a preprocessing translation unit. After preprocessing, a preprocessing translation unit is called a translation unit. Previously translated translation units may be preserved individually or in libraries. The separate translation units of a program communicate by (for example) calls to functions whose identifiers have external linkage, manipulation of objects whose identifiers have external linkage, or manipulation of data files. Translation units may be separately translated and then later linked to produce an executable program.
ISO C++11
2.1/1 1 The text of the program is kept in units called source files in this International Standard. A source file together with all the headers (17.6.1.2) and source files included (16.2) via the preprocessing directive #include, less any source lines skipped by any of the conditional inclusion (16.1) preprocessing directives, is called a translation unit. [ Note: A C++ program need not all be translated at the same time. —end note ]
2012年12月02日 06点12分
9
level 15
5 语言实现的典型组成和程序的典型实现过程
对于C/C++支持多个翻译单元的语言,在编译得到多组目标代码后还需要组合成为完整的程序,称为
链接(linking)。编译型实现的链接过程一般由独立于编译器外的
链接器(linker)实现。(对于具体实现,有时可能还需要考虑
装载器(loader)的行为。)
编译型实现一般是包含编译器驱动器、被编译器驱动器调用的翻译器、链接器在内的多个可执行程序和若干库的组合。
解释型实现则相对容易做得比较紧凑,比如单一的可执行的解释器(当然环境往往需要其它程序或操作系统提供支持)。
借助宿主实现时,宿主程序可以只存在被实现的语言的代码,通过一个可调用的对应的外部的实现(基本上都是解释器)执行;但若语言能够实现得足够轻便那么也可以直接让宿主程序具备实现语言的功能(比如直接链接到某些库上)。
此外,程序运行时的某些公共行为(例如动态地获取类型信息)只能在运行时确定,由
运行时环境(runtime environment)提供支持。其中的程序除了可能存在的虚拟机外,一般实现为供用户程序调用的库,称为
运行时库(runtime library)。
若编译得到一个简单程序,只需要向编译器驱动器传递信息指定要编译和连接的代码以及需要调用的选项。但是,若有更复杂的需求(例如单独指定每个翻译单元调用的翻译器),直接调用编译器驱动器就力有不逮了,通常需要调用编译器中的各个翻译和其它辅助程序以完成程序的生成。
2012年12月02日 07点12分
13
level 15
6 使用语言实现以外的工具
可以理解,程序的翻译单元一多,手动调用的翻译程序命令行的重复工作也越多,低效易错。解决方法是使用
构建系统(build system)完成
自动化构建(build automation)。(确切地说,这里只是
按需自动化(on-demand automation),此外还可以定时构建等等。)
原理是很简单的:生成程序的流程无非是那么几种,那么把重复的工作交给工具自动完成——还可以生成程序以外的东西,例如文档。
由于需求的复杂,命令行往往是不够的,需要脚本DSL来辅助。最流行的手法之一是使用makefile,通过make工具调用。这在大型(尤其是*NIX背景的)项目中可能起到非常重要的作用。
make可能仍然不够自动化,还有automake等工具。make可能过于复杂而不直观,有CMake等替代……
对一些不大的程序,用make这样的命令行构建工具可能不够简便,加上编写程序时自然还有使用编辑器和其它工具的需要,一个提供图形用户界面(GUI)的集成开发环境(Integrated Development Environment, IDE)可能是更好的选择,如Visual Studio、Code::Blocks、Qt Creator、Dev-C++、CodeLite、Eclipse、NetBeans、KDevelop,等等。IDE提供的GUI允许用户不用记忆具体工具的命令行用法,但对于熟练用户可能没什么优势,而且对系统开销一般较大,所以他们宁可使用编辑器+命令行来编写程序。
这里的要点是,
IDE不属于实现
,虽然和计算机上的语言实现同属软件范畴。当然,
IDE更不可能和语言等同。事实上,绝大多数IDE可以供用户选择切换多种语言和语言实现。
其它如版本控制、设计工具等和编码的直接联系较少,学习时可以酌情使用。
2012年12月02日 08点12分
14
好文章,收藏了,虽然看不懂
2012年12月02日 08点12分
level 15
扩展阅读A 可复用的语言实现的一般架构
语言实现有许多相似之处。通常需要考虑到多个目标平台(target);而相同的语言,文法和语义规则检查,逻辑上是一致的。这样,实现可以分为对应语言的前端(front end)和对应目标平台的后端(back end)。这样的架构还允许不同的前端共用后端,减少跨平台语言实现的工作量。GCC的后端(注意GCC同时是总的实现(GNU Compiler Collection)和C编译器(GNU C Comiler)——包含C的前端和公用的后端的名称)、LLVM、JVM、CLR等都支持多个前端。
若实现中前端和后端一一对应,它们之间的交互可以被隐藏,只用于实现内部。否则,这样的交互一般通过中间代码(也可以说是语言,和被实现的语言并不对应,如对于C++原生实现有GCC的GENERIC和GIMPLE、LLVM IR等,对于使用VM的Java实现有JVM bytecode、Dalvik bytecode等)来进行,前端的输出成为后端的输入。
实用的实现需要考虑优化。若存在中间代码,部分优化操作可保持和被实现的语言无关,集中对中间代码进行,这部分实现称为中端(middle end)或(中间代码)优化器(optimizer),实际上的架构是前端-中端-后端。包括GCC和LLVM在内的许多传统编译型实现都使用这种架构。
2012年12月02日 08点12分
15
level 13
新手真的需要学这些么,我不是学院派的,我还是更倾向于一些可以解决实际问题的东西,而不是虚无缥缈的东西
2012年12月02日 13点12分
28
细节不全需要掌握,但是入门了至少应该能看得懂,以后有相关问题也不至于连搜什么都不知道。
2012年12月02日 13点12分
我觉得重要的是:1、留心注意任何实践的同时补充理论,并以理论为指导,2、培养一种严谨的思维方式,3、培养一种有条理、逻辑性的思维方式。不知道你有没有体会,反正我有,有些问题,在有足够理论知识时,则不成为问题;而缺乏足够理论时,疑惑就产生了,且不容易解。一时半会儿没办法举例。。。
2012年12月02日 13点12分
回复 幻の上帝 :话说什么叫“解决实际问题”?最解决实际问题的嘛,就是总是能随便拿到别人写好的程序,可以直接用得舒服,你根本不用考虑怎么折腾这些玩意儿。
2012年12月02日 13点12分
另外一个体会,例如说解决实际问题1、2时,缺乏相关理论,则不能发现其实问题1和2存在类同之处,然后将其当做两个问题去解决,必须如此这般解决100个类似或者1000个类似的问题,积累到一定量,才悟出来,其中的关联。对这类情况,若辅以一定的理论补充,只需再多10个类似问题,就能很快举一反三
2012年12月02日 13点12分