当前位置:网站首页>ZUCC_编译语言原理与编译_实验01 语言分析与简介
ZUCC_编译语言原理与编译_实验01 语言分析与简介
2022-06-24 06:59:00 【星星不想卷】
编译语言原理与编译实验报告
| 课程名称 | 编译语言原理与编译 |
|---|---|
| 实验项目 | 语言分析与简介 |
描述
Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go的语法接近C语言,但对于变量的声明有所不同。Go支持垃圾回收功能。Go的并行计算模型是以东尼·霍尔的通信顺序进程(CSP)为基础,采取类似模型的其他语言包括Occam和Limbo,Go也具有这个模型的特征,比如通道传输。通过goroutine和通道等并行构造可以建造线程池和管道等。在1.8版本中开放插件(Plugin)的支持,这意味着现在能从Go中动态加载部分函数。
与C++相比,Go并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能,但增加了切片(Slice) 型、并发、管道、垃圾回收功能、接口等特性的语言级支持。Go 2.0版本将支持泛型,对于断言的存在,则持负面态度,同时也为自己不提供类型继承来辩护。
不同于Java,Go原生提供了关联数组(也称为哈希表(Hashes)或字典(Dictionaries))。
缺陷
在发表 Go 语言 2.0 的草案时,官方称没有泛型、异常处理与模块对于 Golang 发展造成很大的阻碍, 等同承认 Golang 没有这些特色是设计错误。
发展历史
2007年,Google设计Go,目的在于提高在多核、网络机器(networked machines)、大型代码库(codebases)的情况下的开发效率。当时在Google,设计师们想要解决其他语言使用中的缺点,但是仍保留他们的优点。
静态类型和运行时效率。(如:C++)
可读性和易用性。(如:Python 和 JavaScript)
高性能的网络和多进程。
设计师们主要受他们之间流传的“不要像C++”启发。
Go于2009年11月正式宣布推出,版本1.0在2012年3月发布。之后,Go广泛应用于Google的产品以及许多其他组织和开源项目。
在2016年11月,Go(一种无衬线体)和Go Mono 字体(一种等宽字体)分别由设计师查尔斯·比格洛和克莉丝·荷姆斯发布。两种字体均采用了WGL4,并且依照着 DIN 1450 标准,可清晰地使用了 large x-height 和 letterforms 。
在2018年8月,本地的图标更换了。然而,Gopher mascot 仍旧命相同的名字。
在2018年8月,Go的主要贡献者发布了两个关于语言新功能的“草稿设计——泛型和异常处理,同时寻求Go用户的反馈。Go 由于在1.x时,缺少对泛型编程的支持和冗长的异常处理而备受批评。
语言特征
编程范式

go是经典的函数式编程、静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,展示了函数式编程下的代码扩展能力,以及函数的相互和随意拼装带来的好处。
特征
- stateless:函数不维护任何状态。函数式编程的核心精神是 stateless,简而言之就是它不能存在状态,你给我数据我处理完扔出来,里面的数据是不变的。
- immutable:输入数据是不能动的,动了输入数据就有危险,所以要返回新的数据集。
优势
- 没有状态就没有伤害。
- 并行执行无伤害。
- Copy-Paste 重构代码无伤害。
- 函数的执行没有顺序上的问题。
因为没有状态,所以代码在并行上根本不需要锁(不需要对状态修改的锁),所以可以拼命地并发,反而可以让性能很不错
go语言独有特征
- Go语言反对函数和操作符重载(overload)
- Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数(virtual function)和虚函数重载。
- Go语言也放弃了构造函数(constructor)和析构函数(destructor)
- 在放弃了大量的OOP特性后,Go语言送上了一份非常棒的礼物:接口(interface) 。
撰写风格
Go有定义如下的撰写风格
- 每行程序结束后不需要撰写分号(;)。
- 大括号({)不能够换行放置。
- if判断式和for循环不需要以小括号包覆起来。
- 使用 tab 做排版
除了第二点外(换行会产生编译错误),在不符合上述规定时,仍旧可以编译,但使用了内置gofmt工具后,会自动整理代码,使之符合规定的撰写风格。
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}

项目架构
Go的工作区位于GOPATH,其目录结构如下:
| 目录 | 用途 |
|---|---|
| src | 引用的外部库 |
| pkg | 编译时,生成的对象文件 |
| bin | 编译后的程序 |
轻型协程
Go的主要特色在于易于使用的并行设计,叫做Goroutine,透过Goroutine能够让程序以异步的方式执行,而不需要担心一个函数导致程序中断,因此Go也非常地适合网络服务。
package main
import (
"io"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}

