互联网世界中的C语言——我的golang学习笔记:1(基础语法快速过)

前言

学习任何知识都会有一个学习背景

最近,我们团队乃至我司整个云服务,上go的呼声越来越高!新服务已经开始用go开发,部分现有Java版的服务重构为go也只是时间问题而已,故相关技术积累势在必行!在云网络的分布式服务乃至在一切高并发,分布式后台服务中,golang都有着很大的优势。

据我对国内互联网行业的实际考察,了解,目前国内主流互联网公司都在积极投入go的怀抱……

青云更是全栈使用了go……

还有火的一塌糊涂的docker。

它为云而生。

它为并发而生。

还有go的安全、简洁、高效

有良好的Java、C/C++背景,拿起go很容易。

……

参考:

2017年值得学习的三大编程语言

2017年编程语言排行榜,相比2016,go的飞速上升趋势很明显。而Java有很大份额的缩减……

当然,当前在企业级应用开发中,Java依然占有一席之地。

而个人认为:一个优秀的研发工程师乃至架构师……是不能仅仅局限于一门语言的,语言只是拿来就用的工具,关键还是实战经验和行业内功的修炼。但是对于任何编程语言,不学也不是凭空就会的。

简单说go的历史和应用

历史和概况

Go语言是一个年轻的语言,是Google在2009年发布的第二款开源的编程语言,它是编译型语言,专门针对多处理器系统应用程序的编程进行了优化,因此使用Go编译的程序可以媲美C或C++代码的速度。

go还具有类似Java的垃圾回收机制,且实现了数组安全……故go有机的结合了Java和C/C++,使其本身更加安全、高效的同时还天然支持并行进程。

可以说,go是为并发而生的! 既然它是为并发而生,那么在编写高并发程序时,go写起来也十分简洁。

目前流行的go应用

1、Go语言的杀手级应用就是Docker,Docker应该大家都知道,目前在国内火的一塌糊涂。

2、Codis,一种Redis的集权解决管理方案,很大部分go开发,由豆瓣推出。

3、

Glow,类似Hadoop,也是一种大数据处理框架,性能非常好,是Hadoop的go的实现。

4、

Cockroach数据库,译作蟑螂……意味着该数据库的生存能力很强,是高稳定性业务环境的首选数据库之一。

……

go的开发环境搭建

可以选择源码安装、标准安装包(win下),第三方工具安装(brew,install,yum等)。

如果是win,就使用标准包。官网有下载地址。

推荐第三方工具安装,简单。

比如Mac下的Homerbrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装go

brew install go

看安装成功之后的提示就能知道,go的运行需要一个环境变量——GOPATH,是go中非常重要的一个环境变量,必须配置好,假如不配,那么安装go的一些工具,或者自己写的包,就会出问题。当然还有GOROOT。

PS:其实在目前的go版本中,这些环境变量已经在安装的时候默认被配置ok了。只需要在机器的环境配置文件中做如下操作即可:

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin
==> Summary
??  /usr/local/Cellar/go/1.9: 7,639 files, 293.7MB

$GOPATH 目录约定有三个子目录:

  • src 存放源代码(比如:.go .c .h .s,以及下载安装的一些工具包)
  • pkg 编译后生成的文件(比如:.a)
  • bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中)

下面查看go的环境变量配置,看看配置的情况如何了。

发现查看环境变量的是go env命令

bash-3.2$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/dashuai/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.9/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.9/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build746511617=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

发现GOROOT、GOPATH等环境变量确实已经在安装go 1.9 时,被默认设置。

然后,按照之前安装go成功之后的提示,在.bash_profile文件中进行如下配置:

 export PATH=$PATH:/usr/local/opt/go/libexec/bin

最后,source下即可。

PS:各个环境变量含义

  • $GOROOT 表示go的安装位置,它的值一般都是 $HOME/go,当然也可以安装在别的地方,这个都无所谓的。
  • $GOARCH 表示目标机器的处理器架构。
  • $GOOS 表示目标机器的操作系统,它的值可以是 darwin,freebsd,linux 或 windows。
  • $GOBIN 表示编译器和链接器的安装位置,默认是 $GOROOT/bin,如果使用的是 Go 1.0.3 及以后的版本,一般情况下可以将它的值设置为空,Go 将会使用前面提到的默认值。
  • $GOPATH 表示工作的环境路径,允许包含多个目录。当有多个目录时,Windows分隔符是分号,Linux是冒号,当有多个GOPATH时,默认会将go get命令的内容放在第一个目录下。$GOPATH 目录约定有三个子目录:
    • src 存放源代码(比如:.go .c .h .s 等)
    • pkg 编译后生成的文件(比如:.a)
    • bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 \$PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)很多Go 命令都依赖于此变量,例如go get命令会将获取到的包放到GOPATH中。

