go语言入门

第一个程序:打印Hello World!

Go 语言的基础组成有以下几个部分:

  • 包声明
  • 引入包
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释
package main

import "fmt"

func main() {
	fmt.Println("Hello World!")
}

变量定义

// 1.指定类型的方式
var a int = 6
var b int // golang中变量声明时若未指定变量的值,则为默认值
var s string // 默认为 ""
var x, y, z int = 1, 2, 3 // 指定类型后,多个变量一起声明,其类型都为指定的类型
// 也可以把多个变量放在"()"中一起声明
var (
    aa = 3
    ss = "kkk"
    bb = true
)


// 2.不指定类型的方式
var a = 6
var s = "abc"
var x, y, z = 100, 9.3, "sss"  // 多个变量可以一起声明,并且类型也可不同

// 3.甚至可以不适用var,只需在声明变量的时候使用 ":=" 即可,编译器会自动推断类型(包内变量不能这么声明)
a, b, c, d := 3, 4, true, "abc"

内建变量类型

  • bool、string
  • (u)int(长度由操作系统决定), (u)int8, (u)int16, (u)int32, (u)int64, uintptr(指针类型)
  • byte、rune(长度为4字节的字符类型)
  • float32、float64、complex64(实部虚部各32位的复数)、complex128(实部虚部各64位的复数)

golang中只有强制类型转换

// 求直角三角形斜边长度
func triangle() {
	a, b := 3, 4
    // a*a + b*b 为int类型,math.Sqrt()参数类型为float64,需要通过float64(xxx)进行强制类型转换
    // c类型为int,math.Sqrt()返回值为float64,需要通过int(xxx)进行强制类型转换
	var c int = int(math.Sqrt(float64(a*a + b*b)))
	fmt.Println(c)
}

常量类型

// golang中常量用const定义,其数值可以作为任意变量使用
// golang中常量不需要用全大写表示
// 有如下几种定义方式
const filename = "abc.txt"

const (
    aa = 3
    ss = "kkk"
    bb = true
)
// 因为a, b为常量,所以可以把它们看作文本,当调用math.Sqrt(a*a + b*b)时,可以视为float类型
const a, b = 3, 4
var c int = int(math.Sqrt(a*a + b*b))

枚举类型

// 使用常量定义枚举类型
func enum() {
    const (
        cpp = 0
        java = 1
        python = 2
        golang = 3
    )
    
    fmt.Println(cpp, java, python, golang)
}

// 打印结果为 0, 1, 2, 3


// 可以使用自增值iota简化
func enum() {
    // _表示占位符,此处没有变量
    const (
        cpp = iota
        _
        javascript
        python
        golang
    )
    
    fmt.Println(cpp, javascript, python, golang)
}

// 打印结果为 0, 2, 3, 4

// 使用iota打印b, kb, mb, gb, tb, pb大小
func enum() {
    // _表示占位符,此处没有变量
    const (
        b = 1 << (10 * iota)
        kb
        mb
        gb
        tb
        pb
    )
    
    fmt.Println(b, kb, mb, gb, tb, pb)
}

// 打印结果为 1 1024 1048576 1073741824 1099511627776 1125899906842624 1152921504606846976

条件语句

首先在当前目录下简历abc.txt文件

if else语句
func main() {
    const filename = "abc.txt"
    // 系统函数ioutil.ReadFile()会返回两个值,文件内容和错误信息
    contents, err := ioutil.ReadFile(filename)
    
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s\n")
    }
}

// 当abc.txt未创建时,会打印错误信息:open aabc.txt: The system cannot find the file specified.
// 当abc.txt创建后,会打印出文件内容信息

// 上面的条件语句也可修改为如下格式
func main() {
    const filename = "abc.txt"
    if contents, err := ioutil.ReadFile(filename); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s\n")
    }
}

