GO语言系列之 基本数据类型和操作符
一、文件名 & 关键字 & 标识符
-
1.所有go源码以.go结尾
-
2.标识符以字母或下划线开头,大小写敏感
-
3._是特殊标识符,用来忽略结果
-
4.保留关键字
除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
二、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. 值类型和引用类型
-
值类型:变量直接存储值,内存通常在栈中分配
-
引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过GC回收。
值类型:基本数据类型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)“” 双引号:支持转义字符,如\n, \t, %%等等
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) }
- 复杂数据类型
结构体 struct 接口 interface 数组 [数组长度]值类型{值1,值2} 切片 slice []值类型{值1,值2} map [key类型]值类型{key:值} 指针 * 函数 func 管道 chan
- 取值范围
int8 -128到127 uint8 0到255 int16 -32768到32767 uint16 0到65535 int32 -2147483648到2147483647 uint32 0到4294967295 int64 -9223372036854775808到9223372036854775807 uint64 0到18446744073709551615
-
数据格式转换
package main import ( "encoding/json" "fmt" "reflect" "strconv" ) type User struct { Id int `json:"id"` Name string `json:"name"` } func main() { //string到int n10, _ := strconv.Atoi("12") fmt.Println(reflect.TypeOf(n10)) // string到int64 n64, _ := strconv.ParseInt("12", 10, 64) fmt.Println(reflect.TypeOf(n64)) // string to float32、float64 f_32, _ := strconv.ParseFloat("3.1415", 32) f_64, _ := strconv.ParseFloat("3.1415", 64) fmt.Printf("%T\n", f_32) // float64 fmt.Printf("%T\n", f_64) // int到string s1 := strconv.Itoa(11) fmt.Println(reflect.TypeOf(s1)) // int64到string s2 := strconv.FormatInt(11,10) fmt.Println(reflect.TypeOf(s2)) // float32/64 to string s3 := strconv.FormatFloat(3.1415, 'E', -1, 32) s4 := strconv.FormatFloat(3.1415, 'E', -1, 64) fmt.Println(reflect.TypeOf(s3)) fmt.Println(reflect.TypeOf(s4)) // int和int64 n_10_64 := int64(11) fmt.Println(reflect.TypeOf(n_10_64)) // int64 to int n_64_10 := int(n_10_64) fmt.Println(reflect.TypeOf(n_64_10)) s := `{"id": 1, "name": "wxnacy"}` // 将字符串反解析为结构体 var user User _ = json.Unmarshal([]byte(s), &user) fmt.Println(user) // {1 wxnacy} // 将字符串反解析为字典 var d map[string]interface{} _ = json.Unmarshal([]byte(s), &d) fmt.Println(d) // map[id:1 name:wxnacy] // 将字符串反解析为数组 s = `[1, 2, 3, 4]` var a []int _ = json.Unmarshal([]byte(s), &a) fmt.Println(a) // [1 2 3 4] // 将结构体解析为字符串 str_user, _ := json.Marshal(user) fmt.Println(string(str_user))// {"id":1,"name":"wxnacy"} // 数组解析为字符串 str_a, _ := json.Marshal(a) fmt.Println(string(str_a)) // [1,2,3,4] <nil> // map解析为字符串 str_d, _ := json.Marshal(d) fmt.Println(string(str_d)) // {"id":1,"name":"wxnacy"} <nil> }
六、栈和堆
-
栈是在栈内存中分配的,公用的,性能最高, 堆是在系统内存中分配的
-
基本数据类型是在栈中存储的 引用数据类型是在堆中存储的
函数传参:做了一个拷贝,如果参数为变量值,则函数内部对变量做的操作不会影响实际变量的值。若传的参数为地址,则对地址指向变量的操作会影响实际变量的值。
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 */
练习作业
- 1. 判断 101-200 之间有多少个素数,并输出所有素数。注:定义:为在大于1的自然数中,除了1和它本身以外不再有其他因数的数称为质数
- 2. 打印出100-999中所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153 是一个“水仙花数”,因为 153=1 的三次方+5 的三次方+3 的三次方。
- 3. 对于一个数n,求n的阶乘之和,即: 1! + 2! + 3!+…n!
参考
// 判断 101-200 之间有多少个素数,并输出所有素数。 package main import ( "fmt" "math" ) func isPrime(n int) bool { sqrt_n := int(math.Sqrt(float64(n))) + 1 for i := 2; i < sqrt_n; i++ { if n%i == 0 { return false } } return true } func main() { var n int var m int fmt.Printf("请输入两个数字:") fmt.Scanf("%d%d", &n, &m) for i := n; i < m; i++ { if isPrime(i) == true { fmt.Printf("%d\n", i) } } }
/* 打印出100-999中所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。 例如:153 是一个“水仙花数”,因为 153=1 的三次方+5 的三次方+3 的三次方。 */ package main import ( "fmt" "strconv" ) func isShuixianhua(n int) bool { var i, j, k int i = n % 10 j = (n / 10) % 10 k = (n / 100) % 10 sum := i*i*i + j*j*j + k*k*k return sum == n } func isShuixianhua2(n int) bool { var result = 0 str := strconv.Itoa(n) for i := 0; i < len(str); i++ { num := int(str[i] - '0') result += (num * num * num) } number, err := strconv.Atoi(str) if err != nil { fmt.Printf("can not convert %s to int\n", str) } if result == number { return true } return false } func main() { var n int var m int fmt.Printf("请输入两个数字:") fmt.Scanf("%d,%d", &n, &m) for i := n; i <= m; i++ { if isShuixianhua(i) == true { fmt.Println(i) } } }
// 对于一个数n,求n的阶乘之和,即: 1! + 2! + 3!+…n! package main import "fmt" func sum(n int) uint64 { var s uint64 = 1 var sum uint64 = 0 for i := 1; i <= n; i++ { s = s * uint64(i) fmt.Printf("%d!=%d\n", i, s) sum += s } return sum } func main() { var n int fmt.Printf("请输入一个数字:") fmt.Scanf("%d", &n) s := sum(n) fmt.Println(s) }