go的IDE

可以使用LiteIDE,为go量身打造。

熟悉Java的也可以直接使用eclipse或者intelli idea,都有相关插件,本人使用了eclipse。

当然,直接上VIM也是大有人在的。

hello word!

和Java一样,go定义包使用package,导入包使用import,程序入口也是main,函数声明使用func,同Java,Go也需要括号包住函数体,不同的是,语句的结束可以加分号,也可以不加,看个人习惯。比如从Java,C++来的,又是强迫症患者,那么一般都要写分号。如下:

  1 package main
  2 import "fmt"
  3
  4 /*
  5 注释风格和C/C++,JAVA类似
  6 */
  7 func main() {
  8     fmt.Println("hello world!"); //打印hello world!
  9 }
 10

运行go程序,可以使用build命令先编译连接源代码,生成可执行文件(程序文件名本身),方便以后随时拿来运行。

wangyishuaideMacBook-Pro:goStu wangyishuai$ go build hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello    hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ ./hello
hello world!

当然,图省事,直接使用go run命令执行也是ok的,但是这样不会生成任何中间结果。

wangyishuaideMacBook-Pro:goStu wangyishuai$ rm hello
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello.go
wangyishuaideMacBook-Pro:goStu wangyishuai$ go run hello.go
hello world!
wangyishuaideMacBook-Pro:goStu wangyishuai$ ls
hello.go

有一个小坑,如果import的包不用,编译会报错。比如,fmt包导入了就必须使用,类似的包括变量等。当注释掉第8行代码,在执行会报错,这点严谨性,比Java强多了。

wangyishuaideMacBook-Pro:goStu wangyishuai$ go run hello.go
# command-line-arguments
./hello.go:2:8: imported and not used: "fmt"

go的常用关键字(25个)

  • var和const :变量和常量的声明
  • var varName type  或者 varName : = value
  • package and import: 导入
  • func: 用于定义函数和方法
  • return :用于从函数返回
  • defer someCode :在函数退出之前执行
  • go : 用于并行
  • select 用于选择不同类型的通讯
  • interface 用于定义接口
  • struct 用于定义抽象数据类型
  • break、case、continue、for、fallthrough、else、if、switch、goto、default 流程控制
  • chan用于channel通讯
  • type用于声明自定义类型
  • map用于声明map类型数据
  • range用于读取slice、map、channel数据

如上,其实和其他大部分语言都差不多。其中go、chan、以及select是go的特色和重点,又比如map类似Java的hashmap……其他的也没什么新鲜的,接下来快速过。

go程序的组织模式

go程序类似Java,都是通过package组织,然后每个独立的、可运行的go程序的入口必须都是main函数,也就是main包里的main函数。

变量和常量

使用var关键字

var是go最基本的定义变量方式 ,但是它的变量类型是放在了变量名后面。

如下,格式化打印语句

Printf

和C语言非常像,其实go就是C语言衍生改进而来,这也是为什么称它为互联网世界中的C语言的原因。

package main 

import (
	"fmt"
)

func main() {
	var x int;
	x = 1;
	fmt.Printf("%d", x);
}

简洁的定义

有时候会觉得总是多写一个var比较烦,那么可以用:=来定义变量(个人感觉也不是想象中的那么简洁……)

package main 

import (
    "fmt"
)

func main() {
    var x int;
    x = 1;
    fmt.Printf("%d", x);

    a, b, c := 1, 2, 3; // 自动判断类型,类似Python
    fmt.Println(a, b, c);
}

这种方式类似Python,省去了写类型的步骤,在go程序中十分常见。但是这样的写法有一个缺点::= 无法用于函数体外部,而var可以。故var也是有存在的必要的。

eclipse直接提示变量y的定义错误,而变量x是ok的。