// 执行结果同上,但是需要注意,此时contents, err只能在条件语句中使用
switch语句
// 实现一个输入分数,返回对应等级的函数
// 不同于其他语言的switch语句,golang中的case中不需要写break,会自动跳出
func grade(score int) string {
    g := ""

    switch {
    case score < 0 || score > 100:
        panic(fmt.Springf("Wrong score: %d", score)) // panic用于报错,会强行终止程序
    case score < 60:
        g = "F"
    case score < 70:
		g = "D"
	case score < 80:
		g = "C"
	case score < 90:
		g = "B"
	default:
		g = "A"
	}
	
    return g
}

循环语句

值得注意的是,golang中只有for语句,没有while语句

// 实现一个将10进制int类型数据转换为二进制格式字符串的函数
func convertToBin(n int) string {
	if n == 0 {
		return "0"
	}

	res := ""
	for ; n > 0; n >>= 1 {
        // 因为 n&1是 int类型,需要调用strconv.Itoa()函数,强制转换为string类型
		res = strconv.Itoa(n&1) + res
	}
	return res
}

// 读取文件
func printFile(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(file)

    // 省略初始条件
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
}

// 死循环
func forever() {
	for {
		fmt.Println("abc")
	}
}

函数

golang中的函数可以有多个返回值,返回值也可以起名字

// 带余除法
func divfunc div(a, b int) (q, r int) {
	return a / b, a % b
}

// 运算操作
func eval(a, b int, op string) (int, error) {
	switch op {
	case "+":
		return a + b, nil
	case "-":
		return a - b, nil
	case "*":
		return a * b, nil
	case "/":
		return a / b, nil
	default:
		return 0, fmt.Errorf(
			"unsupported operation: %s", op)
	}
}

golang中函数也可以作为函数的参数

// 定义一个函数apply,第一个参数为一个函数func,后面两个参数为int类型整数
// reflect.ValueOf(op).Pointer()语句可以获取func函数的指针
// 接着可以通过p获取函数func的其他信息,runtime.FuncForPC(p).Name()获取函数名称
func apply(op func(int, int) int, a, b int) int {
	p := reflect.ValueOf(op).Pointer()
	opName := runtime.FuncForPC(p).Name()
	fmt.Printf("Calling function %s with args (%d, %d)\n", opName, a, b)
	return op(a, b)
}

func pow(a, b int) int {
	return int(math.Pow(float64(a), float64(b)))
}

func main {
    // 传入匿名函数进行apply函数的调用
    fmt.Println(apply(
    func(a, b int) int {
        return int(math.Pow(float64(a), float64(b)))
    }, 3, 4))
    
    // 也可以传入定义过的函数作为参数进行调用
    fmt.Println(apply(pow, 3, 4))
}

golang中支持可变长参数列表

// 计算所有整形数据的和
func sum(numbers ...int) int {
	res := 0
	for i:= range numbers {
		res += numbers[i]
	}
	return res
}

指针

和c语言一样,golang中也有指针的概念,不同的是,golang中的指针要比c语言中的方便很多,因为其不能进行运算

// 指针的定义和使用
a := 2
var pa *int = &a
*pa = 3
fmt.Println(a)

// 此时打印结果为3,说明确实通过指针将a的值修改了

值得注意的是,在golang中,函数传参都属于值传递,没有引用传递的概念

// 这种方式显然不能修改实际传入的参数的值
func swap(a, b int) {
    a, b = b, a
}

// 要想修改,则可以通过指针进行操作
func swap(pa, pb *int) {
    *pa, *pb = *pb, *pa
}

数组

func main() {
    // 数组定义有如下几种方式
	var arr1 [5]int // 默认每个元素都是零值
    arr2 := [3]int{1, 3, 5} // 用 := 声明时,必须要赋值
	arr3 := [...]int{2, 4, 6, 8, 10}// 可以用[...]省略数组长度
	var grid [4][5]int // 二维数组

	fmt.Println(arr1, arr2, arr3, grid)

    // 对数组进行遍历,使用 range arr3,可以得到两个值 i, v 分别表示下标和对应位置元素值
	for i, v := range arr3 {
		fmt.Println(i, v)
	}
}

