入门go语言(一)

1、变量与常量

1.1、变量声明

  • 第一种,指定变量类型,如果没有初始化,则变量默认为零值

var num int
num = 5
  • 第二种,根据值自行判定变量类型
var name = "阿凡达"
  • 第三种,省略var,使用:=,:=左边必须是新定义的变量,否则报错
f := 0.0043
  • 多变量声明
var a,b,c = 1,2,3
d,e,f := 4,5,6

1.2、常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

  • 声明
const PI = 3.14159
  • iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量。

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

const (
	A = iota  // iota=0	A=0
	B         // iota=1	B=1
	C         // iota=2	C=2
	D = "sky" // iota=3	D="sky"
	E         // iota=4	E="sky"
	F = 39    // iota=5	F=39
	G = iota  // iota=6	G=6
	H         // iota=7	H=7
)

const (
	J = iota // iota=0	J=0
)

2、流程结构

2.1、if 语句

package main

import "fmt"

func main() {
	var num int
	fmt.Print("请输入 num 的值:")
	fmt.Scanln(&num)
	if num >= 5 {
		fmt.Println("num 大于等于5.")
	} else if num <= 0 {
		fmt.Println("num 小于等于0.")
	} else {
		fmt.Println("num 大于 0 小于 5.")
	}
}

2.2、switch 语句

switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止。

switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。

switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。

语法:

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

2.3、for 循环

语法

Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。

和 C 语言的 for 一样(1、先执行init,2、再判断 init 是否满足 condition 条件,3、执行循环体,4、执行 post ,给变量增值或减值):

for init; condition; post { 循环体 }

和 C 的 while 一样:

for condition { }

和 C 的 for(;;) 一样:

for { }

EXAMPLE:

//打印九九乘法表
func main() { for i := 1; i <= 9; i++ { for j := 1; j <= i; j++ { fmt.Printf("%d * %d = %d ", j, i, i*j) } fmt.Println() } }

 

3、数组

数组是存储一组数据类型相同且长度固定的数据序列。

3.1、声明数组

语法:var variable_name [SIZE] variable_type

var num_array [5]int

3.2、初始化数组

// 定义和赋值放一起 [size]表示数组的长度,[...]表示不指定数组长度,长度由赋值的长度自动推导
var num_array2 = [6]int{0, 1, 2, 3, 4, 5} var string_array = [...]string{"one", "two", "three", "four", "five", "six"}

3.3、访问数组元素

语法:array_name[index]

3.4、数组是值类型

package main

/*
1、值类型,赋值的时候,传递的是值的副本
	int,float,string,array
2、引用类型,赋值的时候,传递的是内存地址
	slice,map

数组的类型:[size]type
[6]int
[6]int
[6]string

*/

import "fmt"