注意:

1、package和import必须挨着,也就是它们中间不能有其他代码,否则报错。

2、对于没有显式初始化的变量,Go语言总是将零值赋值给该变量。

3、在Go语言中,声明变量的时候,类型名总是在变量名的后面。

const常量

没什么可说的,和C语言一样的用法,唯一不同的就是定义常量时,加类型或者不加都可以,但是注意,最好养成习惯,类型加在常量名后面。

func main() {
    fmt.Println(x);
    const dashuai string = "dashuai";
    const gogogo = 888;
}

而且,字符串数据类型和Java不太一样,是完全小写的string。下面会说到。

注意:

1、当需要设置多个常量的时候,不必重复使用const,可以使用小括号设置 (var同理)

package main

import (
    "fmt"
)

func main() {
    var (
        a int;
        b string;
        c bool;
    )

    fmt.Println(a, b, c) // 0  false

    const (
        aa = 1
        bb = "dada"
        cc = true
    )

    fmt.Println(aa, bb, cc)
}

数据类型

bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr

无类型的数值常量可以兼容go内置的任何类型的数值

在不同类型数值之间进行运算或者比较操作,需要进行类型转换。类型转换采用type(value)的方式,只要合法就能转换成功,即使会导致数据精度丢失。

快速过一下,和Java对比,如下常用的几个:

整型

1、go独有的 rune,其实就是代表了无符号32位整型 unit32。

2、有符号整型 int (依赖于os,可能是int32或者int64)

  1. int8 [-128, 127]
  2. int16 [-32768, 32767]
  3. int32 [-2147483648, 2147483647]
  4. int64 [-9223372036854775808, 9223372036854775807]

也就是说,go是见名知意,go中可以直接定义xxx位的整型。

3、字节 byte,等价于 uint8

4、无符号整型 uint ,依赖于不同os实现,可以是uint32或者uint64

  1. uint8 [0, 255]
  2. uint16 [0, 65535]
  3. uint32 [0, 4294967295]
  4. uint64 [0, 18446744073709551615]
  5. uintptr 一个可以恰好容纳指针值的无符号整型(对32位平台是uint32, 对64位平台是uint64)

可以通过unsafe.Sizeof函数实现类似C语言里sizeof操作符的功能,查看类型的字节长度。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    str := "dashuai"
    fmt.Println(unsafe.Sizeof(str)) // 16

    a := 1
    fmt.Println(unsafe.Sizeof(a)) // 8

    var b int8 = 1
    fmt.Println(unsafe.Sizeof(b)) // 1

    var c int32 = 1
    fmt.Println(unsafe.Sizeof(c)) // 4

    var d int64 = 1
    fmt.Println(unsafe.Sizeof(d)) // 8
}

unsafe包含了用于获取Go语言类型信息的方法。

浮点类型

1、float32 ,±3.402 823 466 385 288 598 117 041 834 845 169 254 40x1038 计算精度大概是小数点后7个十进制数

2、float64,±1.797 693 134 862 315 708 145 274 237 317 043 567 981x1038 计算精度大概是小数点后15个十进制数

布尔类型 bool(取值true false)

注意区分Java的boolean/Boolean写法,其实是和C语言一样,这里也说下,学习go,就对比着C语言和Java语言,这样用起来是很快的。

布尔值使用内置的true和false。Go语言支持标准的逻辑和比较操作,这些操作的结果都是布尔值。还可以通过!b的方式反转变量b的真假。也就是逻辑非。

go可直接定义复数

这也是go独有的, complex32、complex64,在针对一些算法的实现时,非常方便,比如傅里叶变换。

func main() {
    var x complex64 = 10 + 9i;
    fmt.Print(x + x);
}

complex32复数,实部和虚部都是float32

complex64复数,实部和虚部都是float64

字符串

常用是string定义,Go语言中的字符串是 UTF-8编码格式(当字符为 ASCII 码时则占用 1 个字节,其它字符根据需要占用 2-4 个字节)。

UTF-8 是被广泛使用的编码格式,是文本文件的标准编码,其它包括 XML 和 JSON 在内,也都使用该编码。由于该编码对占用字节长度的不定性,Go 中的字符串也可能根据需要占用 1 至 4 个字节,这与C++、Java 或者 Python 不同。