// 打印结果为:
// [0 0 0 0 0] [1 3 5] [2 4 6 8 10] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
// 0 2
// 1 4
// 2 6
// 3 8
// 4 10

值得一提的是,不同于其他语言,在golang中,数组作为参数时,其传递的也是值,并非引用

func main() {
	arr := [...]int{1, 2, 3, 4, 5}
	update(arr)

	fmt.Println(arr)
}

func update(arr [5]int) {
	arr[0] = 100
}

// 打印结果为 1, 2, 3, 4, 5 证明update函数并没有修改实参的值,故数组作为参数也是值传递
  • 在golang中[10]int 和 [20]int是两种不同的类型

  • 调用 update(arr [5]int)会拷贝数组

  • 在golang中一般不直接使用数组,而是使用切片

切片

golang中切片本身是没有值的,它是对底层数组的一个view,即一个视图

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 = arr[2:6] // 数组下标[2, 6)的元素的视图,即 {2, 3, 4, 5, 6}
s2 = arr[:6]// 数组下标[0, 6)的元素的视图,即 {2, 3, 4, 5, 6}
s3 = arr[2:]// 数组下标[2, 7)的元素的视图,即 {2, 3, 4, 5, 6, 7}
s4 = arr[:]// 数组的全部元素的一个视图,即 {1, 2, 3, 4, 5, 6, 7}

s2 = s2[:3]// 对切片进行reslice,即 {2, 3, 4}
s2 = s2[1:]// 再进行reslice,即 {3, 4}

// 切片作为函数参数传递时,不会进行拷贝,而是作为底层数组的一个view传入,所以在函数内部修改切片会改变数组元素的值
func main() {
	arr := [...]int{1, 2, 3, 4, 5}
    update(arr[:])

	fmt.Println(arr)
}

func update(arr []int) {
	arr[0] = 100
}
// 打印结果为 100, 2, 3, 4, 5,证明以上说法是对的
切片的扩展

在这里插入图片描述
在这里插入图片描述

  • arr为{0, 1, 2, 3, 4, 5, 6}, s1为{2, 3, 4, 5}, s2为{5, 6}
  • slice可以向后扩展,但是不能向前扩展
  • s[i]的i不可以超越len(s),向后扩展不可以超过cap(s)
切片的其他操作

append() 和 copy() 函数

如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。

func main() {
   var numbers []int
   printSlice(numbers)

   /* 允许追加空切片 */
   numbers = append(numbers, 0)
   printSlice(numbers)

   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)
   printSlice(numbers)

   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)   
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

/**
	打印结果为
	len=0 cap=0 slice=[]
    len=1 cap=1 slice=[0]
    len=2 cap=2 slice=[0 1]
    len=5 cap=6 slice=[0 1 2 3 4]
    len=5 cap=12 slice=[0 1 2 3 4]
*/

Map

golang中的Map是通过hash表实现的,可以使用内建函数 make 也可以使用 map 关键字来定义 Map,如果不初始化 map,那么就会创建一个 nil map。

func main() {
    var m map[string]string // nil
    m2 := make(map[string]string) // empty map
    m3 := map[string]string {
        "France": "Paris",
        "Italy": "罗马",
        "Japan": "东京",
        "India": "新德里"
    }

    // 使用range进行遍历
    for k, v := range countryCapitalMap {
        fmt.Println(k, "首都是", v)
    }

    // 查看元素在集合中是否存在
    capital, ok := countryCapitalMap[ "美国" ]
    if (ok) {
        fmt.Println("美国的首都是", capital)
    } else {
        fmt.Println("美国的首都不存在")
    }
    
    // 打印结果为“美国的首都不存在”
}
  • map使用了hash表,所以键值必须可以比较相等
  • 除了slice,map,function以外的内建类型都可以作为key
  • Struct类型不包含slice,map,function类型字段的话,也可以作为key
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值