学习笔记Golang语法学习笔记
Posted 棉花糖灬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记Golang语法学习笔记相关的知识,希望对你有一定的参考价值。
一、入门
go是编译型的语言,代码风格类似于C语言,其最大特点是支持并发编程,go文件后缀名为.go
在命令行通过go run helloworld.go
来运行,或先通过go build helloworld.go
编译,然后./helloworld
执行,在windows下编译生成的是.exe文件。go mod tidy
命令拉取缺少的模块,移除不用的模块
go语言代码的第一行使用package声明包,名为main的包比较特殊,它定义的是一个独立的可执行程序,而不是库。在 main 里的 main 函数也很特殊,它是整个程序执行时的入口。使用import导入使用的包,import必须跟在package声明之后。缺少或多了不需要的包,编译都不会通过。
go语言不需要用分号结尾,除非多个语句或声明出现在同一行。实际上在特定符号后面的换行符会被转换为分号,所以在什么地方换行会影响对go代码的解析,例如必须和关键字func在同一行,不能独立成行。
字符串必须是双引号,单行注释是//
,多行注释是/**/
package main
import (
"fmt"
"os"
)
func main()
var s, sep string
sep = " "
// os.Args是通过命令行运行代码时传入的各个参数,第0个参数默认是本代码的路径
for i := 0; i < len(os.Args); i++
s = s + sep + os.Args[i]
fmt.Println(s)
i++
和i--
是语句而非表达式,故j = i++
是非法的,且只支持后缀,++i
也是非法的。
go语言中只有for循环,for循环有以下几种形式
for initialization; condition; post
// 语句
// 相当于C语言中的while循环
for condition
// 语句
// 死循环
for
// 语句
// rang产生一对值:索引和索引处的元素值
for _,arg := range os.Args[1:]
// 语句
go语言中不允许存在无用的变量,即声明了但是没用到的变量,所以索引值用不到时不能用普通的变量名代替,而是需要使用空标识符_
定义变量的几种方式,建议使用第一、二种
s := ""
var s string
var s = ""
var a, b string = "aaa", "bbb"
string包中string.Join(os.Args[1:], " ")
可以使用空格将os.Args中的元素连接起来
格式化字符:
符号 | 含义 |
---|---|
%d | 十进制整数 |
%x, %o, %b | 十六进制,八进制,二进制整数 |
%f, %g, %e | 浮点数 |
%t | 布尔值 |
%c | 字符 |
%s | 字符串 |
%q | 带双引号的字符串或带单引号的字符 |
%v | 变量的自然形式 |
%T | 变量的类型 |
%% | 字面上的百分号标志 |
例:找出重复行
package main
import (
"bufio"
"fmt"
"os"
)
func main()
// make可以创建新的map,map[string]int表示一个键为string类型,值为int类型的map
counts := make(map[string]int)
// bufio中的Scanner可以读入输入,以换行分割,以Ctrl+D结束
input := bufio.NewScanner(os.Stdin)
for input.Scan()
counts[input.Text()]++
for i, n := range counts
if n > 1
// 格式化输出
fmt.Printf("%d\\t%s\\n", n, i)
例:从stdin或指定文件读取并找出重复行
package main
import (
"bufio"
"fmt"
"os"
)
func main()
counts := make(map[string]int)
files := os.Args[1:]
if len(files) == 0
countLines(os.Stdin, counts)
else
for _, arg := range files
f, err := os.Open(arg)
// err为nil时表示成功打开
if err != nil
fmt.Fprintf(os.Stderr, "dup2: %v\\n", err)
continue
countLines(f, counts)
f.Close()
for i, n := range counts
if n > 1
fmt.Printf("%d\\t%s\\n", n, i)
// 函数和其他包级别的实体可以任意次序声明
func countLines(f *os.File, counts map[string]int)
input:=bufio.NewScanner(f)
for input.Scan()
// 更改子函数中的counts值会影响到main函数中counts的值
counts[input.Text()]++
输出从url获取的内容
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main()
for _, url := range os.Args[1:]
// 产生一个http请求
resp, err := http.Get(url)
if err != nil
fmt.Fprintf(os.Stderr, "fetch: %v\\n", err)
os.Exit(1)
// 读取响应的响应体
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\\n", url, err)
os.Exit(1)
fmt.Printf("%s\\n", b)
并发获取多个url
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
)
func main()
start := time.Now()
// 创建一个字符串通道,通道是允许某一例程向另一例程传递指定类型的值的通信机制
ch := make(chan string)
for _, url := range os.Args[1:]
// main函数在一个goroutine中执行,go语句创建额外的goroutine,即一个并发执行的函数
go fetch(url, ch)
for range os.Args[1:]
// <-ch是接收发送的值
fmt.Println(<-ch)
fmt.Printf("%.2fs elapsed\\n", time.Since(start).Seconds())
func fetch(url string, ch chan<- string)
start := time.Now()
resp, err := http.Get(url)
if err != nil
// 在通道ch上发送一个值
ch <- fmt.Sprint(err)
return
// io.Copy获取响应内容,并通过ioutil.Discard输出流进行丢弃
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
if err != nil
ch <- fmt.Sprintf("while reading %s: %v\\n", url, err)
return
secs := time.Since(start).Seconds()
ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
迷你web服务器
package main
import (
"fmt"
"log"
"net/http"
)
func main()
// 回声请求调用处理程序
http.HandleFunc("/",handler)
log.Fatal(http.ListenAndServe("localhost:8000",nil))
func handler(w http.ResponseWriter,r *http.Request)
// 回显请求URL r的路径部分
fmt.Fprintf(w,"URL.Path = %q\\n",r.URL.Path)
迷你回声和计数服务器
package main
import (
"fmt"
"log"
"net/http"
"sync"
)
var mu sync.Mutex
var count int
func main()
http.HandleFunc("/", handler)
http.HandleFunc("/count", counter)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
func handler(w http.ResponseWriter, r *http.Request)
// 加锁
mu.Lock()
count++
mu.Unlock()
fmt.Fprintf(w, "URL.Path = %q\\n", r.URL.Path)
func counter(w http.ResponseWriter, r *http.Request)
mu.Lock()
fmt.Fprintf(w, "Count %d\\n", count)
mu.Unlock()
使用&
操作符获取一个变量的地址,使用*
操作符获取指针引用的变量的值。
二、程序结构
1. 名称
go中有25个关键字:
break | defalut | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
内建常量:
true false iota nil
内建类型:
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内建函数:
make len cap new append copy close delete
complex real imag
panic recover
go语言中区分大小写,如果实体(方法或变量)名称以大写字母开头,则它对包外是可见和可访问的,如fmt中的Printf。包名总是由小写字母组成。变量名通常采用驼峰命名
2. 声明
声明的作用是给一个程序实体命名,并设定其部分或全部属性
实体有4个主要的声明:变量(var)、常量(const)、类型(type)和函数(func)
3. 变量
变量声明的方式为:var 变量名 数据类型 = 表达式
,类型和表达式可以省略一个,但不可全省略。如果省略类型,则类型由表达式决定;如果表达式省略,则其初始值对应类型的零值,接口和引用类型的零值是nil。
var a int
var b int = 100
var c = 100
包级别的初始化在main开始之前进行
声明多个变量
var a, b int = 100, 200
var c, d = 100, "abcd"
var (
e int = 100
f bool = true
)
g, h := 100, "abcd"
(1) 短变量声明
格式为:变量名 := 表达式
在局部变量中主要使用短变量声明
短变量声明至少声明一个新变量,否则会编译错误,如
f, err := os.Open(infile)
// 编译错误,因没有新的变量
f, err := os.Create(outfile)
:=
是声明并赋值,并且系统自动推断类型,不需要var关键字,而=
必须先使用var声明
(2) 指针
指向整数变量的指针的数据类型是*int
x := 1
p := &x
// *p是指针指向的变量值
*p = 2
*p++ // *p指向的变量值加1
package main
import "fmt"
func swap(a, b *int)
*a, *b = *b, *a
func main()
a := 10
b := 20
swap(&a, &b)
fmt.Println("a = ", a, " b = ", b)
// 一级指针
var p *int
p = &a
// 二级指针
var pp **int
pp = &p
fmt.Println(&p)
fmt.Println(pp)
(3) new函数和make函数
表达式new(T)
创建一的T类型的匿名变量,初始化为T类型的零值,并返回其地址。
p := new(int) // *int(指针)类型的p
fmt.Println(*p)
*p = 2
表达式make(T)
创建一个T类型的匿名变量,初始化为T类型的零值,并返回该变量。与new的不同之处在于,new返回的是变量地址,而make返回的是变量本身。
make
的形式必须是make(Type, len, cap)
,且Type
的值只能是slice
、map
和channel
三者之一。
make([]int, 2)
make([]int, 2, 4)
make(map[string]string)
make(chan, int)
make(chan, int, 1)
(4) 赋值
x = 1
*p = true
person.name = "bob" // 结构体成员
connt[x] = count[x] * scale // connt[x]为数组或slice或map的元素
// 多重赋值
x, y = y, x
i, j, k = 1, 2, 3
medals := []string"gold", "silver", "bronze"
(5) 类型声明
type
声明定义一个新的名称类型,它和某个已有类型使用同样的底层类型。其格式为:type 类型名 底层类型
package tempconv
import "fmt"
type Celsius float64
type Fahrenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreeezingC Celsius = 0
BoilingC Celsius = 100
)
func CToF(c Celsius) Fahrenheit
// 强制类型转换
return Fahrenheit(c*9/5 + 32)
func FToC(f Fahrenheit) Celsius
return Celsius((f - 32) * 5 / 9)
其中Celsius和Fahrenheit虽然底层类型都是float,但是它们不能进行比较和合并,Celsius()和Fahrenheit()表示强制类型转换。如果两个类型具有相同的底层类型,或都是指向相同底层类型的指针类型,则二者是可以相互转换的。
(6) 包和文件
通过标出的标识符是否以大写字母开头来管理标识符是否对外可见。
包的初始化按照在程序中导入的顺序来进行,依赖顺序优先,每次初始化一个包。
三、基本数据
1. 整数
rune
类型是int32
类型的同义词,byte
类型是uint8
类型的同义词
取模余数正负号总是与被除数一致
运算符优先级从高到低如下:
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
其中&^
表示位清空(AND NOT),在z = x &^ y
中,若y的某位是1,则z的对应位为0,反之为x的对应位。
2. 浮点数
float32和float64
3. 复数
复数有complex64和complex128两种,可以使用real()和imag()分别提取复数的实部和虚部
x := 1 + 2i
4. 布尔值
一个布尔类型的值只有两种:true和false
5. 字符串
字符串是不可变的字节序列,但可以做字符串拼接。
s := "hello,world"
t := "!!!"
fmt.Println(len(s))
fmt.Println(s[0],s[7])
s += t
s[0] = '0' // 报错,不可更改
bytes、strings、strconv、unicode四个标准包对字符串操作特别重要
-
strings包提供了搜索、替换、比较、修整、切分和连接的函数
-
bytes包里也类似,用于操作字节slice
-
strconv提供布尔值、整数、浮点数与字符串类型之间的相互转换函数
-
unicode包有判别文字符号值特性的函数,如IsDigit、IsLetter、IsUpper、IsLower等
6. 常量
const p = 3.14
const(
e = 2.71828
pi = 3.1415926
)
// 其中b和d会复用前一项的表达式及其类型
const(
a = 1
b
c = 2
d
)
// 常量生成器itoa,从0开始取值,逐项加1
type Weekday int
const(
Sunday Weeday = itoa
Monday
Tuestday
Wednesday
Thursday
Friday
Saturday
)
四、复合数据结构
1. 数组
长度固定,且拥有多个相同数据类型的元素。
// 默认初始化为零值
var a [3]int
var b [3]int = [3]int1,2,3
length := len(a) // 内置函数len()可以获取数组大小
// 遍历数组中的元素
for _, v := range b
fmt.Printf("%d\\n",v)
// 用...表示数组长度由初始化元素个数决定,此时变量类型为数组,而非切片
c := [...]int1,2,3
fmt.Printf("%T\\n",c)
// 如果一个数组的元素是可比较的,则数组是可比较的,如果数组长度不同不可比较
fmt.Println(b == c)
package main
import "fmt"
type Currency int
const (
ZERO Currency = iota
ONE
TWO
THREE
FOUR
)
func main()
// 索引:值
num := [...]intZERO: 0, ONE: 1, TWO: 2, THREE: 3, FOUR: 4
fmt.Println(TWO, num[TWO])
// 定义了100个元素的数组,其中下标为99的元素为-1,其他默认为0
a := [...]int99: -1
fmt.Println(a)
在调用函数时,传入的参数会创建一个副本,函数内对数组的任何修改都仅影响副本,此时可以使用引用传递(传递数组的地址)来解决问题
package main
import "fmt"
// 传入的数组必须是长度为4的
func printArray(myArray [4]int)
for index, value := range myArray
fmt.Println("index = ", index, ", value = ", value)
// 只是值拷贝,不会影响原数组的值
myArray[0] = 111
func main()
// 固定长度的数组
var myArray1 [10]int
myArray2 := [10]int1, 2, 3, 4<以上是关于学习笔记Golang语法学习笔记的主要内容,如果未能解决你的问题,请参考以下文章