Go 这样做的好处是:不仅减少了内存和硬盘空间占用,同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。

Go语言中字符串的可以使用双引号( " )或者反引号( ` )来创建。

双引号用来创建可解析的字符串,所谓可解析的是指字符串中的一些符号可以被格式化为其他内容,如 "\n" 在输出时候会被格式化成换行符, 如果需要按照原始字符输出必须进行转义。

反引号创建的字符串原始是什么样,那输出还是什么,不需要进行任何转义。

package main

import (
    "fmt"
)

func main() {
    str := "123"
    str1 := "\n"
    str2 := `\n`
    str3 := "\\n"
    fmt.Println(str) // 123
    fmt.Println(str1) // 换行
    fmt.Println(str2) // \n
    fmt.Println(str3) // \n
}

注意

1、Go语言中的部分转义字符
\\ 反斜线

\‘  单引号

\" 双引号

\n 换行符

\uhhhh  4个16进制数字给定的Unicode字符

2、在Go语言中单个字符可以使用单引号 ‘ 来创建。而且,一个单一的字符可以用一个单一的rune来表示(rune是uint32类型)。这也是容易理解的,因为Go语言的字符串是UTF-8编码,其底层使用4个字节表示,也就是32 bit。

格式化字符串

目前为止,使用了fmt.Printffmt.Prinfln,对于前者的使用,就像C语言中的printf函数一样,常用的格式化指令如下:

格式化指令 含义
%% %字面量
%b 一个二进制整数,将一个整数格式化为二进制的表达方式
%c 一个Unicode的字符
%d 十进制数值
%o 八进制数值
%x 小写的十六进制数值
%X 大写的十六进制数值
%U 一个Unicode表示法表示的整形码值,默认是4个数字字符
%s 输出以原生的UTF-8字节表示的字符,如果console不支持UTF-8编码,则会输出乱码
%t 以true或者false的方式输出布尔值
%v 使用默认格式输出值,或者使用类型的String()方法输出的自定义值,如果该方法存在的话
%T 输出值的类型

常用的格式化指令修饰符如下:

  • 空白 如果输出的数字为负,则在其前面加上一个减号"-"。如果输出的是整数,则在前面加一个空格。使用%x或者%X格式化指令输出时,会在结果之间添加一个空格。例如fmt.Printf("% X", "实")输出E5 AE 9E
  • #
    • %#o 输出以0开始的八进制数据
    • %#x 输出以0x开始的十六进制数据
  • + 让格式化指令在数值前面输出+号或者-号,为字符串输出ASCII字符(非ASCII字符会被转义),为结构体输出其字段名
  • - 让格式化指令将值向左对齐(默认值为像右对齐)
  • 0 让格式指令以数字0而非空白进行填充

字符串处理包

strings 包提供了如查找字符串,分割字符串,判断前后缀,判断字符串包含,字符串替换,统计字符串出现的次数等常用操作。

strconv 包提供了许多可以在字符串和其他类型的数据之间进行转换的函数。例如可以将数字转换为字符串,将数字样式的字符串转换为数值(将字符串"12345"转换int类型的整数)

这些直接查文档就可以了。

数组 array slice

array可以类比Python的元组,slice可以类比Python的列表,Java的list,很好理解。

package main 

import (
    "fmt"
)

/*
array用法
*/
func main() {
    var x [10]int;
    x[0] = 100;
    x[8] = 800;
    // %v 可以直接遍历打印数组,非常方便,C语言是没有的。非常高级的用法%v,意思是value,同样数组也不能越界使用。
    // 如果没有初值,自动设为0
    fmt.Printf("%v \n", x);
    // 看长度,和Python的len函数一样
    fmt.Println(len(x));

    var str [5]string;
    str[3] = "aaaa";
    // 默认初值为空串
    fmt.Printf("%v \n", str);

    // 如果使用简洁声明,那么可以直接赋值
    y := [3]int{1, 2, 3}
    fmt.Printf("%v \n", y);
}

在看下slice,其实它就是一个动态数组

    var x [10]int; // 这是一个静态数组,array,之前说过了
    var slice_x []int; // 非常明显,不需要显式声明长度

还可以通过从一个数组或在一个已经存在的slice中再次声明slice

    xx := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 一个静态数组
    z := xx[1:3]; //对xx切片(和Python一样的道理),切片范围是前闭后开区间,也就是截取了2,3。然后截取的结果赋值给数组z,go的数组同样从下标0开始

其实,现在的z就变为了动态数组slice,类似于指针。

同样slice也可以用简洁方式声明,并且配合关键字make去分配内存空间,make用于内建类型(map、slice 和channel)的内存分配,有些类似C语言的malloc。

package main 

import (
    "fmt"
)

func main() {
    x := make([]int, 3, 8); // 长度为3,容量为8
    fmt.Printf("%v \n", x); // [0 0 0] 

    // 会自动扩容
    x = append(x, 1, 2, 3, 4, 5, 6, 7, 8);
    fmt.Printf("%v \n", x); // [0 0 0 1 2 3 4 5 6 7 8]
    fmt.Printf("%v %v \n", len(x), cap(x)); // 11 16
}

小结

1、总的来说,如下图,可以从一个array声明slice,其实就是指针指向。

2、在Go语言中,字符串支持切片操作,但是需要注意的是:

如果字符串都是由ASCII字符组成,那可以随便使用切片进行操作

如果字符串中包含其他非ASCII字符,直接使用切片获取想要的单个字符时需要十分小心,因为对字符串直接使用切片时是通过字节进行索引的,但是非ASCII字符在内存中可能不是由一个字节组成。如果想对字符串中字符依次访问,可以使用range操作符。

另外获取字符串的长度可能有两种含义,一种是指获取字符串的字节长度,一种是指获取字符串的字符数量。

3、字符串支持以下操作:

语法 描述
s += t 将字符串t追加到s末尾
s + t 将字符串s和t级联
s[n] 从字符串s中索引位置为n处的原始字节
s[n:m] 从位置n到位置m-1处取得的字符(字节)串
s[n:] 从位置n到位置len(s)-1处取得的字符(字节)串
s[:m] 从位置0到位置m-1处取得的字符(字节)串
len(s) 字符串s中的字节数
len([]rune(s)) 字符串s中字符的个数,可以使用更快的方法utf8.RuneCountInString()
[ ]rune(s) 将字符串s转换为一个unicode值组成的串
string(chars) chars类型是[]rune或者[]int32, 将之转换为字符串
[ ]byte(s) 无副本的将字符串s转换为一个原始的字节的切片数组,不保证转换的字节是合法的UTF-8编码字节

指针

通常情况下Go语言中的变量持有相应的值。也就是说,我们可以将一个变量想象成它所持有的值来使用。

其中有些例外:通道、函数、方法、map、切片slice 是引用变量,它们持有的都是引用,也即保存指针的变量。

在go中,值在传递给函数或者方法的时候会被复制一次,对于布尔类型和数值类型来说这so easy,但是对于大型变量却代价很大。而且复制值传参的方式,修改值只是修改了副本,这能保证原始变量不被修改,但也一定程度上增加了修改原始值的麻烦。(和C语言一样的道理,同样go也保留了C语言的指针)

Go语言中有指针,也没什么新鲜东西,同样在每次传递给函数或者方法的只是变量的内存地址,这是非常高效的。而且一个被指针指向的变量可以通过该指针来修改,这就很方便的在函数或者方法中通过指针修改原始变量。

Go语言中的指针操作符也是使用&*操作符。

交换两个变量

func swap1(x, y, p *int) {
    if *x > *y {
        *x, *y = *y, *x
    }

    *p = *x + *y
}

map

类比Java的hashmap,在go中,常用于json解码等。map和Java的哈希表一样,必须申请内存,使用make,否则报错

    var a map[string]int32;
    a["aaaa"] = 111;
    fmt.Printf("%v \n", a); 

执行出错,nil就是null,零值。

panic: assignment to entry in nil map

map和slice不一样,slice可以不make直接用,底层会做处理,但是map不行。

    var a map[string]int32;
    a = make(map[string]int32);
    a["111"] = 111;

    fmt.Printf("%v \n", a); // map[111:111] 

还是使用简洁写法爽,个人也推荐

    a := make(map[string]int32);

流程控制

这个东西几乎每个语言都有,下面看常用的几个

if 条件

package main 

import (
    "fmt"
)

func main() {
    x := 1;
    if x > 1 { // 注意条件表达式没有括号包住()
        fmt.Println(x);
    } else if  x < 1 {
        fmt.Println(x);
    } else {
        fmt.Println(x);
    }
}

for 循环语句,两种用法

1、当做普通的for循环

package main 

import (
    "fmt"
)

func main() {
    var x int;
    sum := 0;

    for x = 1; x <= 10; x++ {
        sum += x;
    }

    fmt.Println(sum);
}

2、当做while循环使用(因为go里没有while关键字)

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;
    sum := 0;

    for x <= 100 {
        sum += x;
        x++;
    }

    fmt.Println(sum);
}

