Golang 学习笔记-1:变量&函数

变量&函数

最近在学习golang,写下学习笔记提升记忆。为了看起来不是那么枯燥,本学习笔记采用分析代码的形式。

首先搬出我们最经典的第一段代码:

hello world

    package main // 0

import "fmt" // 1实现格式化的 I/O

/* Print something */ // 2
func main() { // 3
fmt.Println("Hello, world; or καλημε ́ρα κóσμε; orこんにちは 世界") // 4
}

首先我们要认识到

每个Go 程序都是由包组成,程序的运行入口是包main

  1. 首行这个是必须的。所有的 Go 文件以 package 开头,对于独立运行的执行文件必须是 package main;

  2. 这是说需要将fmt加入到main。不是main 的包被称为库 末尾以 // 开头的内容是单行注释 Package fmt包含有格式化I/O函数,类似于C语言的printf和scanf

  3. 这也是注释,表示多行注释。

  4. package main 必须首先出现,紧跟着是 import。在 Go 中,package 总是首先出现, 然后是 import,然后是其他所有内容。当 Go 程序在执行的时候,首先调用的函数 是 main.main(),这是从 C 中继承而来。这里定义了这个函数

  5. 调用了来自于 fmt 包的函数打印字符串到屏幕。字符串由 " 包裹,并且可以包含非 ASCII 的字符。这里使用了希腊文和日文、中文"

编译和运行代码

构建 Go 程序的最佳途径是使用 go 工具。 构建 helloworld 只需要:

    1. go build helloworld.go
   # 结果是叫做 helloworld 的可执行文件。
   2. ./helloworld
   # Hello, world; or καλημε ́ρα κóσμε; or こんにちは世界

变量

Go 是静态类型语言,不能在运行期改变变量类型。

变量如果不提供初始化值将自动初始化为零值。如果提供初始化值,可省略变量类型,由编译器自动推断。

    var x int
// 使用关键字 var 定义变量, 跟函数的参数列表一样,类型在后面。
var c, python, java bool
// 多个相同类型的变量可以写在一行。
var f float32 = 1.6
var i, j int = 1, 2
// 变量定义可以包含初始值,每个变量对应一个。
var s = "abc"
// 如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。

变量在定义时没有明确的初始化时会赋值为*零值* 。

零值是:

  • 数值类型为 0 ,

  • 布尔类型为 false ,

  • 字符串为 "" (空字符串)。

在函数内部,可用更简略的 ":=" 式定义变量。

    func main() {
n, s := 12, "Hello, World!"
println(s, n)
}

函数外的每个语句都必须以关键字开始( var 、 func 、等等), := 结构不能使用在函数外。

可一次定义多个变量。

    var x, y, z int
var s, n = "abc", 123
var (
a int
b float32
)

func main() {
n, s := 0x1234, "Hello, World!"
println(x, s, n)
}

一个特殊的变量名是 _(下划线)。任何赋给它的值都被丢弃。在这个例子中,将 35 赋值给 b,同时丢弃 34。

    _, b := 34, 35

Go 的编译器会对声明却未使用的变量报错

    var s string // 全局变量没问题。

func main() {
i := 0 // Error: i declared and not used。(可使  "_ = i" 规避)
}

定义完之后的变量可以被重新赋值 比如第8行,将计算结果赋值给result。

常量

常量值必须是编译期可确定的数字、字符串、布尔值。

常量的定义与变量类似,只不过使用 const 关键字

    const x, y int = 1, 2
const s = "Hello, World!"
// 多常量初始化 // 类型推断
// 常量组
const (
a, b = 10, 100
c bool = false
)

func main() _{
const x = 'xxx' // 未使用局部常量不会引发编译错误
}

在常量中,如果不提供类型和初始化值,那么被看作和上一常量相同

    const (
s = "abc"
x // x = "abc"
)

变量值的引用

通常情况下 go 语言的变量持有相应的值对于通道函数方法映射以及切片的引用变量,它们持有的都是引用,也既是保存指针的变量

值在传递给函数或者方法的时候会被复制一次

不同类型参数所占空间如下:

类型占用空间
bool类型占1~8个字节
传递字符串占 16个字节(64位)或者8个字节(32位)
传递切片占 16个字节(64位)或者12个字节(32位)
传递指针占 8个字节(64位)或者4个字节(32位)

数组是按值传递的,所以传递大数组代价较大 可用切片代替

变量是赋给内存块的名字,该内存块用于保存特定的数据类型

指针是指保存了另一个变量内存地址的变量。创建的指针用来指向另一个某种类型的变量。为了便于理解,我们看以下两段代码。

x := 3    y := 22
// 变量 x, y 为int型 分别赋值 3   22  内存地址 0xf840000148   0xf840000150
x == 3 && y == 22

pi := &x

// 变量pi 为 *int(指向int型变量的指针)   在这里我们将变量x的内存地址赋值给pi,即pi 保存了另一个变量的内存地址(这也是指针定义)

pi == 3 && x == 3 && y == 22
x++

// x + 1 此时 x==4 pi 指向x的内存地址 所以

pi == 4 && x == 4 && y == 22

*pi++

// *pi ++ 意为着pi指向的值增加
*pi == 5 & x == 5 && y == 22

pi := &y

//pi 指向y的内存地址
*pi == 22 && x == 5 && y == 22

*pi++

// *pi++ 意为着pi指向的值增加

*pi == 23 && x == 5 && y == 23

基本类型

Go 有明确的数字类型命名, 支持 Unicode, 支持常用数据结构

