【Go】C++ 转 Go 第(二)天:变量、常量、函数与init函数

本专栏文章持续更新 ,新增内容使用蓝色表示。

食用指南

本文适合有 C++ 基础的朋友,想要快速上手 Go 语言。可直接复制代码在 IDE 中查看,代码中包含详细注释和注意事项。

Go 的环境搭建请参考以下文章:

【Go】C++ 转 Go 第(一)天:环境搭建 Windows + VSCode 远程连接 Linux -CSDN博客

变量声明与使用

Go 语言对变量的要求非常严格,float32 和 float64 是不同的类型,且没有自动类型转换机制。

C++ vs Go 变量声明对比

特性 C++ Go
类型声明 int a = 5; var a int = 5
类型推导 auto b = "hello"; (C++14+) b := "hello"
多变量声明 int x=1, y=2; x, y := 1, 2
默认值 未初始化是未定义行为 自动零值初始化

注意事项:

Go 中没有 char 类型,使用 byte(ASCII) 和 rune(Unicode)。

变量声明后必须使用,否则编译报错。

:=简短声明只能在函数内部使用。

var.go

Go 复制代码
// 声明变量
package main

import (
	"fmt"
)

// 二、全局变量声明
var e int
var f float64 = 9
var g = "q"

// var h:=1		// := 只能用在函数体内使用

func main() {
	// 一、局部变量声明
	// 默认值是0
	var a int
	fmt.Println("a =", a)
	// go 中没有 char 字符类型,但是有byte (ASCII) / rune (Unicode)

	// 带初始化值
	var b int = 8
	fmt.Println("b =", b)

	// 自动匹配,相当于auto
	// 1. var
	var c = "var c"
	// 使用类型推导可以使用 %v 占位符
	fmt.Printf("c = %v \t", c)    // 非格式化输出,带换行符
	fmt.Printf("type : %T \n", c) // 格式化输出

	// 2. :=
	d := 5.5
	fmt.Printf("d = %v \t", d)
	fmt.Printf("type : %T \n", d)

	fmt.Println("e =", e)
	fmt.Println("f =", f)
	fmt.Println("g =", g)
	// fmt.Println("h =", h)

	// 三、多个变量声明
	// 1. 单行
	// 1) 同一种数据类型
	var a1, a2 int = 56, 78
	fmt.Println("a1 =", a1, ",", "a2 =", a2)

	// 2) 不同数据类型(自动匹配)
	var b1, b2 = "hello", 8.9
	fmt.Printf("b1 = %v \t", b1)
	fmt.Printf("type : %T \n", b1)
	fmt.Printf("b2 = %v \t", b2)
	fmt.Printf("type : %T \n", b2)

	// 2. 多行
	var (
		c1 int  = 1
		c2 bool = false
	)
	fmt.Println("c1 =", c1, ",", "c2 =", c2)

}

执行结果

常量与iota枚举

iota 是 Go 语言的常量计数器,只能在 const 内部使用。

iota 特性

每遇到一个 const 关键字,iota 重置为0

const 声明块中每新增一行,iota 自增1

支持在表达式中使用 iota

const.go

Go 复制代码
package main

import (
	"fmt"
)

// 二、const 定义枚举类型
const (
	apple  = 0
	banana = 1
)

// 可在 const 中添加关键字 iota(只能在const内部使用),第一行的 iota 的默认值是0,之后每行 iota 加1。
// 读法: i o ta
const (
	_         = iota // 跳过 0
	monday           // 1
	tuesday          // 2
	wednesday        // 3
)

const (
	cat   = iota * 2 // 0 * 2
	dog              // 1 * 2
	panda            // 2 * 2
)

const (
	a, b   = iota + 1, iota + 2 // iota = 0, a = iota + 1 = 1, b = iota + 2 = 2
	a1, b1                      // iota = 1, a1 = iota + 1 = 2, b1 = iota + 2 = 3
	a2, b2                      // iota = 2, a2 = iota + 1 = 3, b2 = iota + 2 = 4

	c, d = iota * 2, iota * 3 // iota = 3, c = iota * 2 = 6, d = iota * 3 = 9
	c1, d1
	c2, d2
)

func main() {
	// 一、常量
	const x string = "xian"
	fmt.Println("x =", x)
	// x = "xining"	// 只读

	fmt.Printf("apple=%d, banana=%d \n", apple, banana)
	fmt.Printf("monday=%d, tuesday=%d, wednesday=%d\n", monday, tuesday, wednesday)
	fmt.Printf("cat=%d, dog=%d, panda=%d\n\n", cat, dog, panda)

	fmt.Printf("a=%d, b=%d \n", a, b)
	fmt.Printf("a1=%d, b1=%d \n", a1, b1)
	fmt.Printf("a2=%d, b2=%d \n\n", a2, b2)

	fmt.Printf("c=%d, d=%d \n", c, d)
	fmt.Printf("c1=%d, d1=%d \n", c1, d1)
	fmt.Printf("c2=%d, d2=%d \n", c2, d2)
}

执行结果

函数

特性

  • 参数顺序为参数名在前,类型在后。

  • Go 的返回值中没有 void,不需要返回值时直接省略返回类型。

  • Go 语言支持多返回值。

  • Go 中只有值传递,但可以传递指针来修改原值。

func.go

Go 复制代码
package main

import "fmt"