注意

:=操作符用于多个逗号分隔的变量时,如果该变量已经存在,则只是简单的修改它的值。但是当:=操作符位于作用域的起始处时,Go语言会创建一个新的变量,不管该变量之前是否存在,如在if或者for语句中。

a, b, c := 2, 3, 5
for a := 7; a < 8; a++ {
    fmt.Println(a)
}

例子先使用:=声明并赋值了三个变量。for语句处又一次使用:=操作符声明了变量a。而for语句代表了一个新的作用域,所以:=在这里新声明创建了一个变量a,这个变量和之前的变量a是完全不同的两个变量(内存地址不一样)

switch case

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;

    switch x { // 同样没有括号
        case 1:
            fmt.Println(1); // 不需要break,go自身做了优化,默认给加了break语句。但是也可以手动加上。
        case 2:
            fmt.Println(2);
        default:
            fmt.Println(3);
    }
}

如果想让switch语句在case执行后不中断,继续执行接下来的流程,可以使用fallthrough(穿透语句)

package main 

import (
    "fmt"
)

func main() {
    var x int = 1;

    switch x { // 同样没有括号
        case 1:
            fmt.Println(1); // 不需要break,go自身做了优化,默认给加了break语句。但是也可以手动加上。
            fallthrough;
        case 2:
            fmt.Println(2);
        default:
            fmt.Println(3);
    }
} // 打印 1 换行 2

