Go语言的基本概念与语法

2023-11-02

包package

每个 Go 程序都是由包组成的.

程序从 main包开始运行.

本程序通过导入路径 "fmt" and "math/rand"来使用这两个包.

按照约定,包名与导入路径的最后一个元素相同。例如,"math/rand" 包中的源码均以packagerand` 语句开始.

注意: 执行这些程序的环境是确定性的 所以每次运行示例程序 rand.Intn 都会返回相同的数字.

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("My favorite number is", rand.Intn(10))
}

Imports(导入)

此代码将导入分组到带括号的"factored(分解)"导入语句中.

您还可以编写多个导入语句,例如:

import "fmt"
import "math"
或者
import (
	"fmt"
	"math"
)

但是使用factored import语句是一种很好的风格.

基本数据类型

bool

布尔值是一组布尔值,truefalse

默认值:false

string

字符串是所有 8 位字节字符串的集合,通常必须表示 UTF-8 编码的文本。字符串可能为空,但 不是零。字符串类型的值是不可变的。

默认值:“”

int

int 是大小至少为 32 位的有符号整数类型。这是一个非重复类型,而不是 int32 的别名。

默认值:0

int类型还有其他相关类型:

  • int8:是所有有符号 8 位整数的集合。 范围:-2 ^8 到 2 ^8 - 1。
  • int16:是所有有符号 16 位整数的集合。 范围:-2 ^16 到 2 ^16 - 1。
  • int32:是所有有符号 32 位整数的集合。 范围:-2 ^32 到 2 ^32 - 1。
  • int64:是所有有符号 64 位整数的集合。 范围:-2 ^64 到 2 ^64 - 1。
  • uint:一种无符号整数类型,大小至少为32位。这是一个不同的类型,而不是 uint32 的别名。
  • uint8:是所有无符号 8 位整数的集合。 范围:0 到 2 ^8 - 1。
  • uint16:是所有无符号 16 位整数的集合。 范围:0 到 2 ^16 - 1。
  • uint32:是所有无符号 32 位整数的集合。 范围:0 到 2 ^32 - 1。
  • uint64:是所有无符号 64 位整数的集合。 范围:0 到 2 ^64 - 1。
  • uintptr:是一个整数类型,它足够大,可以容纳任何指针。

注意:
int, uint, and uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽. 当你需要一个整数值时,你应该使用 int 除非你有特定的理由使用一个固定大小或无符号的整数类型。

byte

字节是 uint8 的别名,在所有方面都等效于 uint8。用于区分字节值和 8 位无符号 整数值。

rune

rune 是 Int32 的别名,在所有方面都等同于 Int32。用于区分字符值和整数值。

float

  • float32:float32 是所有 IEEE-754 32 位浮点数的集合。
  • float64:float64 是所有 IEEE-754 64 位浮点数的集合。

complex

复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。

  • complex64:是所有复数的集合,float32 实数和虚部。
  • complex128:是所有复数的集合,float64 实数和虚部。复数的默认类型。

变量

变量声明

var 语句用于声明一个变量列表,跟函数的参数列表一样,类型在最后。

var 语句可以出现在包或函数级别。

语法:var i int

var声明可以包含初始化程序,每个变量一个。如果存在初始化器,则可以省略类型;

在函数内部, 可以使用 := 短赋值语句来代替具有隐式类型的 var 声明。

但是,在函数外部, 每个语句都以关键字 (var, func, 等) 因此 := 结构不可用。

觉得每行都用 var 声明变量比较烦琐?没关系,还有一种为懒人提供的定义变量的方法:

var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)

使用关键字 var 和括号,可以将一组变量定义放在一起。

变量的初始化

可以这么写:var hp int = 100 也可以省略int直接这么写:var hp = 100 下边可以有为什么可以这么写的原因。还有短变量初始化操作: hp := 100

匿名变量

匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

常量

常量像变量一样声明,但使用 const 关键字。常量可以是字符、字符串、布尔值或数字值。不能使用 := 语法声明常量。

数字常量是高精度 。无类型常量采用其上下文所需的类型。(一个 int 最多可以存储 64 位整数,有时更少.)

和变量声明一样,可以批量声明多个常量:

const (
    e  = 2.7182818
    pi = 3.1415926
)

iota 常量生成器

常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

for语句

for语法结构

Go 只有一个循环结构,即 for 循环.

基本 for 循环包含三个由分号分隔的组件:

  • init 语句: 在第一次迭代之前执行
  • 条件表达式:在每次迭代前求值
  • post 语句:在每次迭代结束时执行

init语句通常是一个简短的变量声明,并且在那里声明的变量仅在该 for 语句的范围内可见.

一旦布尔条件评估为 false ,循环将停止迭代。init 和 post 语句是可选的;如果省略循环条件,它将永远循环,因此可以紧凑地表示无限循环.

注意: 与 C、Java 或 JavaScript 等其他语言不同,for 语句的三个组件周围没有括号,并且大括号 { } 始终需要。

package main

import "fmt"

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}

for 是Go中的while

C和Java 的 while 在 Go 中叫做 for

package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

if语句

Go 的 if 语句就像它的 for 循环;表达式不需要用括号 ( ) 括起来,但需要大括号 { } .

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

就像 forif 语句可以在条件之前以短语句执行。语句声明的变量作用域仅在 if 之内。

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

If 和 else

if 短语句中声明的变量也可以在任何 else 块中使用。

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// can't use v here, though
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

switch

switch 语句是编写一连串 if - else 语句的简便方法。它运行其值等于条件表达式的第一个 case。

Go 的 switch 类似于 C、C++、Java、JavaScriptPHP 中的 switch,只是 Go 只运行选定的case,而不是所有后续case。

实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。另一个重要的区别是 Go 的 switchcase 不需要是常量,所涉及的值也不需要是整数。

package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
		case "darwin":
			fmt.Println("OS X.")
		case "linux":
			fmt.Println("Linux.")
		default:
			// freebsd, openbsd,
			// plan9, windows...
			fmt.Printf("%s.\n", os)
	}
}

switch 的求值顺序

switch 的 case 语句从上到下顺次执行,直到匹配成功时停止。 如果 i==0不调用 f

switch i {
case 0:
case f():
}

注意: Go playground 场中的时间总是从 2009-11-10 23:00:00 UTC 开始。

没有条件的switch

没有条件的 Switch 同 switch true 一样。这种构造可以是编写长 if-then-else 链的一种干净方式.

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

defer语句

defer 语句推迟函数的执行,直到周围的函数返回。

延迟调用的参数会立即计算,但直到周围的函数返回时才会执行函数调用。

package main

import "fmt"

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

Stacking defers(defer栈)

延迟的函数调用被推送到栈上。当函数返回时,其延迟调用将按后进先出顺序执行。

package main

import "fmt"

func main() {
	defer fmt.Println("world")
	fmt.Println("hello")
}

结果:

hello
world

go中关键字

Go语言中的关键字一共有 25 个:

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

类型转换

在 Go 中不同类型的项之间的赋值需要显式转换。比如

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

或者

i := 42
f := float64(i)
u := uint(f)

类型推断

当声明一个变量而不指定显式类型 (通过使用 := syntax 或 var = 表达式语法), 变量的类型是从右侧的值推断出来的.

当右值声明了类型时,新变量的类型与其相同:

var i int
j := i // j is an int

但是,当右侧包含无类型的数字常量, 新的变量可以是 int, float64, 或 complex128 取决于常量的精度:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

Go函数

一个函数可以接受零个或多个参数.

在本例中,add 接受两个 int 类型的参数.

请注意,类型在变量名 之后 .

package main

import "fmt"

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

当两个或多个连续命名函数参数共享一个类型时,除了最后一个之外,可以省略所有类型.

在这个例子中,我们缩短

x int, y int
可以写成
x, y int

Multiple results(多值返回)

一个函数可以返回任意数量的结果。该 swap 函数返回两个字符串。

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

Named return values(命名的返回值)

Go 的返回值可以命名。如果是这样,它们将被视为在函数顶部定义的变量。

这些名称应用于记录返回值的含义。

不带参数的 return 语句返回命名的返回值,这被称为“裸”返回。

裸返回语句应该只在短函数中使用,就像这里显示的例子一样。它们会损害较长函数的可读性。

package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func main() {
	fmt.Println(split(17))
}

函数值

函数也是值。它们可以像其他值一样传递,函数值可以用作函数参数和返回值

package main

import (
	"fmt"
	"math"
)

func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

函数闭包

Go 函数可以是闭包。闭包是引用其主体外部的变量的函数值。该函数可以访问并分配给引用的变量;从这个意义上说,函数是“绑定”到变量的.

例如, adder 函数返回一个闭包。每个闭包都绑定到它自己的 sum 变量.

Go语言nil:空值/零值

在Go语言中,布尔类型的零值(初始值)为 false,数值类型的零值为 0,字符串类型的零值为空字符串"",而指针、切片、映射、通道、函数和接口的零值则是 nil。

nil 是Go语言中一个预定义好的标识符,有过其他编程语言开发经验的开发者也许会把 nil 看作其他语言中的 null(NULL),其实这并不是完全正确的,因为Go语言中的 nil 和其他语言中的 null 有很多不同点。

nil 标识符是不能比较的

这点和 python 等动态语言是不同的,在 python 中,两个 None 值永远相等。对于 nil 来说是一种未定义的操作。

nil 不是关键字或保留字

nil 并不是Go语言的关键字或者保留字,也就是说我们可以定义一个名称为 nil 的变量,比如下面这样:var nil = errors.New("my god")

虽然上面的声明语句可以通过编译,但是并不提倡这么做。

nil 没有默认类型

package main
import (
    "fmt"
)
func main() {
    fmt.Printf("%T", nil)
    print(nil)
}

运行结果如下所示:

.\main.go:9:10: use of untyped nil

不同类型 nil 的指针是一样的

package main
import (
    "fmt"
)
func main() {
    var arr []int
    var num *int
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p", num)
}

结果:
0x0
0x0

通过运行结果可以看出 arr 和 num 的指针都是 0x0。

nil 是 map、slice、pointer、channel、func、interface 的零值

零值是Go语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。

不同类型的 nil 值占用的内存大小可能是不一样的

一个类型的所有的值的内存布局都是一样的,nil 也不例外,nil 的大小与同类型中的非 nil 类型的大小是一样的。但是不同类型的 nil 值的大小可能不同。

具体的大小取决于编译器和架构,上面打印的结果是在 64 位架构和标准编译器下完成的,对应 32 位的架构的,打印的大小将减半。


--------------------------------------------------------------欢迎叨扰此地址---------------------------------------------------------------

本文作者:Java技术债务
原文链接:https://cuizb.top/myblog/article/detail/1687832300
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go语言的基本概念与语法 的相关文章

随机推荐

  • vue json数据可视化展示

    使用vue json viewer插件 官网地址 https www npmjs com package vue json viewer 安装vue json viewer插件 npm install vue json viewer sav
  • 文件映射mmap简单设置文件大小(lseek (ftruncate可以设置文件大小))__使用mmap即文件映射实现文件的快速复制代码

    lseek fd pagesize 10 100 SEEK SET lseek应该是文件指针移动到的位置 why mmap1是文件的长度呢 lseek 是获取文件的长度 移动到最后 则是文件的总长 如lseek fd 80 1 write
  • js获取当前时间(昨天、今天、明天)

    1 时间格式化 1 昨天的时间 2 var day1 new Date 3 day1 setTime day1 getTime 24 60 60 1000 4 var s1 day1 getFullYear day1 getMonth 1
  • 【牛客网机试】写出一个程序,接受一个十六进制的数,输出该数值的十进制表示。

    题目描述 写出一个程序 接受一个十六进制的数 输出该数值的十进制表示 输入描述 输入一个十六进制的数值字符串 注意 一个用例会同时有多组输入数据 请参考帖子https www nowcoder com discuss 276处理多组输入的问
  • 起名字!

    五一假期没有出去 闲来找了以下名字 仅以参考 黄哲轩 黄凯轩 黄良宇 黄思禾 黄景龙 黄作湛 黄沐东 黄阳棣 黄骁辉 黄浩锭 黄雷星 黄海城 黄文冰 黄箫程 黄海泽 黄鑫群 黄子铭 黄嘉涛 黄智豪 黄之维 黄逸鑫 黄培祺 黄圣东 黄曙橙 黄
  • 快速入门高斯过程(Gaussian process)回归预测

    前言 这篇文章主要是教会你如何快速了解高斯过程进行回归预测的 并没有太多的公式推导 只有简单的相关的概念的介绍 如果您要自己掌握并使用高斯过程进行一个简单的预测 当然还需要进行一些基础知识学习的 我会在文章最后推荐一些博主有关高斯过程详细介
  • 【Rust 基础篇】Rust Never类型:表示不会返回的

    题解 牛群的重新排列 import java util public class ListNode int val ListNode next 题解 二叉树之寻找第k大 考察二叉树的深度优先遍历 二叉搜索树中序遍历后可以得到升序的序列 所以
  • 常见算法 - 按大小合并多个有序链表

    leetcode 23 Merge k sorted linked lists and return it as one sorted list Analyze and describe its complexity Example Inp
  • csdn百科

    csdn编辑 CSDN创立于1999年 是中国最大的IT社区和服务平台 为中国的软件开发者和IT从业者提供知识传播 职业发展 软件开发等全生命周期服务 满足他们在职业发展中学习及共享知识和信息 建立职业发展社交圈 通过软件开发实现技术商业化
  • 【逻辑】笔面试

    1 二进制问题 1 1 毒药问题 问题 有1000个一模一样的瓶子 其中有999瓶是普通的水 有1瓶是毒药 任何喝下毒药的生命都会在一星期之后死亡 现在你只有10只小白鼠和1个星期的时间 如何检验出哪个瓶子有毒药 首先一共有1000瓶 2的
  • web 基本标签

    浏览器 内核 渲染引擎 js引擎 外壳 5大常用浏览器 chrome 谷歌 内核 webkit blink FireFox 火狐 内核 Gecko Safiri 苹果 内核 webkit opear 欧朋 欧洲 挪威 内核 Presto I
  • k8s进阶篇-云原生存储ceph

    第一章 Rook安装 rook的版本大于1 3 不要使用目录创建集群 要使用单独的裸盘进行创建 也就是创建一个新的磁盘 挂载到宿主机 不进行格式化 直接使用即可 对于的磁盘节点配置如下 做这个实验需要高配置 每个节点配置不能低于2核4G k
  • zz: C++中构造函数或析构函数定义为private

    2019独角兽企业重金招聘Python工程师标准 gt gt gt C 中构造函数或析构函数定义为private 转自 http www blogjava net fhtdy2004 archive 2009 05 30 278971 ht
  • webpack4.x下babel的安装、配置及使用

    前言 目前 ES6 ES2015 这样的语法已经得到很大规模的应用 它具有更加简洁 功能更加强大的特点 实际项目中很可能会使用采用了ES6语法的模块 但浏览器对于ES6语法的支持并不完善 为了实现兼容 就需要使用转换工具对ES6语法转换为E
  • Oracle SQL Developer 连接oracle报错ORA-01017: invalid username/password; logon denied

    安装了oracle客户端和PL SQL Developer 来实现对oracle数据库的可视化操作 在使用在登录PL SQL无法登录 提示错误 ORA 01017 invalid username password logon denied
  • 代理IP与Socks5代理:跨界电商的智能引擎与安全卫士

    一 背景介绍 跨界电商是在全球范围内销售商品和服务的新兴模式 但随之而来的是复杂多变的市场环境和网络安全挑战 代理IP和Socks5代理为企业提供了智能化的数据采集引擎和强大的网络安全防护 二 代理IP 智能引擎助力市场洞察 多地区数据获取
  • MySQL进阶篇

    MySQL进阶篇 一 存储引擎 1 MySQL体系结构 2 存储引擎简介 3 存储引擎特点 4 存储引擎选择 5 总结 二 索引 1 索引概述 2 索引结构 3 索引分类 4 索引语法 5 SQL性能分析 6 索引使用 6 1 验证索引效率
  • sql如何比较两个数据表的所有数据是否相等

    select 表A minus select 表B
  • IDEA中JDK的配置与Maven的配置详解

    1 在项目结构中配置JDK的版本 1 将Moudle SDK改为1 8 version 1 8 注 这是自己的SDK 而Project SDK是系统自带的SDK 不是自己的SDK 2 选择自己的1 8版本并配置好JDK主路径 2 在设置 构
  • Go语言的基本概念与语法

    文章目录 包package Imports 导入 基本数据类型 bool string int byte rune float complex 变量 变量声明 变量的初始化 匿名变量 常量 iota 常量生成器 for语句 for语法结构