包声明
引入包
函数
变量
语句&表达式
注释
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("hello world!")
}
第一行代码 package main
定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
下一行 import "fmt"
告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
下一行 func main()
是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
下一行/*...*/
是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
下一行 fmt.Println(...)
可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print(“hello, world\n”) 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没+有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出;标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的。
执行go run test.go
可以看到输出为hello world!
执行go build test.go
可以生成二进制文件test.exe
Go 程序可以由多个标记组成,可以是
关键字,标识符,常量,字符串,符号
如以下 GO 语句由 6 个标记组成:
fmt.Println("Hello, World!")
//6 个标记是(每行一个):
fmt
.
Println
(
"Hello, World!"
)
在 Go 程序中,一行代表一个语句结束
如果你打算将多个语句写在同一行,它们则必须使用;
人为区分,但在实际开发中我们并不鼓励这种做法
以下为两个语句:
fmt.Println("Hello, World!")
fmt.Println("百度:baidu.com")
注释不会被编译,每一个包应该有相关注释
单行注释是最常见的注释形式,你可以在任何地方使用以//
开头的单行注释。
多行注释也叫块注释,均已以/*
开头,并以*/
结尾。
// 单行注释
/*
你好
欢迎来南京
*/
(A~Z和a~z)
数字(0~9)
、下划线_
组成的序列,但是第一个字符必须是字母或下划线而不能是数字。//以下是有效的标识符
mahesh kumar abc move_name a_123
myname50 _temp j a23b9 retVal
//以下是无效的标识符
1ab(以数字开头)
case(Go 语言的关键字)
a+b(运算符是不允许的)
Go 语言的字符串可以通过 +
实现:
实例
package main
import "fmt"
func main() {
fmt.Println("hello" + "world")
}
//以上实例输出结果为:
helloworld
. | . | . | . | . |
---|---|---|---|---|
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
. | . | . | . | . |
---|---|---|---|---|
append | bool | byte | cap | close |
copy | false | float32 | float64 | imag |
int32 | int64 | iota | len | make |
println | real | recover | string |
程序一般由关键字、常量、变量、运算符、类型和函数组成。
程序中可能会使用到这些分隔符:括号 ()
,中括号 []
和大括号 {}
。
程序中可能会使用到这些标点符号:.
,
;
:
和 …
var age int;
//语句中适当使用空格能让程序更易阅读
//无空格:
fruit=apples+oranges;
//在变量与运算符间加入空格,程序看起来更加美观,如:
fruit = apples + oranges;
package main
import (
"fmt"
)
func main() {
// %d 表示整型数字,%s 表示字符串
var age = 18
var time = "2022-3-15"
var url = "age=%d,time=%s"
var target_url = fmt.Sprintf(url, age, time)
fmt.Println(target_url)
}
输出结果为:
age=18,time=2022-3-15
常用的占位符
格式化指令 | 含义 |
---|---|
%% | %字面量 |
%b | 将一个整数格式转化为二进制的表达方式 |
%c | 相应Unicode码点对应所表示的字符 |
%d | 十进制整数 |
%o | 八进制整数 |
%x | 小写的十六进制数值 |
%X | 大写的十六进制数值 |
%U | 一个Unicode表示法表示的整型码值 |
%s | 输出以原生的UTF8字节表示的字符,如果console不支持utf8编码,则会乱码 |
%t | 以true或者false的方式输出布尔值 |
%v | 使用默认格式输出值,或者如果方法存在,则使用类性值的String()方法输出自定义值 |
%T | 输出值的类型 |
在 Go 编程语言中,数据类型用于声明函数和变量
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存
Go 语言按类别有以下几种数据类型:
|类型 |描述|
|–|–|–|
|布尔型|布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
|数字类型 |整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
|字符串类型|字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
|派生类型|包括:指针类型(Pointer)、数组类型、结构化类型(struct)、Channel 类型、函数类型、切片类型、接口类型(interface)、Map 类型 |
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
类型 | 描述 |
---|---|
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 identifier1, identifier2 type
package main
import "fmt"
func main() {
var a string = "hello"
fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
}
//输出结果如下
hello
1 2
第一种,指定变量类型,如果没有初始化,则变量默认为零值
var v_name v_type
v_name = value
package main
import "fmt"
func main() {
//声明一个变量并初始化
var a = "goroute"
fmt.Println(a)
//没有初始化就为零
var b int
fmt.Println(b)
//布尔值零值为false
var c bool
fmt.Println(c)
}
//输出的结果如下
goroute
0
false
数值类型(包括complex64/128)为 0
布尔类型为 false
字符串为 ""(空字符串)
第二种,根据值自行判定变量类型
var v_name = value
package main
import "fmt"
func main() {
var a = true
fmt.Println(a)
}
//输出结果如下
true
第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误
package main
import "fmt"
func main() {
a := 1
fmt.Println(a)
}
//此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
var a int
a =1
package main
import "fmt"
func main() {
//相当于var a string = "hello"
a := "hello"
fmt.Println(a)
}
package main
import "fmt"
func main() {
a, b, c := 1, 2, 3
fmt.Println(a, b, c)
}
// 这种因式分解关键字的写法一般用于声明全局变量
var(
test1 int
test2 float32
test3 string
)
package main
import "fmt"
var x, y int
var (
a int
b bool
)
var c, d int = 1, 2
var e, f = 666, "hello world!"
func main() {
//这种不带声明格式的只能在函数体中出现
g, h := 7878, "happy"
fmt.Println(x, y, a, b, c, d, e, f, g, h)
}
//输出结果如下
0 0 0 false 1 2 666 hello world! 7878 happy
int
、float
、bool
和 string
这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:=
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i
的值进行了拷贝:可以通过 &i
来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)
值类型变量的值存储在堆中
内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
弹栈:变量的释放
堆的顺序:先进先出
package main
import "fmt"
//变量在内存中的存储方式
var num int = 10
var sun int = 20
func main() {
var test1 = "hello"
var test2 = "world"
fmt.Println("num=", num)
//调用内存地址使用&
fmt.Println("num的内存地址是", &num)
fmt.Println("sun的内存地址是", &sun)
//调用函数中中变量
fmt.Println(test1)
fmt.Println(test2)
}
//输出结果如下
num= 10
num的内存地址是 0x709258
sun的内存地址是 0x709260
hello
world
上面代码中,在全局定义的变量是存放在栈中的,他是属于文件的
var num int = 10
var sun int = 20
而局部变量则是存放在堆中,是属于main函数的
var test1 = "hello"
var test2 = "world"
当全局变量加载完以后会加载main函数,但是main函数中定义的变量并不会被压栈,而是会被放入堆中
在test函数中是无法调用main函数中的变量的,存放位置是不同的
函数中的变量会随着函数的结束而结束它的生命周期
在函数内部声明的变量教局部变量,生命周期仅限于函数内部
在函数外部声明的变量交全局变量,生命周期作用于整个包,如果是大写首字母,则作用域整个程序
语句块中的变量,生命周期仅限于语句块,语句块外部不能被识别
示例
package main
import "fmt"
//定义全局变量
var a string = "10"
func main() {
n()
m()
fmt.Println("主函数输出:", a)
}
func n() {
println("n函数输出:", a)
}
func m() {
a := "0"
fmt.Println("m的函数输出:", a)
}
//输出结果如下
n函数输出: 10
m的函数输出: 0
主函数输出: 10
package main
import "fmt"
/*
:=
定义即声明,无需写var,系统会自动推断数据类型,无需自定义数据类型
格式只能在函数体内
*/
func main() {
num := "hello"
fmt.Println(num)
//查看变量的数据类型
fmt.Printf("num的数据类型是:%T", num)
}
//输出结果为
hello
num的数据类型是:string
空白标识符_
也被用于抛弃值,如值5
在_,b=5,7
中被抛弃
_
实际上是一个只写变量,你不能得到它的值,这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你不需要从一个函数得到所有返回值
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val
和 错误 err
是通过调用Func1
函数同时得到:val,err = Func(var1)
package main
import "fmt"
func main() {
_, numb, strs := numbers() //只获取函数返回值的后两个
fmt.Println(numb, strs)
}
func numbers() (int, int, string) {
a, b, c := 1, 2, "str"
return a, b, c
}
//输出结果如下
2 str
常量是一个简单值的标识符,使用 const
修饰,是程序在运行时,不会被修改的量
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
const identifier [type] = value
//可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型
//显式类型定义:
const b string = "abc"
//隐式类型定义:
const b = "abc"
//多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
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)
fmt.Println()
fmt.Println(a, b, c)
}
//输出结果如下
面积为 : 50
1 false str
len()
, cap()
, unsafe.Sizeof()
函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:package main
import (
"fmt"
"unsafe"
)
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main() {
fmt.Println(a, b, c)
}
//输出结果如下
abc 3 16
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
)
package main
import "fmt"
func main() {
const (
//iota相当于索引,从0开始
a = iota //0
b //1
c //2
)
fmt.Println(a, b, c)
}
//输出结果
0 1 2
//求半径为5的圆的面积和周长
//面积=Π*半径²
//周长=2*Π*半径
package main
import "fmt"
func main() {
const Π float32 = 3.14
var r float32 = 5
//周长a
a := 2 * Π * r
//面积
s := Π * r * r
fmt.Println("周长:", a, "\n面积:", s)
}
//输出结果为
周长: 31.400002
面积: 78.5
//求出2的指数次方结果
package main
import "fmt"
func main() {
const (
a = 1 << iota //往左移0位
b = 2 << iota //往左移1位
c //往左移2位
d //往左移3位
e //往左移4位
)
fmt.Println(a, b, c, d, e)
}
//输出结果如下
1 4 8 16 32
//写一个程序获得当前运行系统中go的工作目录和项目目录路径,并输出在终端
package main
import (
"fmt"
"os"
)
func main() {
//获取系统参数,go的工作目录
var home string = os.Getenv("GOROOT")
fmt.Println("go的工作目录是:", home)
//获取go的项目目录
var project string = os.Getenv("GOPATH")
fmt.Println("go的项目目录是:", project)
}
//输出结果如下
go的工作目录是: D:\Go
go的项目目录是: D:\goproject
//定义两个常量 Man=1 和 Female=2,获取当前时间的秒数,如果能被Female整除,则在终端打印Female,否则打印Man
package main
import (
"fmt"
"time"
)
const (
Female = 2
Man = 1
)
func main() {
for {
//获取当前时间戳
sec := time.Now().Unix()
if sec%Female == 0 {
fmt.Println("Female")
} else {
fmt.Println("Man")
}
//1000毫秒=1秒
time.Sleep(1000 * time.Millisecond)
}
}