很重要的一个关键字 range

类似Java的迭代器,可以便捷的遍历容器

package main

import (
    "fmt"
)

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

    for i, v := range x {
        fmt.Println(i, v) // i相当于key,v相当于value,和Java的foreach很像
    }

    // 当然,可以不用关心key,只取value,go提供了一个符号 _,代表丢弃值
    for _, v := range x {
        fmt.Println(v)
    }
/*
1
2
3
4
5
*/

    // 还可以遍历字符串,默认打印的是ask码
    str := "abcdefg"
    for i, v := range str {
        fmt.Println(i, v)
    }
/*
0 97
1 98
2 99
3 100
4 101
5 102
6 103
*/

    // 如果想打印字符串
    for _, v := range str {
        fmt.Printf("%c \n", v)
    }
    /*
a
b
c
d
e
f
g
    */
}

另外, "_" 也叫空标示符,它是一个占位符,用于在赋值操作的时候将某个值赋值给空标示符号,从而达到丢弃该值的目的。空标示符不是一个新的变量,因此将它用于:= 操作符号的时候,必须同时为至少另一个值赋值。

时间: 2024-10-10 06:30:12

互联网世界中的C语言——我的golang学习笔记:1(基础语法快速过)的相关文章

C语言学习笔记之基础语法(一)

1.内存寻址由大到小,优先分配内存地址比较大的字节给变量 2.变量有限定义,内存地址就越大 3.输出格式为%p 4.一个变量要先初始化,再使用 #include <stdio.h> int main() { int a=10; int b=20; char c='f'; double d=2.0; float f=3.0f; printf("%p\n",&a); printf("%p\n",&b); printf("%p\n&q

从优酷被劫持流量,揭秘互联网世界中的鸡贼操作

毋庸置疑的是,互联网的出现彻底改变了人类社会进程的节奏.在给人们带来无尽便利的同时,也上演了"黑暗"的一面.除了隐私泄露."人肉"搜索.诈骗等之外,甚至连人们的闲暇时光都彻底被互联网吞噬.此外,互联网世界中的鸡贼操作,也在一次次地刷新我们的"三观". 在利益驱动之下,频繁上演的鸡贼操作正在变得越来越猖狂.如果不着手治理的话,它们就像是大自然中的垃圾.废气一样,将会让整个互联网世界变得"乌烟瘴气".就在近日,优酷上诉一事揭开了互

