一、文件名 & 关键字 & 标识符
-
1.所有go源码以.go结尾
-
2.标识符以字母或下划线开头,大小写敏感
-
3._是特殊标识符,用来忽略结果
-
4.保留关键字
二、Go程序的基本结构
package main import "fmt" func main() { fmt.Println("hello world") }
1. 任何一个代码文件隶属于一个包
2.import 关键字,引用其他包
import ("fmt") import ("os") 通常习惯写成 import ( "fmt" "os" )
3.golang可执行程序,package main, 并且有且只有一个main入口函数
4.包中函数调用
a.同一个包中函数,直接调用
b.不同包中函数,通过包名+点+函数名进行调用
5.包访问控制权限
a.大写意味着这个函数/变量是可导出的
b.小写意味着这个函数/变量是私有的,包外部不能访问
小练习
1. 写一个程序,对给定一个数字n,求出所有两两相加等于n的组合
package main import "fmt" func list(n int) { for i := 0; i <= n; i++ { fmt.Printf("%d+%d=%d\n", i, n-i, n) } } func main() { list(10) } // go build -o bin/list.exe go_dev/day2/example1/main
2. 一个程序包含两个包add和main,其中add包中有两个变量:Name和age。请问main包中如何访问Name和age?
package add // 方式一 // var Name string = "hello world" // var Age int = 10 // 方式一 // var Name string = "hello world" // var Age int = 10 // 方式二 var Name string var Age int func Test() { Name = "hello wrold" Age = 20 }
package main import ( "fmt" "go_dev/day2/example2/add" ) func main() { // 方式一 直接调用 // fmt.Println("Name=", add.Name) // fmt.Println("Age=", add.Age) // 方式二 先调用函数 add.Test() fmt.Println("Name=", add.Name) fmt.Println("Age=", add.Age) }
3. 包别名的应用,开发一个程序,使用包别名来访问包中的函数?
package main import ( "fmt" a "go_dev/day2/example2/add" ) func main() { // 方式一 直接调用 // fmt.Println("Name=", add.Name) // fmt.Println("Age=", add.Age) // 方式二 先调用函数 // add.Test() // fmt.Println("Name=", add.Name) // fmt.Println("Age=", add.Age) // 包的别名 a.Test() fmt.Println("Name=", a.Name) fmt.Println("Age=", a.Age) }
4. 每个源文件都可以包含一个init函数,这个init函数自动被go运行框架调用。开发一个程序演示这个功能?
package add // 先执行全局变量,在执行init函数, 在执行main函数 // 方式一 // var Name string = "hello world" // var Age int = 10 // 方式二 var Name string var Age int func Test() { Name = "hello wrold" Age = 20 } func init() { Name = "hello wrold" Age = 20 }
package main import ( "fmt" // "go_dev/day2/example2/add" a "go_dev/day2/example2/add" ) func main() { // 方式一 直接调用 // fmt.Println("Name=", add.Name) // fmt.Println("Age=", add.Age) // 方式二 先调用函数 // add.Test() // fmt.Println("Name=", add.Name) // fmt.Println("Age=", add.Age) // 包的别名 // a.Test() // fmt.Println("Name=", a.Name) // fmt.Println("Age=", a.Age) // init fmt.Println("Name=", a.Name) fmt.Println("Age=", a.Age) }
5. 包的只初始化,不引用。请开发一个程序,演示这个做法
package test import "fmt" var Name string = "hello world" var Age int = 1000 func init() { fmt.Println("Name=", Name) fmt.Println("Age=", Age) Age = 10 fmt.Println("Age=", Age) fmt.Println("this is a test....") }
package add import ( _ "go_dev/day2/example2/test" )
三、函数声明和注释
1.函数声明:func 函数名字(参数列表)(返回值列表){}
2. 注释,两种注释,单行注释://和多行注释 /* */
四、常量和变量
1. 常量
- (1). changliang使用const修饰,代表永远是只读的,不能修改
- (2). const只能修饰boolean,number(int相关类型、浮点类型、complex)和string
- (3). 语法:const identifier[type] = value,其中type可以省略
- (4). 比较优雅的写法
const ( a = 0 b = 1 c = 2 )
- (5). 更加专业的写法
const ( a = iota b // 1 c // 2 )
练习
1. 定义两个常量Man=1和Female=2,获取当前时间的秒数,如果能被Female整除,则在终端打印female,否则打印man。Second := time.Now().Unix()
package main import ( "fmt" "time" ) const ( Man = 1 Female = 2 ) func main() { second := time.Now().Unix() if second%Female == 0 { fmt.Println("Female") } else { fmt.Println("Man") } }
2、变量
(1). 语法:var identifier type
例1
var a int var b string var c bool var d int = 8 var e string = "hello world"
例2
var ( a int // 默认为0 b string // 默认为“” c bool // 默认为false d = 8 e = "hello world" )
练习
1. 写一个程序获取当前运行的操作系统名称和PATH环境环境变量的值,并打印在终端。
package main import ( "fmt" "os" ) func get_os() { go_os := os.Getenv("GOOS") fmt.Printf("the operaing system is: %s\n", go_os) path := os.Getenv("PATH") fmt.Printf("Path is: %s\n", path) } func main() { get_os() }
变量的作用域
-
1. 在函数内部声明的变量叫做局部变量,生命周期仅限于函数内部。
-
2. 在函数外部声明的变量叫做全局变量,生命周期作用于整个包,如果是大写的,则作用于整个程序。
五、数据类型和操作符
1. 值类型和引用类型
-
值类型:基本数据类型int、float、bool、string以及数组和struct
-
引用类型:指针、slice、map、chan等都是引用类型。
练习:
- 写一个程序用来打印值类型和引用类型变量到终端,并观察输出结果
package main import "fmt" func main() { a := 5 b := make(chan int, 1) fmt.Println("a=", a) fmt.Println("b=", b) } 输出 // a= 5 // b= 0xc00003a070
- 写一个程序,交换两个整数的值。比如: a=3; b=4; 交换之后:a=4;b=3
package main import "fmt" var n int = 100 /* 赋值语句只能在函数中, 以下情况都会报错 var a int a = 100 或者 a := 100 */ func swap(a int, b int) { tmp := a a = b b = tmp } func swap1(a int, b int) (int, int) { return b, a } func swap2(a *int, b *int) { // 改变的是地址指向的那片内存的值 tmp := *a *a = *b *b = tmp } func test() { // 类型转换 var a int8 = 100 var b int16 = int16(a) // 赋值必须类型相同 fmt.Printf("a=%d, b=%d", a, b) } func main() { first := 10 second := 20 // 方式一 swap(first, second) // copy的是first和second的变量值 fmt.Println("first=", first) fmt.Println("second=", second) // 方式二 a, b := swap1(first, second) fmt.Println("a=", a) fmt.Println("b=", b) // 方式三 c, d := second, first fmt.Println("c=", c) fmt.Println("d=", d) // 方式四 swap2(&first, &second) // copy的是first和second的地址 fmt.Println("first=", first) fmt.Println("second=", second) test() }
2. 数据类型和操作符
- 数据类型
(1) bool类型,只能存true或false
(2) 相关操作符,!、&&、||
(3) 数字类型,主要有int、int8、int16、int32、int64、uint8、uint16、uint32、uint64、float32、float64
(4) 类型转换,type(variable),比如:var a int=8; var b int32=int32(a)
(5) 字符类型:var a byte = 'c'
(6) 字符串类型:var str string = "hello world"
注:字符串表示两种方式: 1)双引号 2)`` (反引号)
- 操作符
(1) 逻辑操作符:=、!=、<、<=、>和>=
(2) 数学操作复:+、-、*、/ 等等
练习
- 使用math/rand生成10个随机整数,10个小于100的随机整数以及10个随机浮点数?
package main import ( "fmt" "math/rand" "time" ) func init() { rand.Seed(time.Now().Unix()) } func main() { for i := 0; i < 10; i++ { a := rand.Int() fmt.Println(a) } for i := 0; i < 10; i++ { a := rand.Intn(100) fmt.Println(a) } for i := 0; i < 10; i++ { a := rand.Float32() fmt.Println(a) } }
- 字符串两种表示方式
package main import "fmt" func main() { var str = "hello world\n" var str1 = ` 床前明月光, 疑是地上霜, 举头望明月, 低头思故乡。 ` var b byte = 'c' fmt.Println(str) fmt.Println(str1) fmt.Println(b) fmt.Printf("%c\n", b) } 输出: hello world 床前明月光, 疑是地上霜, 举头望明月, 低头思故乡。 99 c
- 字符串逆转
package main import "fmt" func reverse(str string) string { var result string strlen := len(str) for i := 0; i < strlen; i++ { result = result + fmt.Sprintf("%c", str[strlen-i-1]) } return result } func reverse1(str string) string { var result []byte tmp := []byte(str) length := len(str) for i := 0; i < length; i++ { result = append(result, tmp[length-i-1]) } return string(result) } func main() { str1 := "hello" str2 := "world" // str3 := str1 + " " + str2 str3 := fmt.Sprintf("%s %s", str1, str2) n := len(str3) fmt.Println(str3) fmt.Printf("len(str3)=%d\n", n) substr := str3[0:5] fmt.Println(substr) substr = str3[6:] fmt.Println(substr) result := reverse(str3) fmt.Println(result) result1 := reverse1(str3) fmt.Println(result1) }
六、栈和堆
-
栈是在栈内存中分配的,公用的,性能最高, 堆是在系统内存中分配的
-
基本数据类型是在栈中存储的 引用数据类型是在堆中存储的
函数传参:做了一个拷贝,如果参数为变量值,则函数内部对变量做的操作不会影响实际变量的值。若传的参数为地址,则对地址指向变量的操作会影响实际变量的值。
package main import "fmt" func modify(a int) { a = 10 return } func modify1(a *int) { *a = 10 } func main() { a := 5 b := make(chan int, 1) fmt.Println("a=", a) fmt.Println("b=", b) modify(a) // 做了一个拷贝, 不影响原来的值 fmt.Println("a=", a) modify1(&a) // 参数传了一个地址,对其指向的变量值进行操作会影响其地址指向的变量值 fmt.Println("a=", a) } // 栈是在栈内存中分配的,公用的,性能最高, 堆是在系统内存中分配的 // 基本数据类型是在栈中存储的 引用数据类型是在堆中存储的 /* a= 5 b= 0xc00008c000 a= 5 a= 10 */