// 形参的变量名和类型和C++的相反,末尾的是返回值类型。没有返回值,末尾可以不写
// 注意:go中没有void关键字,不需要返回值时直接省略

// go中只有值传递,但是传递的值不同,结果也不同

// 一、返回单个匿名返回值
func test(a int, b int) bool {
	fmt.Println("-------test-------")
	fmt.Println("a =", a, "b =", b)
	c := true
	return c
}

// 二、返回多个匿名返回值
func test1(a int, b string) (int, string) {
	fmt.Println("-------test1------")
	fmt.Println("a =", a, "b =", b)
	return a, b
}

// 三、返回多个有名返回值
// 1. 多个返回值类型不同
func test2(a int, b string) (e float64, f string) {
	fmt.Println("-------test2------")
	fmt.Println("a =", a, "b =", b)

	// 这两个变量也属于函数形参,在未赋值之前使用不会报错,前者默认为0,后者默认为空
	// 防止不可控的野指针、野地址的出现
	fmt.Println("e =", e, "f =", f)

	// go没有隐式类型转换,比如将float64改为float32或者int都会报错
	e = 17.3
	f = "world"
	return
}

// 2. 多个返回值类型相同
func test3(a int, b string) (e, f int) {
	fmt.Println("-------test3------")
	fmt.Println("a =", a, "b =", b)
	return 6, 7
}

// 传递指针,函数内更改会影响原来的值
func change(n *int) {
	*n = 99
}

func main() {
	c := test(1, 2)
	// 注意go中定义的变量必须要使用,否则会报错
	fmt.Println("c =", c)

	// 多返回值时, 可以使用下划线 _ 忽略不需要的返回值
	_, d := test1(3, "hello")
	fmt.Println("d =", d)

	e, f := test2(4, "world")
	fmt.Println("e =", e, "f =", f)

	e1, f1 := test3(5, "word")
	fmt.Println("e1 =", e1, "f1 =", f1)

	fmt.Println("\n======传指针=======")
	// 使用时传地址
	num := 7
	fmt.Println("未调用之前:", num)
	change(&num)
	fmt.Println("调用之后:", num)

}

执行结果

init 函数与包导入

目录结构如下所示:

导入方式说明

  1. 标准导入:通过包名调用导出函数

  2. 匿名导入:只执行包的init函数,不直接使用包中其他函数

  3. 别名导入:为长包名设置简短别名

  4. 导入点:将包中全部方法导入当前作用域(谨慎使用)

lib1/lib1.go

Go 复制代码
package lib1

import "fmt"

func init() {
	fmt.Println("-----lib1.init()-----")
}

// lib1包提供的API
// 以下函数名大写表示对外开放
// 小写则表示当前函数只能再包内调用
func Lib1Test() {
	fmt.Println("-----lib1Test()-----")
}

lib2/lib2.go

Go 复制代码
package lib2

import "fmt"

func init() {
	fmt.Println("-----lib2.init()-----")
}

// lib2包提供的API
// 以下函数名大写表示对外开放
// 小写则表示当前函数只能再包内调用
func Lib2Test() {
	fmt.Println("-----lib2Test()-----")
}

main.go

Go 复制代码
package main

// 暂时采用这种写法
// 导包时会调用对应的 init 函数
import (
	// 从 GOPATH 开始的相对路径
	// 1. 标准导入
	"GoLangStudy/5_initStudy/lib1"
	"GoLangStudy/5_initStudy/lib2"
	// 2. 匿名导入(_+空格)
	// go 对语法要求严格,导包不使用,会报错
	// 如果只执行包的 init() 函数,不直接使用包中的其他函数,可通过匿名导包实现
	//_ "GoLangStudy/5_initStudy/lib2"
	// 3. 别名导入
	// 包名很长的时候可以使用
	// mylib2 "GoLangStudy/5_initStudy/lib2"
	// 4. 导入点(谨慎使用)
	// 将包中的全部方法导到当前包的作用域中,可直接调用API
	// 不同包可能会出现同名函数,不推荐生产上使用
	// . "GoLangStudy/5_initStudy/lib2"
)

func main() {
	// 1. 通过导包,可以调用 lib1 的函数。
	lib1.Lib1Test()
	lib2.Lib2Test()

	// 3. 别名调用方式
	// mylib2.Lib2Test()

	// 4. 导入点
	// Lib2Test()
}

执行结果


如有问题或建议,欢迎在评论区中留言~

相关推荐
-睡到自然醒~4 小时前
[go 面试] 并发与数据一致性:事务的保障
数据库·面试·golang
光头闪亮亮4 小时前
Golang使用gofpdf库和barcode库创建PDF原材料二维码标签【GBK中文或UTF8】及预览和打印
go
科比不来it5 小时前
Go语言数据竞争Data Race 问题怎么检测?怎么解决?
开发语言·c++·golang
驰羽5 小时前
[GO]Go语言包访问控制与导入机制
golang
光头闪亮亮7 小时前
go-fitz库-PDF文件所有页转换到HTML及从HTML中提取图片的示例教程
go
用户855651414467 小时前
环信http请求失败排查
go
像风一样自由20208 小时前
Rust Tokio vs Go net/http:云原生与嵌入式生态选型指南
开发语言·golang·rust
道之极万物灭8 小时前
Go小工具合集
开发语言·后端·golang
不会写DN11 小时前
用户头像文件存储功能是如何实现的?
java·linux·后端·golang·node.js·github