Go语言学习笔记一(语法篇)

国庆节七天假期,这段时间刚好项目那边催的不是很紧,基本上每天都是白天重构一下项目代码,晚上自己学习.(大概是因为容总那边的人都去度假了把项目进度放慢了吧.这两天“彩虹”姐姐也来凑热闹,据说还是直接从澳门出发,然后漂洋过海到了咱们大广东,昨天还在顺德发了个大脾气.苦逼的咱们几个还是天天待在工作室发霉,因为害怕被彩虹姐姐带走,所以把工作室的门窗锁得紧紧的,然后把空调开到20多度,穿上外套,哈哈,这才叫个爽啊!).最近看到了一篇文章名曰<不要在一门技术上吊死> 深有感触,于是就开始着手学习久闻大名但

Go语言并发与并行学习笔记(一)

转:http://blog.csdn.net/kjfcpua/article/details/18265441 如果不是我对真正并行的线程的追求,就不会认识到Go有多么的迷人. Go语言从语言层面上就支持了并发,这与其他语言大不一样,不像以前我们要用Thread库 来新建线程,还要用线程安全的队列库来共享数据. 以下是我入门的学习笔记. 首先,并行!=并发, 两者是不同的,可以参考:http://concur.rspace.googlecode.com/hg/talk/concur.html G

Python语言学习前提:基础语法

一.变量 1.变量:存储数据.存储数据需要对应内存空间,基于变量的数据类型,解释器会分配指定内存,决定什么数据被存储到内存中. 变量数据类型:整数.小数.字符. 2.变量赋值:变量在使用前必须赋值,赋值的变量才被创建. #!/usr/bin/env python student = 8000 #整数型 miles = 150.0 #浮点型 name = "crystal" #字符串 print student print miles print name 3.多个变量赋值 1)创建变量

思考在伟大的互联网世界中,我是谁?-记录于2016年底

互联网伟大在哪里? 互联网的发明是不是伟大的,这个问题就如同这个世界上许许多多的问题一样,很大程度上取决于人们不同的经历.不同的见识,乃至不同的信念.不同的人生态度. 摘录网上的一段表述:"互联网(产业)更可能被理解为用于改造传统产业及传统社会的广义的"工具",也可以被理解为在自然资源环境与人文社会环境的支撑下存在的一个复杂的"环境"."我个人比较倾向于这个观点,只是互联网与其他更早期的工具不同,互联网本身具有其特殊性.系统性,因此并不能简单地将

Golang学习笔记(3)---go语言变量、常量的定义及赋值

单个变量的赋值与声明 变量的声明格式:var  <变量名称>  <变量类型> 变量的赋值格式:<变量名称> = <表达式> 声明的同时赋值:var <变量名称> [变量类型] = <表达式> <变量名> := <变量值>  --->只能在函数体内使用 多个变量的复制与声明 全局变量的声明可以使用var()的方式简写 全局变量的声明不可以省略var,但可使用并行的方式 所有变量都可以使用类型推断 并行方式的

C语言一维数组初步学习笔记

数组 可以存储一组或者多组数值的变量,里面包含多个元素,数组的每个成员都是一个数组元素. 一维数组 定义:类型 数组名[常量表达式] = {值1, 值2, 值3…}; ? 1 2 3 4 int a[3] = {0, 1, 2}; float f[2] = {1.2, 2.3, 3.14}; char str[] = {'h', 'e', 'l', 'l', 'o'}; chat str1 = "iPhone";//这也是定义字符数组的方法,字符数组后面会详细讲解,这里先了解一下 当数

《r语言实战》菜鸟学习笔记(二)

这一部分将要说明R语言的数据类型以及数据输入方面的内容 因子 R语言中变量可以归结为名义型,有序型和连续变量. 名义型 :没有顺序之分的变量.如 天气 阴晴等 有序型:有顺序关系,但不是数量关系. 心情好 坏 适中等 连续型:就是同是有数量和顺序.当然这里的连续型并不是数学中的连续,也包括离散数据 名义型和有序型在R中称为因子. 下面介绍factor()函数 diabetes <- c("Type1", "Type2", "Type1",