func main() {
	array1 := [6]int{0, 1, 2, 3, 4, 5}
	array2 := [6]int{0, 1, 2, 3, 4, 5}
	array3 := [...]string{"one", "two", "three", "four", "five", "six"}

	// 数组的类型
	fmt.Printf("%T
", array1) //[6]int
	fmt.Printf("%T
", array2) //[6]int
	fmt.Printf("%T
", array3) //[6]string

	// 数组是值类型
	array4 := array1
	fmt.Println(array1) //[0 1 2 3 4 5]
	fmt.Println(array4) //[0 1 2 3 4 5]
	// array1的改变不会影响到array4,说明数组是值类型
	array1[0] = 10
	fmt.Println(array1) //[10 1 2 3 4 5]
	fmt.Println(array4) //[0 1 2 3 4 5]

	// 值类型比较的是值是否一样
	fmt.Println(array1 == array2) // false
	array1[0] = 0
	fmt.Println(array1 == array2) // true
}

 3.5、切片

package main

/*
slice就是不定长的数组

slice的两种定义方式:
1、 s1 := []int{}
[]中不填数组的长度,{}中可填数据,也可不填
[]type{}中间,则填要存储数据的类型,如 int,string,map[int]string

2、 s2 := make([]int, 0, 8)
通过内置函数make,make专门用来创建引用类型数据

slice的值存储的是内存地址
*/

import "fmt"

func main() {
   s1 := []int{}
   s2 := make([]int, 4, 8)
   fmt.Println(s1) // []
   fmt.Println(s2) // [0 0 0 0]

   //使用 append 内置函数像 slice 中添加数据
   fmt.Printf("s1 的内存地址是:%p;存储的值的内存地址是:%p
", &s1, s1)
   s1 = append(s1, 1, 2, 3, 4)
   fmt.Printf("添加数值后 s1 的内存地址是:%p;存储的值的内存地址是:%p
", &s1, s1)

}

  

3.6、深浅拷贝

package main

/*
1、深拷贝是值类型之间的拷贝,拷贝的是数值
2、浅拷贝是引用类型之间的拷贝,拷贝的是内存地址

copy(dst, src)函数:
相当于
for i:=0;i<len(dst);i++ {
	dst[i]=src[i]
}
当然,也可使用切片,指定复制和粘贴的位置,如:
dst[2:]		从2的索引开始粘贴,直到结束
src[4:6]	复制索引4到6的数值
*/

import "fmt"

func main() {
	s1 := []int{0, 1, 2, 3, 4}

	//浅拷贝
	//s1 的内存地址是:0xc00005a420,s1 的底层数组的内存地址是:0xc00008e030
	//s2 的内存地址是:0xc00005a480,s2 的底层数组的内存地址是:0xc00008e030
	//可以看到,s1和s2的存储的底层数组的内存地址是一样的,这就是浅拷贝
	fmt.Printf("s1 的内存地址是:%p,s1 的底层数组的内存地址是:%p,s1 的值是:%d
", &s1, s1, s1)
	s2 := s1
	fmt.Printf("s2 的内存地址是:%p,s2 的底层数组的内存地址是:%p,s2 的值是:%d
", &s2, s2, s2)

	fmt.Println("*****************************************************************")

	//深拷贝
	//s1 的内存地址是:0xc000004460,s1 的底层数组的内存地址是:0xc00000e360
	//s3 的内存地址是:0xc000004580,s3 的底层数组的内存地址是:0xc00000e390
	//可以看到,s1和s2的存储的底层数组的内存地址是不一样的,这就是深拷贝
	fmt.Printf("s1 的内存地址是:%p,s1 的底层数组的内存地址是:%p,s1 的值是:%d
", &s1, s1, s1)
	s3 := make([]int, 5, 5)
	copy(s3, s1)
	fmt.Printf("s3 的内存地址是:%p,s3 的底层数组的内存地址是:%p,s3 的值是:%d
", &s3, s3, s3)
}

 

4、string

4.1、strings包

package main

import (
	"fmt"
	"strings"
)

func main() {
	// string 包的 replace 函数
	str1 := "heeelloleeeworld"
	str1 = strings.Replace(str1, "eee", "www", 1)
	fmt.Println(str1)

	// 判断字符串是否包含子字符串
	fmt.Println(strings.Contains(str1, "eee"))

	// 判断字符串中是否包含某个字符
	fmt.Println(strings.ContainsAny(str1, "d"))

	// 判断字符串是否以某个前缀开头
	fmt.Println(strings.HasPrefix(str1, "heee"))

	// 判断字符串是否以某个字符串结尾
	fmt.Println(strings.HasSuffix(str1, "rld"))

	// 以指定字符串切分字符串
	fmt.Println(strings.Split(str1, "eee"))
}

4.2、strconv包

package main

import (
	"fmt"
	"strconv"
)

func main() {
	str1 := "true"
	b1, _ := strconv.ParseBool(str1)
	fmt.Printf("%T
", b1)

	// Atoi 把 string 转换成 int
	str2 := "2020"
	n1,_ := strconv.Atoi(str2)
	fmt.Printf("%T
", n1)

	//
	n2 := strconv.Itoa(n1)
	fmt.Printf("%T
", n2)

}

  

5、函数

5.1、函数的声明

格式:

parameters1和parameters2都是函数形式参数,value1和value2是返回值,

func funName(parameters1 type1, parameters2 type2) (value1 type3, value2 type4) {
	//函数体
	value1 := 4
	value2 := 9
	return
} 

5.2、函数的可变参数

格式:

args ...type这里表示的可变参

func funcName([para type], args ...type) {
	函数体
}

EXAMPLE:

func Sum(args ...int) { // 求和
	sum := 0
	for i := 0; i < len(args); i++ {
		sum += args[i]
	}
	fmt.Println(sum)
}

5.3、函数的数据类型

我们试着打印下函数的数据类型看看

package main

import "fmt"

func main() {
	//注意不带括号,带括号的函数表调用执行
	fmt.Printf("fun11 函数的数据类型是:%T
", fun11)	// func()
	fmt.Printf("fun22 函数的数据类型是:%T
", fun22)	// func(int, int, string)
	fmt.Printf("fun33 函数的数据类型是:%T
", fun33)	// func([]int) (int, int)

	// 把函数赋值给一个变量
	fun44 := fun11
	fun44()
	// 指向同一块内存地址
	fmt.Printf("fun11 函数的内存地址:%p
", fun11)
	fmt.Printf("fun44 函数的内存地址:%p
", fun44)
}

func fun11() {
	fmt.Println("this is the test!")
}

func fun22(n1,n2 int, s1 string) {

}

func fun33(s1 []int) (int, int) {
	return 0,0
}

5.4、函数的本质

函数定义时,内存中会开辟一段内存空间给函数用,函数名则指向这段内存空间的地址。

函数名不带括号,就表示这段内存地址;函数名带括号,就表示执行这个函数。

5.5、回调

回调是指,把一个函数当参数,传递给另外一个函数。

package main

/*
回调:把一个函数当做参数,传递给另外一个函数
*/

import "fmt"

func main() {
	res := opt(1, 2, add)
	fmt.Println(res)
}

// 加
func add(a, b int) int {
	return a + b
}

//减
func sub(a, b int) int {
	return a - b
}

// 算术运算
//	           形参名+函数类型
func opt(a, b int, fun func(int, int) int) int {
	return fun(a, b)
}

5.6、闭包

闭包函数:声明在一个函数中的函数
闭包:闭包函数总能访问外部函数的变量,外部函数的变量不会因调用而销毁

package main

import "fmt"

func main() {
	zhaoyun := walk("赵云")
	zhangfei := walk("张飞")
	zhaoyun(2)
	zhangfei(3)
	zhangfei(1)
	zhaoyun(2)
}

func walk(name string) func(int) {
	total := 0
	haha := func(hour int) {
		total += hour
		fmt.Printf("%s: 行军%d小时!
", name, hour)
		fmt.Printf("%s: 一共行军%d小时!
", name, total)
	}
	return haha
}

  

 6、指针

一个指针变量指向了一个值的内存地址。

*表示去指针数据

&表示取地址

定义:

// 定义一个存储整数类型的指针
var p1 *int
// 定义一个双重指针
var p2 **int

使用:

// 给指针赋值
p1 = &a
p2 = &p1
// 取出指针变量的值
*p1
*p2

  

空指针:

空指针用nil表示

7、结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义:

type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

  

example:

package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}


func main() {

    // 创建一个新的结构体
    fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})

    // 也可以使用 key => value 格式
    fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})

    // 忽略的字段为 0 或 空
   fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})
}

  

访问结构体成员:

如果要访问结构体成员,需要使用点号 . 操作符

结构体.成员名

  

原文地址:https://www.cnblogs.com/wufj/p/13417857.html