类型长度默认值说明
bool1false
byte10unit8
rune40int32 的别名 代表一个Unicode 码
int, unit4 或 8032 或 64
int8, unit810-128 ~ 127, 0~255
int16, unit1620-32768 ~ 32767, 0 ~ 65535
int32, unit3240-21亿~ 21亿, 0 ~ 42亿
int64, unit6480
float3240.0
float6480.0
complex648

complex12816

unitptr4或8
足以存储指针的unit32 或unit64 整数
array

值类型
struct

值类型
string
""UTF-8 字符串
slice
nil引用类型
map
nil引用类型
channel
nil引用类型
interface
nil接口
function
nil函数

intuint 和 uintptr 类型在32位的系统上一般是32位,而在64位系统上是64位。当你需要使用一个整数类型时,你应该首选 int,仅当有特别的理由才使用定长整数类型或者无符号整数类型。引用类型包括 slicemap  channel。它们有复杂的内部结构,除了申请内存外,还需要初始化相关属性

类型转换

go 不支持 隐式的类型转换

使用表达式 T(v) 将值 v 转换为类型 T 。

var b byte = 100
// var n int = b // Error: cannot use b (type byte) as type int in assignment
var n int = int(b) // 显式转换

不能将其他类型当 bool 值使用

a := 100
if a { // Error: non-bool a (type int) used as if condition
println("true")
}

函数

首先看下面这段代码

    package main

import "fmt"

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

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

函数定义

使用关键字 func 定义函数,左大括号不能另起一行

golang中符合规范的函数一般写成如下的形式:

    func functionName(parameter_list) (return_value_list) {

}

// parameter_list 是参数列表
// return_value_list 是返回值列表 下边有详细的讲解

函数的特性
  • 无需声明原型。 (1)

  • 支持不定长变参。

  • 支持多返回值。

  • 支持命名返回参数。

  • 支持匿名函数和闭包。

  • 不支持 嵌套 (nested)、重载 (overload) 和 默认参数 (default parameter)

    func test(x int, y int, s string) (r int, s string) { // 类型相同的相邻参数可合并
n := x + y // 多返回值必须用括号。
return n, fmt.Sprintf(s, n)
}

关键字 func 用于定义一个函数test 是你函数的名字int 类型的变量 x, y 和 string 类型的变量 s 作为输入参数参数用pass-by-value方式传递,意味着它们会被复制两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。

在这个例子中:

x int, y int

被缩写为

x, y int

变量 r 和 s 是这个函数的命名返回值。在 Go 的函数中可以返回多个值。如果不想对返回的参数命名,只需要提供类型:(int, string)。 如果只有一个返回值,可以省略圆括号。如果函数是一个子过程,并且没有任何返回值,也可以省略这些内容。函数体。注意 return 是一个语句,所以包裹参数的括号是可选的。不定长参数其实就是slice,只能有一个,且必须是最后一个。

    func test(s string, n ...int) string {
var x int
for _, i := range n {
x += i
}
return fmt.Sprintf(s, x)
}
// 使用slice 做变参时,必须展开
func main() {
s := []int{1, 2, 3}
println(test("sum: %d", s...))
}

函数是第一类对象,可作为参数传递

就像其他在 Go 中的其他东西一样,函数也是值而已。它们可以像下面这样赋值给变量:

    func main() {
a := func() { // 定义一个匿名函数,并且赋值给 a
println("Hello")
} // 这里没有 ()
a() // 调用函数
}

如果使用 fmt.Printf("\%T\n", a) 打印 a 的类型,输出结果是 func()

返回值

函数可以返回任意数量返回值

Go 函数的返回值或者结果参数可以指定一个名字,并且像原始的变量那样使用,就像 输入参数那样。如果对其命名,在函数开始时,它们会用其类型的零值初始化

    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)
}

/*
      函数可以返回任意数量返回值
      swap 函数返回了两个字符串
   */

Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用。

    package main

import "fmt"

func split(sum int) (x, y int) { // 初始化返回值为 x,y
x = sum * 4 / 9 // x,y 已经初始化,可以直接赋值使用
y = sum - x
return // 隐式返回x,y(裸返回)
}

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

/*
      在长的函数中这样的裸返回会影响代码的可读性。
   */

有返回值的函数,必须有明确的return 语句,否则会引发编译错误

名词解释

函数原型

函数声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型,但是不必对形参命名。这三个元素被称为函数原型,函数原型描述了函数的接口函数原型类似函数定义时的函数头,又称函数声明。为了能使函数在定义之前就能被调用,C++规定可以先说明函数原型,然后就可以调用函数。函数定义可放在程序后面。 由于函数原型是一条语句,因此函数原型必须以分号结束。函数原型由函数返回类型、函数名和参数表组成,它与函数定义的返回类型、函数名和参数表必须一致。函数原型必须包含参数的标识符(对函数声明而言是可选的)注意:函数原型与函数定义必须一致,否则会引起连接错误。

下节预告

变量和函数部分暂时这些,有更新还会补充。下一篇将会是控制流。将会用到的代码为:

    package main

import "fmt"

func main() {
result := 0
for i := 0; i <= 10; i++ {
result = fibonacci(i)
fmt.Printf("fibonacci(%d) is: %d\n", i, result)
}
}

func fibonacci(n int) (res int) {
if n <= 1 {
res = 1
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
return
}

参考链接

Go 指南: https://tour.go-zh.org/basics/4The way to go -- 变量:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/04.4.mdEffective Go:https://golang.org/doc/effective_go.html


最后,感谢女朋友支持和包容,比❤️

想了解以下内容可以在公号输入相应关键字获取历史文章: 公号&小程序 | 设计模式| 并发&协程

640?wx_fmt=jpeg


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值