C++GO语言微服务项目之 go语言基础语法

目录

[01 变量定义](#01 变量定义)

[02 自增语法](#02 自增语法)

[03 指针](#03 指针)

[04 go不支持的语法](#04 go不支持的语法)

[05 string](#05 string)

[06 定长数组-forrange](#06 定长数组-forrange)

[07 动态数组追加元素](#07 动态数组追加元素)

[08 切片截取-copy-make介绍](#08 切片截取-copy-make介绍)

[09 map介绍](#09 map介绍)

[10 函数](#10 函数)

[11 内存逃逸](#11 内存逃逸)

[12 import](#12 import)

[13 命令行参数-switch](#13 命令行参数-switch)

[14 标签与continue-goto-break配合使用](#14 标签与continue-goto-break配合使用)

[15 枚举const-iota](#15 枚举const-iota)

[16 结构体基本语法](#16 结构体基本语法)

[17 init函数](#17 init函数)

[18 defer-文件读取-匿名函数](#18 defer-文件读取-匿名函数)


01 变量定义

二、基础语法

1. 变量定义

```go

package main

import "fmt" //goland会帮我们自动导入程序中使用的包

func main() {

//变量定义: var

//常量定义: const

//01-先定义变量,再赋值 var 变量名 数据类型

var name string

name = "duke" //Ctrl + Alt +l 可以快速格式化代码

var age int

age = 20

fmt.Println("name:", name)

fmt.Printf("name is :%s, %d\n", name, age)

//02 定义时直接赋值

var gender = "man"

fmt.Println("gender:", gender)

//03 定义直接赋值,使用自动推导 (最常用的)

address := "北京"

fmt.Println("address:", address)

//灰色部分表示形参

test(10, "str")

//04-平行赋值

i, j := 10, 20 //同时定义两个变量

fmt.Println("变换前==》 i:", i, ", j:", j)

i, j = j, i

fmt.Println("变换后==》i:", i, ", j:", j)

}

func test(a int, b string) {

fmt.Println(a)

fmt.Println(b)

}

```

![1585881281278](assets/1585881281278.png)

02 自增语法

基础数据类型:

```go

int,int8 int16, int32, int64

uint8 ... uint64

float32

float64

true/false

```

2. 自增语法

c语言:i++, i--, --i, ++i

go语言:i++, i--, 没有--i,++i, 自增语法必须单独一行

```go

package main

import "fmt"

func main() {

i := 20

i++

//++i //这个也是错的, 语义更加明确

//fmt.Println("i:", i++) //这是错误的,不允许和其他代码放在一起,必须单独一行

fmt.Println("i:", i)

}

```

03 指针

3. 指针

```go

package main

import "fmt"

func main() {

//go语义也有指针

//结构体成员调用时: c语言: ptr->name go语言: ptr.name

//go语言在使用指针时,会使用内部的垃圾回收机制(gc : garbage collector),开发人员不需要手动释放内存

//c语言不允许返回栈上的指针,go语言可以返回栈上的指针,程序会在编译的时候就确定了变量的分配位置:

//编译的时候,如果发现有必要的话,就将变量分配到堆上

name := "lily"

ptr := &name

fmt.Println("name:", *ptr)

fmt.Println("name ptr:", ptr)

//02-使用new关键字定义

name2Ptr := new(string)

*name2Ptr = "Duke"

fmt.Println("name2:", *name2Ptr)

fmt.Println("name2 ptr:", name2Ptr)

//可以返回栈上的指针,编译器在编译程序时,会自动判断这段代码,将city变量分配在堆上

res := testPtr()

fmt.Println("res city :", *res, ", address:", res)

//空指针,在c语言: null, c++: nullptr,go: nil

//if两端不用加()

//即使有一行代码,也必须使用{}

if res == nil {

fmt.Println("res 是空,nil")

} else {

fmt.Println("res 是非空")

}

}

//定义一个函数,返回一个string类型的指针, go语言返回写在参数列表后面

func testPtr() *string {

city := "深圳"

ptr := &city

return ptr

}

04 go不支持的语法

4. 不支持的语法汇总

  1. 自增--i, ++i不支持

  2. 不支持地址加减

  3. 不支持三目运算符 ? :

  4. 只有false才能代码逻辑假,数字0和nil不能(只有false/true才能表示逻辑真假,数字不能代表逻辑值)

```go

package main

import "fmt"

func main() {

//if 0 {

// fmt.Println("hello")

//}

//if nil {

// fmt.Println("hello")

//}

if true {

fmt.Println("hello")

}

//a, b := 1, 2

//x := a > b ? 0:-1

}

```

5. 字符串string

```go

package main

import "fmt"

func main() {

//1- 定义

name := "duke"

//需要换行,原生输出字符串时,使用反引号``

usage := `./a.out <option>

-h help

-a xxxx`

//c语言使用单引号 + \来解决

fmt.Println("name :", name)

fmt.Println("usage :", usage)

//2. 长度,访问

//C++: name.length

//GO: string没有.length方法,可以使用自由函数len()进行处理

//len: 很常用

l1 := len(name)

fmt.Println("l1:", l1)

//不需要加()

for i := 0; i < len(name); i++ {

fmt.Printf("i: %d, v: %c\n", i, name[i])

}

//3-拼接

i, j := "hello", "world"

fmt.Println("i+j=", i+j)

//使用const修饰为常量,不能修改

const address = "beijing" //在编译期就确定了类型,是预处理,所以不需要推导

const test = 100

//address = "上海"

fmt.Println("address:", address)

//可以直接对比字符串,使用ASCII的值进行比较

if i > j {

;

}

}

```

05 string

5. 定长数组

```go

package main

import "fmt"

func main() {

//1-定义,定义一个具有10个数字的数组

//c语言定义: int nums[10] ={1,2,3,4}

//go语言定义:

// nums := [10]int{1,2,3,4} (常用方式)

// var nums = [10]int{1,2,3,4}

// var nums [10]int = [10]int{1,2,3,4}

nums := [10]int{1, 2, 3, 4,0,0,0,0}

//2-遍历,方式一

for i := 0; i < len(nums); i++ {

fmt.Println("i:", i, ", j:", nums[i])

}

//方式二: for range ===> python支持

//key是数组下标,value是数组的值

for key, value := range nums {

//key=0, value=1 => nums[0]

value += 1

//value全程只是一个临时变量,不断的被重新赋值,修改它不会修改原始数组

fmt.Println("key:", key, ", value:", value, "num:", nums[key])

}

//在go语言中,如果想忽略一个值,可以使用_

//如果两个都忽略,那么 就不能使用 := ,而应该直接使用 =

for _, value := range nums {

fmt.Println("_忽略key, value:", value)

}

//不定长数组定义

//3-使用make进行创建数组

}

```

06 定长数组-forrange

6. 不定长数组(切片、slice)

切片:slice,它的底层也是数组,可以动态改变长度

- 切片1:

```go

package main

import "fmt"

func main() {

//切片:slice,它的底层也是数组,可以动态改变长度

//定义一个切片,包含多个地名

//names := [10]string{"北京", "上海", "广州", "深圳"}

names := []string{"北京", "上海", "广州", "深圳"}

for i, v := range names {

fmt.Println("i:", i, "v:", v)

}

//1.追加数据

names1 := append(names, "海南")

fmt.Println("names:", names)

fmt.Println("names1:", names1)

fmt.Println("追加元素前,name的长度:", len(names), ",容量:", cap(names))

names = append(names, "海南")

//fmt.Println("names追加元素后赋值给自己:", names)

fmt.Println("追加元素后,name的长度:", len(names), ",容量:", cap(names))

names = append(names, "西藏")

fmt.Println("追加元素后,name的长度:", len(names), ",容量:", cap(names))

//2.对于一个切片,不仅有'长度'的概念len(),还有一个'容量'的概念cap()

nums := []int{}

for i := 0; i < 50; i++ {

nums = append(nums, i)

fmt.Println("len:", len(nums), ", cap:", cap(nums))

}

}

```

![1585895660673](assets/1585895660673.png)

小结:

  1. 可以使用append进行追加数据

  2. len获取长度,cap获取容量

  3. 如果容量不足,再次追加数据时,会动态分配2倍空间

07 动态数组追加元素

- ==切片2:==

```go

package main

import "fmt"

func main() {

names := [7]string{"北京", "上海", "广州", "深圳", "洛阳", "南京", "秦皇岛"}

//想基于names创建一个新的数组

names1 := [3]string{}

names1[0] = names[0]

names1[1] = names[1]

names1[2] = names[2]

//切片可以基于一个数组,灵活的创建新的数组

names2 := names[0:3] //左闭右开

fmt.Println("names2:", names2)

names2[2] = "Hello"

fmt.Println("修改names[2]之后, names2:", names2)

fmt.Println("修改names[2]之后, names:", names)

//1. 如果从0元素开始截取,那么冒号左边的数字可以省略

names3 := names[:5]

fmt.Println("name3:", names3)

//2. 如果截取到数组最后一个元素,那么冒号右边的数字可以省略

names4 := names[5:]

fmt.Println("name4:", names4)

//3. 如果想从左至右全部使用,那么冒号左右两边的数字都可以省略

names5 := names[:]

fmt.Println("name5:", names5)

//4. 也可以基于一个字符串进行切片截取 : 取字符串的字串 helloworld

sub1 := "helloworld"[5:7]

fmt.Println("sub1:", sub1) //'wo'

//5. 可以在创建空切片的时候,明确指定切片的容量,这样可以提高运行效率

//创建一个容量是20,当前长度是0的string类型切片

//make的时候,初始的值都是对应类型的零值 : bool ==> false, 数字==> 0, 字符串 ==> 空

str2 := make([]string, 10, 20) //第三个参数不是必须的,如果没填写,则默认与长度相同

fmt.Println("str2:", &str2[0])

fmt.Println("str2: len:", len(str2), ", cap:", cap(str2))

str2[0] = "hello"

str2[1] = "world"

//6.如果想让切片完全独立于原始数组,可以使用copy()函数来完成

namesCopy := make([]string, len(names))

//func copy(dst, src []Type) int

//names是一个数组,copy函数介收的参数类型是切片,所以需要使用[:]将数组变成切片

copy(namesCopy, names[:])

namesCopy[0] = "香港"

fmt.Println("namesCopy:", namesCopy)

fmt.Println("naemes本身:", names)

}

```

08 切片截取-copy-make介绍

![1585898372086](assets/1585898372086.png)

7.字典(map)

哈希表 , key=>value, 存储的key是经过哈希运算的

```go

package main

import "fmt"

func main() {

//1. 定义一个字典

//学生id ==> 学生名字 idNames

var idNames map[int]string //定义一个map,此时这个map是不能直接赋值的,它是空的

idNames[0] = "duke"

idNames[1] = "lily"

for key, value := range idNames {

fmt.Println("key:", key, ", value:", value)

}

}

```

![1585898733163](assets/1585898733163.png)

  1. 使用map之前,一定要对map进行分配空间

```go

package main

import "fmt"

func main() {

//1. 定义一个字典

//学生id ==> 学生名字 idNames

var idNames map[int]string //定义一个map,此时这个map是不能直接赋值的,它是空的

//2. 分配空间,使用make,可以不指定长度,但是建议直接指定长度,性能更好

idScore := make(map[int]float64) //这个也是正确的

idNames = make(map[int]string, 10) //建议使用这种方式

//3. 定义时直接分配空间

//idNames1 := make(map[int]string, 10) //这是最常用的方法

idNames[0] = "duke"

idNames[1] = "lily"

//4. 遍历map

for key, value := range idNames {

fmt.Println("key:", key, ", value:", value)

}

//5. 如何确定一个key是否存在map中

//在map中不存在访问越界的问题,它认为所有的key都是有效的,所以访问一个不存在的key不会崩溃,返回这个类型的零值

//零值: bool=》false, 数字=》0,字符串=》空

name9 := idNames[9]

fmt.Println("name9:", name9) //空

fmt.Println("idScore[100]:", idScore[100]) //0

//无法通过获取value来判断一个key是否存在,因此我们需要一个能够校验key是否存在的方式

value, ok := idNames[1] //如果id=1是存在的,那么value就是key=1对应值,ok返回true, 反之返回零值,false

if ok {

fmt.Println("id=1这个key是存在的,value为:", value)

}

value, ok = idNames[10]

if ok {

fmt.Println("id=10这个key是存在的,value为:", value)

} else {

fmt.Println("id=10这个key不存在,value为:", value)

}

//6. 删除map中的元素

//使用自由函数delete来删除指定的key

fmt.Println("idNames删除前:", idNames)

delete(idNames, 1) //"lily"被kill

delete(idNames, 100) //不会报错

fmt.Println("idNames删除后:", idNames)

//并发任务处理的时候,需要对map进行上锁 //TODO

}

```

![1585899844151](assets/1585899844151.png)

09 map介绍

8. 函数

```go

package main

import "fmt"

//1.函数返回值在参数列表之后

//2.如果有多个返回值,需要使用圆括号包裹,多个参数之间使用,分割

func test2(a int, b int, c string) (int, string, bool) {

return a + b, c, true

}

func test3(a, b int, c string) (res int, str string, bl bool) {

var i, j, k int

i = 1

j = 2

k = 3

fmt.Println(i, j, k)

//直接使用返回值的变量名字参与运算

res = a + b

str = c

bl = true

//当返回值有名字的时候,可以直接简写return

return

//return a + b, c, true

}

//如果返回值只有一个参数,并且没有名字,那么不需要加圆括号

func test4() int {

return 10

}

func main() {

v1, s1, _ := test2(10, 20, "hello")

fmt.Println("v1:", v1, ",s1:", s1)

v3, s3, _ := test3(20, 30, "world")

fmt.Println("v3:", v3, ", s3:", s3)

}

```

10 函数

内存逃逸:

```go

package main

import "fmt"

func main() {

p1 := testPtr1()

fmt.Println("*p1:", *p1)

}

//定义一个函数,返回一个string类型的指针, go语言返回写在参数列表后面

func testPtr1() *string {

name := "Duke"

p0 := &name

fmt.Println("*p0:", *p0)

city := "深圳"

ptr := &city

return ptr

}

```

```go

go build -o test.exe --gcflags "-m -m -l" 11-内存逃逸.go > 1.txt 2>&1

grep -E "name|city" 1.txt --color

```

![1585902031122](assets/1585902031122.png)

9. import

创建目录结构:

![1585903432739](assets/1585903432739.png)

add.go

```go

package add

func add(a, b int) int {

return a + b

}

```

sub.go

```go

package sub

//在go语言中,同一层级目录,不允许出现多个包名

func Sub(a, b int) int {

test4() //由于test4与sub.go在同一个包下面,所以可以使用,并且不需要sub.形式

return a - b

}

```

11 内存逃逸

utils.go

```go

package sub

//package utils //不允许出现多个包名

import "fmt"

func test4() {

fmt.Println("this is test4() in sub/utils!")

}

```

main.go

```go

package main

import (

SUB "12-import/sub" //SUB是我们自己重命名的包名

//"12-import/sub" //sub是文件名,同时也是包名

. "12-import/sub" //.代表用户在调用这个包里面的函数时,不需要使用包名.的形式,不见一使用的

"fmt"

)

func main() {

//res := sub.Sub(20, 10) //包名.函数去调用

res := SUB.Sub(20, 10) //包名.函数去调用

fmt.Println("sub.Sub(20,10) =", res)

res1 := Sub(30, 20)

fmt.Println("Sub(30, 20) :", res1)

//这个无法被调用,是因为函数的首写字母是小写的

//如果一个包里面的函数想对外提供访问权限,那么一定要首写字母大写,

// 大写字母开头的函数相当于 public,

// 小写字母开头的函数相当于 private, 只有相同包名的文件才能使用

//add.add(10,20)

}

```

结果:

![1585903519834](assets/1585903519834.png)

12 import

13 命令行参数-switch

  1. 案例:递归遍历指定目录的文件!

```sh

#!/bin/bash

#案例:递归遍历指定目录的文件!

#目录由用户通过命令行传入:

#./recursive.sh ..

recursive()

{

#接收用户输入的目录

#currDir="$1"

local currDir="$1"

echo "====> 当前目录为:" "$currDir"

#遍历当前目录

for i in `ls $currDir`

do

#这样才是有效的路径,后续才能正确判断性质

#item="currDir+"/"+i" <<=== 这是错误的, /usr/+/+test.txt

item="currDir/i"

#echo "item: " $item

#如果i是文件,则打印

if [ -f "$item" ]; then

echo "文件找到: $item"

elif [ -d "$item" ];then

#如果i是文件夹,则递归遍历

echo "文件夹找到:" "$item"

recursive $item

else

echo "未知文件:" "$item"

fi

done

}

14 标签与continue-goto-break配合使用

#判读输入参数的个数

if [ $# -ne 1 ];then

echo "Usage: recursive <文件目录>"

exit

fi

recursive $1

```

调试

shell调试参数:

  • -n 帮助我们快速检查脚本的语法,而不是逐条执行

  • 脚本如果很大,中间执行过程很长,如果不能实现发现脚本错误,会浪费时间

  • ```sh

bash -n ./recursive.sh

```

  • ![1585713051107](assets/1585713051107.png)

  • -x,执行脚本的同时将代码执行过程打印到屏幕,便于追踪

  • bash -x recursive.sh ==》 作用于整个脚本

  • 可以在代码中,对指定的代码段进行调试

  • set -x ==> 启动调试

  • xxxx

  • set +x ==> 停止调试

![1585713439412](assets/1585713439412.png)

  • -v,一边执行脚本,一边打印错误输出(很少用)

shift 8 ==> *, 快速定位当前光标所值单词(向下寻找,n)

shift 3 ==> #, 快速定位当前光标所值单词(向上寻找,n)

15 枚举const-iota

三、正则表达式

  1. 以S开头的字符串

  2. ^: 可以限定一个字符开头

  3. ```sh

目标:ShelloShello world

表达式:^Shello

```

  1. 以数字结尾的字符串

  2. $:可以限定结尾

  3. 表示数字方式:

  4. 0123456789

  5. 0-9

  6. ```sh

目标:helloworld9

表达式:helloworld[0-9]$

```

  1. 匹配空字符串(没有任何字符)

  2. ^$

  3. 字符串只包含三个数字

  4. 三个数字:\[0-9\]\[0-9\]\[0-9\]

  5. 字符串只有3到5个字母

  6. 字母表示:

  7. a-zA-Z\], 中间不要加空格

  8. 重复次数{m, n}:

  9. 匹配前面修饰字符的次数,最少m次,最多n次

  10. 匹配不是a-z的任意字符

  11. ^ 在[]内部的时候,表示取反(注意,不是限定开头)

  12. ^[^0-9\] : 以非数字开头的字符

  13. 内反,外头

  14. \^a-z\\\] \<\<\<===

  15. 表示0-1的方法

  16. {0,1}

  17. ? : 表示0,1个

  18. a-zA-Z0-9_\]?

内容

hello

hello1

hello*

正则:

hello[a-zA-Z0-9_]?

匹配结果:

hello

hello1

hello

```

16 结构体基本语法

  1. 字符串有1个或多个空白符号(\t\n\r等)

  2. \s ==》 代表空格

  3. +:一个或多个

  4. ```h

内容:hello world

正则:hello\s+world

结果:hello world

```

  1. 字符串有0个或者若干个任意字符(除了\n)

  2. .: 点,代表任意字符,除了\n

  3. *:星号,重复前一个字符0-n次

  4. 匹配任意字符,任意次数:通常做法,是: .* ==> 相当于通配符里面的 *

  5. ```sh

内容:hello asdfaasdfasdfadfasdfasdf asdfasdfasdf world

正则:hello.*world

结果:hello asdfaasdfasdfadfasdfasdf asdfasdfasdf world

```

  1. .{,} ==> 与 .*效果相同

  2. 小结:

  3. ? ===> 重复前面一个字符0-1

  4. \+ ===> 重复前面一个字符1-n

  5. \* ===> 重复前面一个字符0-n

  6. . ===> 表示任意字符

  7. ^ ===> 限定以某个字符开头

  8. \^a-z\] ===\> 在方括号内,表示取反(非a-z的任意字符)

  9. 数字 ====》 [0123456789] 或[0-9]

  10. 字母 ===> [abcd] [a-zA-Z]

  11. 匹配0或任意多组ABC,比如ABC,ABCABCABC

  12. 使用()进行分组,组成新的匹配单元

  13. (ABC)*

  14. 字符串要么是ABC,要么是123

  15. (ABC|123)

  16. ```sh

内容:

ABC

ABCABCABCD

123456

hellowlrd

正则:(ABC|123)

```

17 init函数

  1. 字符串只有一个点号

  2. ^\\.$

  3. 匹配十进制3位整数 :

  4. 0~999 , 5, 10 , 200, 999

  5. 分段匹配:

  6. 一位数:

  7. 0-9

  8. 二位数:

  9. 1-9\]\\\[0-9\\

  10. 三位数:

  11. \[1-9\][0-9]{2}

  12. 整合:

  13. ^ ([0-9]|[1-9]\[0-9\]{1,2})$

  14. 匹配0-255的整数, ip地址

  15. 分段

  16. 一位:[0-9]

  17. 二位:[1-9]\[0-9\]

  18. 三位:

  • 100- 199

  • 1[0-9]{2}

  • 200-249

  • 2[0-4]\[0-9\]

  • 250-255

  • 25[0-5]

  1. 整合:
  • `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`

  • 192.168.1.1

  1. 匹配端口号

  2. 0~65536

  3. email

```sh

\\w!#$%\&'\*+/=?\^_\`{\|}\~-\]+(?:\\.\[\\w!#$%\&'\*+/=?\^_\`{\|}\~-\]+)\*@(?:\[\\w\](?:\[\\w-\]\*\[\\w\])?\\.)+\[\\w\](?:\[\\w-\]\*\[\\w\])? \`\`\` ## 18 defer-文件读取-匿名函数 三种正则表达式: 1. 基础正则:(不使用) ==》 +?\* 都当成普通字符来处理 2. 扩展正则:(我们默认使用这个)==》 +?\* 当成特殊字符 3. Perl正则: 1. +?\*是特殊字符 2. \\d ==》 数字 \[0-9

  1. \w ==> 字符[a-zA-Z]

  2. 用法\d{3}\w 可以匹配:212c

四、其他命令

sort(排序)

```sh

-f 忽略字符大小写

-n 按照数值比较

-t 分割字符

-k 指定分割之后比较的字段

-u 只显示一次

-r 反向排序

```

使用:分割之后,截取第三段,以数字的值进行比较,反向打印

```sh

sort passwd -t: -k3 -n -r

```

uniq(删除重复)

uniq.txt

```sh

xiaoliu 99 man 25

xiaowen 98 man 24

xiaoliu 99 man 25

```

去重方式,需要重复的两行挨着才能去重,一般和sort配合使用

```sh

cat uniq.txt | sort | uniq

```

![1585728928962](assets/1585728928962.png)

wc

-l: 统计文件的行数 <==最常用

-c:统计字符

-w:统计单词数量

相关推荐
bing_1584 分钟前
在Spring Boot 中如何配置MongoDB的副本集 (Replica Set) 或分片集群 (Sharded Cluster)?
spring boot·后端·mongodb
青出于兰10 分钟前
C语言|函数的递归调用
c语言·开发语言
whoarethenext15 分钟前
数据结构堆的c/c++的实现
c语言·数据结构·c++·
2401_8582861120 分钟前
CD36.【C++ Dev】STL库的string的使用 (下)
开发语言·c++·类和对象·string
強云32 分钟前
性能优化-初识(C++)
c++·性能优化
sanx1842 分钟前
从零搭建体育比分网站完整步骤
java·开发语言
若水晴空初如梦1 小时前
QT聊天项目DAY09
开发语言·qt·bootstrap
leo__5201 小时前
matlab 产生载频和宽度的脉冲串,并叠加高斯白噪声
开发语言·人工智能·matlab
关岭风尘1 小时前
Matlab/Simulink - BLDC直流无刷电机仿真基础教程(七) - 波形解析专题P2
开发语言·matlab·bldc电机·相线接错·电机缺相
冼紫菜1 小时前
Spring 项目无法连接 MySQL:Nacos 配置误区排查与解决
java·spring boot·后端·mysql·docker·springcloud