Goroutine是类似线程的概念,属于纤程(区别于协程和线程)。线程属于系统层面,通常来说创建一个新的线程会消耗较多的资源且管理不易;而协程的主要作用是提供在一个线程内的并发性,却不能利用多个处理器线程。而 Goroutine就像轻量级的线程,一个Go程序可以执行超过数万个 Goroutine,并且这些性能都是原生级的,随时都能够关闭、结束,且运行在多个处理器线程上。一个核心里面可以有多个Goroutine,透过GOMAXPROCS参数你能够限制Gorotuine可以占用几个系统线程来避免失控。
在内置的官方包中也不时能够看见Goroutine的应用,像是net/http中用来监听网络服务的函数实际上是创建一个不断执行循环的Goroutine;同时搭配了epoll 等IO多路复用机制维护Goroutine的事件循环。
数据类型
| 类型 | 描述 |
|---|---|
| 布尔型 | 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true |
| 数字类型 | 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码 |
| 字符串类型 | 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本 |
| 派生类型 | 指针类型(Pointer)、数组类型、结构化类型(struct)、Channel 类型、函数类型、切片类型、接口类型(interface)、Map 类型 |
package main
import "fmt"
func main() {
var a = uint(12345678)
fmt.Println(a)
var b = int(-12345678)
fmt.Println(b)
var c = float64(123.123)
fmt.Println(c)
var d = complex128(-123 + 123i)
fmt.Println(d)
var e = byte(123)
fmt.Println(e)
var f = rune(-123123)
fmt.Println(f)
var g = uintptr(123)
fmt.Println(g)
}

转换类型
go 不支持隐式转换类型,比如 :
package main
import "fmt"
func main() {
var a int64 = 3
var b int32
b = a
fmt.Printf("b 为 : %d", b)
}
该程序报错无法通过编译,但是如果改成 b = int32(a) 就不会报错了:

数组
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i, j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j])
}
}

指针
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。
package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15Hm14Cy-1655718082655)(http://owem-pictures.oss-cn-hangzhou.aliyuncs.com/img/image-20220302085947428.png)]
结构体
数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "123"
Book1.author = "456"
Book1.subject = "789"
Book1.book_id = 10
/* book 2 描述 */
Book2.title = "321"
Book2.author = "654"
Book2.subject = "987"
Book2.book_id = 100
/* 打印 Book1 信息 */
printBook(&Book1)
/* 打印 Book2 信息 */
printBook(&Book2)
}
func printBook(book *Books) {
fmt.Printf("Book title : %s\n", book.title)
fmt.Printf("Book author : %s\n", book.author)
fmt.Printf("Book subject : %s\n", book.subject)
fmt.Printf("Book book_id : %d\n", book.book_id)
}

切片
切片是对数组的抽象。数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
package main
import "fmt"
func main() {
var numbers = make([]int,3,5)
printSlice(numbers)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

MAP
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
package main
import "fmt"
func main() {
var countryCapitalMap map[string]string /*创建集合 */
countryCapitalMap = make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
countryCapitalMap [ "India " ] = "新德里"
/*使用键输出地图值 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [country])
}
/*查看元素在集合中是否存在 */
capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */
/*fmt.Println(capital) */
/*fmt.Println(ok) */
if (ok) {
fmt.Println("American 的首都是", capital)
} else {
fmt.Println("American 的首都不存在")
}
}

接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}

错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
f, err := os.Open(filename)
if err != nil {
log.Println("Open file failed:", err)
return
}
defer f.Close()
... // 操作已经打开的f文件
defer关键字
其一是 defer 关键字。 defer 语句的含义是不管程序是否出现异常,均在函数退出时自动执行相关代码
函数允许多个返回值
大多数函数的最后一个返回值会为 error 类型, error 类型只是一个系统内置的 interface
type error interface {
Error() string
}
package main
import (
"fmt"
)
// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider: 0 `
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}

并发
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}

通道
通道(channel)是用来传递数据的一个数据结构。通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 发送到通道 c
}
func main() {
s := []int{
7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从通道 c 中接收
fmt.Println(x, y, x+y)
}

应用领域
Go语言主要应用在以下领域:
1. 服务器编程,主要包括处理日志、数据打包、虚拟机处理、文件系统等;
1. 分布式系统、数据库代理器等;
1. 网络编程,包括Web应用、API应用、下载应用等;
1. 数据库,可以实现数据库的组件实现;
1. 云平台,目前国外很多云平台在采用Go开发。
边栏推荐
猜你喜欢

The article takes you to understand the security of Windows operating system and protect your computer from infringement

2022年流动式起重机司机特种作业证考试题库及在线模拟考试

蓝桥杯_N 皇后问题

More than observation | Alibaba cloud observable suite officially released

Swift 基础 闭包/Block的使用(源码)

OC extension detects whether an app is installed on the mobile phone (source code)

2022 tea artist (intermediate) work license question bank and online simulation examination

Small sample fault diagnosis - attention mechanism code - Implementation of bigru code parsing

About the iframe anchor, the anchor is offset up and down, and the anchor has page display problems Srcdoc problem of iframe

Question 4 - datepicker date selector, disabling two date selectors (start and end dates)
随机推荐
11--无重复字符的最长子串
疫情下更合适的开发模式
Transformers pretrainedtokenizer class
Swift 基礎 閉包/Block的使用(源碼)
搜索与推荐那些事儿
Solve the problem of notebook keyboard disabling failure
Optimization and practice of Tencent cloud EMR for cloud native containerization based on yarn
Pagoda panel installation php7.2 installation phalcon3.3.2
2021-03-11 comp9021 class 8 notes
List of Li Bai's 20 most classic poems
Opening chapter of online document technology - rich text editor
对于flex:1的详细解释,flex:1
For a detailed explanation of flex:1, flex:1
自动化测试的未来趋势
Final review and key points of software process and project management
487. number of maximum consecutive 1 II ●●
Swift foundation features unique to swift
Unity culling related technologies
[ACNOI2022]不是构造,胜似构造
LabVIEW查找n个元素数组中的质数