【0x00】初识go

go的基础组成由以下几部分组成:

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

下面来看以下实例:

package main // 包声明
import "fmt" // 导入包
func main(){ // 函数
	fmt.Println("hello world!") //输出语句
}
/*
我是多行注释
goland 的第一行必须是package 包名来声明这个文件属于哪个包
package main 代表这是一个可独立执行的go程序

goland的第二行必须是import 包名, 不然会自动添加到第二行

func main() 是一个函数,go语言的程序跟C一样,是从main函数开始执行(如果
有init()函数,那么就从init()函数开始执行)

go 的注释风格跟类C语言是一致的
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,
如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程
序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小
写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对
象语言中的 protected )。

注意和C风格的语言不同的一点:
需要注意的是 { 不能单独放在一行!!!!!!


在 Go 程序中,一行代表一个语句结束。所以go并不用像类C语言一样需要分号
但是如果多条语句在一行出现,要用;进行分隔。

go的标识符规则同C语言
*/

Go 语言数据类型

  • 布尔
  • 数字
  • 字符串
  • 派生
    在 Go 编程语言中,数据类型用于声明函数和变量
    数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存

布尔型的值只可以是常量 true 或者 false
数字型:整型 int 和浮点型 float32float64
字符串:Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
派生类型:

  • (a) 指针类型(Pointer)
  • (b) 数组类型
  • © 结构化类型(struct)
  • (d) Channel 类型
  • (e) 函数类型
  • (f) 切片类型
  • (g) 接口类型(interface)
  • (h) Map 类型

整数类型:

  • uint8-无符号 8 位整型 (0 到 255)
  • uint16-无符号 16 位整型 (0 到 65535)
  • uint32-无符号 32 位整型 (0 到 4294967295)
  • uint64-无符号 64 位整型 (0 到 18446744073709551615)
  • int8-有符号 8 位整型 (-128 到 127)
  • int16-有符号 16 位整型 (-32768 到 32767)
  • int32-有符号 32 位整型 (-2147483648 到 2147483647)
  • int64-有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

浮点型:

  • float32-IEEE-754 32位浮点型数
  • float64-IEEE-754 64位浮点型数
  • complex64-32 位实数和虚数
  • complex128-64 位实数和虚数

其他类型:

  • byte-类似 uint8
  • rune-类似 int32
  • uint-32 或 64 位
  • int-与 uint 一样大小
  • uintptr-无符号整型,用于存放一个指针

Go语言变量

变量声明规则

var identifier type
var 标识符 数据类型
var b bool;

变量声明的两种方式:
第一种,指定变量类型,如果没有初始化,则变量默认为零值。(什么叫零值?就是0、false、""(空字符串) 之类的)

var v_name v_type
v_name = value

实例:

package main

import "fmt"

func main(){
	var b bool
	var i int
	var s string
	var s1 string
	s1 = "faker is a nb"
	fmt.Println(b,i,s,s1)
}

result:

false 0  faker is a nb

以下几种类型为 <nil>

var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口

第二种,根据值自行判定变量类型。

var v_name = value

实例:

package main

import "fmt"

func main(){
	var k = 1
	fmt.Println(k)
}

第三种,省略 var, 注意 := 左侧如果没有声明新的变量,就产生编译错误,格式:

v_name := value

实例:

package main

import "fmt"

func main() {
	k := 1
	// k := 2 这样会报错
	k, v := 3, 2 // 这样就不会报错
	fmt.Println(k, v)
}

同时声明多个变量:

//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声
明类型,自动推断

vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经
被声明过的,否则会导致编译错误


// 这种因式分解关键字的写法一般用于声明全局变量
var (
    vname1 v_type1
    vname2 v_type2
)

实例:

package main

var x, y int
var (  // 这种因式分解关键字的写法一般用于声明全局变量
    a int
    b bool
)

var c, d int = 1, 2
var e, f = 123, "hello"

//这种不带声明格式的只能在函数体中出现
//g, h := 123, "hello"

func main(){
    g, h := 123, "hello"
    println(x, y, a, b, c, d, e, f, g, h)
}

意思就是,只有带var的才能在函数外(全局)进行声明。用:=只能在函数体内进行声明。

声明变量方法总结:

#第一种
#单
var b bool
b = false
#多
var a,b,c bool
a,b,c = false,true,false
#第二种
#单
var b = 3
#多
var b,c,d = false,true,false
#第三种
#单
b := 2
#多
b,c,d := 1,2,3
#第一种和第二种方法没有使用限制
#第三种方法只能用于函数体内
#多个变量声明时,只要有1个变量是新变量,就不会有语法错误。
#如果其他变量已经被声明过,那么会变成赋值操作

值类型和引用类型
所有像 intfloatboolstring 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
在这里插入图片描述
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:
在这里插入图片描述
像C语言一样,你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈内存中。
实例:

package main

import "fmt"

func main() {
	a := 1
	b := a
	fmt.Println(&a,&b)
}

输出:

0xc000016058 0xc000016060

更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。其实这跟C语言的指针就很像了。
在这里插入图片描述
这个内存地址为称之为指针,这个指针实际上也被存在另外的某一个字中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。(跟链表差不多)
当使用赋值语句 r2 = r1 时,只有**引用(地址)**被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。

简短形式,使用 := 赋值操作符
我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50b := false
ab 的类型(intbool)将由编译器自动推断。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误。(Go语言强制不让写垃圾代码)
但是全局变量是允许声明但不使用。(可以在函数体或者函数外使用,也可以不适用,没有限制)

python一样,如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同。
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1)。其实并行赋值就是python的序列解包。

Go 语言常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型
常量的定义格式:

const identifier [type] = value

你可以省略类型说明符 type,因为编译器可以根据变量的值来推断其类型。
tips: [] 代表是可选参数。跟linux一样。
举例:

显式类型定义: const b string = "abc"
隐式类型定义: const b = "abc"

多个相同类型的声明可以简写为:

const c_name1, c_name2 = value1, value2

Demo:

package main

import "fmt"

func main() {
   const LENGTH int = 10
   const WIDTH int = 5   
   var area int
   const a, b, c = 1, false, "str" //多重赋值

   area = LENGTH * WIDTH
   fmt.Printf("面积为 : %d", area)
   println()
   println(a, b, c)   
}

常量还可以用作枚举:

const (
    Unknown = 0
    Female = 1
    Male = 2
)

数字 0、1 和 2 分别代表未知性别、女性和男性。
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过(想想为啥:大概是编译的时候就存在的函数只有内置函数,如果用自定义的函数,相当于直接在编译阶段对自定义函数进行了调用操作):

package main

import "unsafe"
const (
    a = "abc"
    b = len(a)
    c = unsafe.Sizeof(a)
)

func main(){
    println(a, b, c)
}

先来看个好玩的特性:

package main

import "fmt"

func main() {
	const (
		a = 100 //b、c、d都会自动变成100 即执行了b=100,c=100等操作
		b
		c
		d
		e = 1 //f、g、h、i都会自动变成1,即执行了f=1、g=1等操作
		f
		g
		h
		i
	)
	fmt.Println(a,b,c,d,e,f,g,h,i)
}

输出:

100 100 100 100 1 1 1 1 1

iota

iota,特殊常量,可以认为是一个可以被编译器修改的常量
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值:

const (
    a = iota
    b = iota
    c = iota
)

第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:

const (
    a = iota
    b
    c
)

iota 用法

package main

import "fmt"

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //独立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢复计数
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

以上实例运行结果为:

0 1 2 ha ha 100 100 7 8

再看个有趣的的 iota 实例:

package main

import "fmt"
const (
    i=1<<iota
    j=3<<iota
    k # 自动补齐成3<<iota
    l # 自动补齐成3<<iota
)

func main() {
    fmt.Println("i=",i)
    fmt.Println("j=",j)
    fmt.Println("k=",k)
    fmt.Println("l=",l)
}

结果:

i= 1
j= 6
k= 12
l= 24

iota 表示从 0 开始自动加 1,所以 i=1<<0, j=3<<1<< 表示左移的意思),即:i=1, j=6,这没问题,关键在 k 和 l,从输出结果看 k=3<<2,l=3<<3
简单表述:
i=1:左移 0 位,不变仍为 1;
j=3:左移 1 位,变为二进制 110, 即 6;
k=3:左移 2 位,变为二进制 1100, 即 12;
l=3:左移 3 位,变为二进制 11000,即 24。

猜你喜欢

转载自blog.csdn.net/weixin_41687289/article/details/88914399