demo:
package main
import "fmt" // 注释
//注释
func main() {
fmt.Printf("Hello World\n")
}
执行:
go run demo.go
编译成可执行文件
go build demo.go
func main() {
var a int
var b string = "aaaa"
var (
c int
d bool
)
conse i = 10
e := 15
a = 1
b = "hello"
c = 2
d = false
f,g:=12,13
if (a + c + e < 100 && d) {
fmt.Printf("Hello World\n")
} else {
fmt.Printf(b)
}
}
func main() {
var s string ="aaaaaa"
sb:=[]byte(s)
s1:=string(sb)
fmt.Printf("%c\n",sb[0])
fmt.Printf("%s\n",s1)
fmt.Printf("%s\n",s)
}
直接访问字符串的下标是不可以的,需要先转换为byte类型,通过string函数转换回来。
其他操作符
Precedence Operator(s)
Highest :
* / % << >> & &^
+ - | ^
== != < <= > >=
<-
&&
Lowest
||
func main() {
if a:=1;a<0{
fmt.Printf("a is less 0")
}else{
fmt.Printf("a is bigger 0")
}
}
<= >=
for {} 类似C的for(;;) 死循环
golang中没有do和while
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("i:%d\n", i)
}
j := 0
for j < 10 {
fmt.Printf("j:%d\n", j)
j++
}
k := 0
for {
fmt.Printf("k:%d\n", k)
k++
if k > 9 {
break
}
}
}
l := []string{"a", "b", "c"}
for k, v := range l {
fmt.Printf("pos:%d,value:%s\n", k, v)
}
func main() {
i := 0
switch i {
case -1, 0:
fmt.Printf("is -1 or 0")
case 1:
fmt.Printf("is 1")
default:
fmt.Printf("default")
}
}
Table 2.3. Go 中的预定义函数
close new panic complex
closed make recover real
len append print imag
cap copy println
array就是Python的数组
map就是Python的字典
array使用[n]定义,其中n是数组的大小,type是元素的类型。n是可选的。
数组的定义和使用。
l1:=[]string{"a","b"}
var l2 [2]int
l2[0]=1
l2[1]=2
var l3 [2][3]int
l3[0][0]=1
print(l1[0])
print(l2[0])
print(l3[0][0])
当传递一个array给函数的时候,函数得到的是一个array的副本,即传值。
slice和array类似,不同的是slice是array的一个指针,所以修改slice,是会影响array的,而且传递一个slice给函数的时候,传递的是指针,所以是传址。
l1:=[]string{"a","b","c","d"}
s1:=l1[1:2]
s2:=l1[:] //类似l1[0:4]
s3:=l1[:2] //类似l1[0:2]
print(s1[0])
print(s2[0])
print(s3[0])
append用户向切片中添加元素,返回新的切片,新的切片的内存地址可能和之前的不一样。
l1:=[]string{"a","b","c","d"}
s2:=append(l1,"e","f")
print(s2[4])
print(s2[5])
map的定义:map[<from type>]<to type>
var map1 = make(map[string]int)
map2:=map[string]int{
"k1":11,"k2":12,
}
print(map2)
map1["k1"] = 12
v, ok := map1["k1"] //12 true
print(v, ok,"\n")
v1, ok1 := map1["k2"] //0 false
print(v1, ok1,"\n")
//map1["k2"] = 0, false //删除,不知道为什么测试失败
遍历:
map2:=map[string]int{
"k1":11,"k2":12,
}
for k,v:=range map2{
print(k,v,"\n")
}
func (p mytype) funcname(q int) (r,s int) { return 0,0 }
DEMO:
func add(a int,b int) (int,int,int){
return a+b,a,b
}
func main() {
a,b,c:=add(12,13)
print(a,b,c)
}
函数的参数都是传值的形式。
func add(a int,b int) (int){
sum:=a+b
return sum
}
func add(a int,b int) (sum int){
sum=a+b
return
}
在定义函数的返回类型的时候,加上类型对应的变量名,然后在函数体中,return后面不带参数,这样go就会找到函数体中的变量sum,然后返回。注意,由于定义函数的时候已经定义了sum变量,所以后面修改的时候不需要加冒号。
例如在打开文件的时候,每一次返回都需要关闭文件描述符,这样会有大量代码重复,在go中,可以定义函数退出前执行的函数。
func test(a int) (sum int){
defer print("test done")
if a<0{
return -1
}else{
return 1
}
}
这样无论a是大于还是小于0,都会输出文字。
类似于Python的*args
func test(args ...int) (int) {
for i, v := range args {
print(i, v, "\n")
}
return 1
}
func main() {
test(1, 2, 3, 4, 5)
}
类似于Python的lambda
add_one := func(i int) int {
return 1 + i
}
print(add_one(2))
func test(i int, fun func(int) int) (int) {
i++
return fun(i)
}
func main() {
add_one := func(i int) int {
return 1 + i
}
print(test(2,add_one))
}
最后的值是4,
go中没有异常的处理,只有恐慌和恢复
func thrownPanic(fun func() int) (b bool) {
defer func() {
if r := recover(); r != nil {
b = true
}
}()
fun()
return
}
func main() {
add_one := func() int {
a := []int{1, 2, 3}
print(a[0])
return 1
}
print(thrownPanic(add_one))
}
在thrownPanic中,会调用fun,然后在函数结束前执行defer的函数,如果fun中产生了异常,r会为非nil,这样返回true,否则返回false
这样外层的函数就能知道调用fun是否产生了异常。
"runtime/debug"
"reflect"
"fmt"
)
func test_func(){
defer func(){
if err:=recover();err!=nil{
fmt.Println("Panic ",err,reflect.TypeOf(err))
debug.PrintStack()
}
}()
list:=[]int{1}
println(list[1])
}
func main(){
test_func()
程序在执行println(list[1])
的时候,会产生恐慌,也就是异常,但是程序不会立刻退出,还会执行defer的函数,这时,通过revocer函数,可以catch住这个异常,然后把异常信息打印出来,这样程序可以继续正常运行,其实跟try exept差不多。
目录结构
/
test.go
/util
util1.go
util2.go
util1.go:
package util
func add(a int, b int) int {
//私有函数,只能在包内被调用
return a + b
}
func Add(a int, b int) int {
//公有函数,可以在其他包中调用
return a + b
}
test.go
package main
import "./util" // 注释
//注释
func main() {
print(util.Add(12,13))
}
有多个地方用到util这个名字:
其中1,2,3需要一样,4可以不一样
在包中,变量或者函数名,根据首字母是否大写来判断该变量或函数是否公有的
在一个包中,也就是文件夹,不同的文件中的变量或函数名不能重复。
import u "./util" // 注释
//注释
func main() {
print(u.Add(12,13))
}
导入util并设置别名为u
package main
import . "./util" // 注释
//注释
func main() {
print(Add(12,13))
}
别名设置为点,就不需要名字了
上面的导入方法是相对路径导入,即在util前面加上./
还有绝对路径的导入
import "shorturl/model" //加载GOROOT/src/shorturl/model模块
每个包都应该包含文档,如果包中有多个文件,文档可以在任意一个。格式:
/*
The regexp package implements a simple library for
regular expressions.
The syntax of the regular expressions accepted is:
regexp:
concatenation ‘|‘ concatenation
*/
package regexp
在util目录下面创建文件util_test.go:
package util
import "testing"
func TestAdd(t *testing.T){
if Add(12,13)!=24 {
t.Log("test Add 12 13 fail")
t.Fail()
}
}
然后cd到util目录,执行go test
,这样go就会调用所有*_test.go
文件里面的Test*
函数。在函数里面,如果测试失败,就调用t.Fail()
标准的Go 代码库中包含了大量的包,并且在安装Go 的时候多数会伴随一起安装。浏
览$GOROOT/src/pkg 目录并且查看那些包会非常有启发。无法对每个包就加以解说,不过下
面的这些值得讨论:b
go中也有指针,但是和C有区别,不能进行指针运算。
var p *int; //p=nil
//*p = 8; //这样会报错,因为p还没有分配内存
var i int;
p = &i; //令p的值等于i的内存值
*p = 8; //相当于修改i的值
print(p, &i, i)//看到,p和*i是一样的,i=8
go中有两种方法可以分配内存:new和make
new是声明一个变量,返回变量的指针。
var p1 *int; //p=nil
p2 := new(int)
var p3 int
print(p1,"\n")
print(*p2,"\n")
print(p3,"\n")
p1是一个指针,但是它还没有初始化
p2也是一个指针,它已经初始化了,初始化值为0
p3是一个变量,已经初始化,初始化值为0
make只能声明slice,map和channel。返回值,而不是指针。
也可以使用new来声明指针,然后使用make来初始化:
p2 := new([]int)
//(*p2)[0]=1 //这样是不行的,因为p2还没有初始化
*p2=make([]int,11)
(*p2)[0]=1
print((*p2)[0])
make([]int,11)
声明了一个长度为11的切片slice
go的结构和C的结构类似,然后使用new来定义
type Person struct {
name string
age int
}
p1:=new(Person)
p1.name="kevin"
p1.age=23
println(p1.name)
println(p1.age)
定义结构的方法:
type Person struct {
name string
age int
}
func (p *Person) SetAge(v int) int {
p.age = v
return v
}
func main() {
p1 := new(Person)
p1.SetAge(12)
println(p1.age)
}
go中一个很重要的概念是goroutine,协程的英文是coroutine,第一个字母不同,即goroutine类似于协程,但是又有所不同,是go特殊的概念。
goroutine的特点:
一个并发的DEMO:
package main
import "time"
//注释
func f(name string) {
for i := 0; i < 10; i++ {
println(name, i)
time.Sleep(1 * 1e9)
}
}
func main() {
go f("f 1")
go f("f 2")
time.Sleep(15 * 1e9)
}
类似于线程,然后启动的方法也比较方便,只需要在前面加一个go
的关键字。
1e9是一个内部的常量,是秒的意思
goroutine之间通过channel来通讯,channel类似于队列,即Python中的Queue。
定义channel的时候,需要指定channel接受的类型,可以为int,string,和interface等。
var c chan int; //定义一个接受int类型的channel
c = make(chan int)
c<-1//向c中put一个对象
item:=<- c //从c中取一个对象
所以上面的并发程序可以改为:
func f(name string) {
for i := 0; i < 10; i++ {
println(name, add(i))
time.Sleep(1 * 1e9)
}
c <- 1//向c中put一个对象
}
func main() {
c = make(chan int)
go f("f 1")
go f("f 2")
<-c
<-c
}
在goroutine中,可以put,也可以get。
上面的channel是无缓冲的,也就是put完之后,goroutine就会阻塞,直到有goroutine取走。
定义有缓冲的channel:
ch:=make(chan int,1)
1就是缓冲的数量
获取的时候,也是阻塞的,可以使用非阻塞的方法:
v,ok:=<-c
如果有值,ok为true,否则为false
尽管是叫并发,但是同一时刻,只有一个goroutine在执行,也就是占用CPU,类似Python的线程和协程。
可以通过runtime.GOMAXPROCS(n)
来设置同一个时刻运行的goroutine的数量,也可以修改环境变量GOMAXPROCS。
读取文件
import "os"
func main() {
buf := make([]byte, 1024)
f ,_:= os.Open("./test.data")
defer f.Close()
for {
n, _ := f.Read(buf)
if n == 0 {
break
}
os.stdout.write(buf[0:n]) //必须使用write,如果使用println,会输出切片的内存地址
}
}
通过bufio读取文件
import "os"
import "bufio"
//注释
func main() {
buf := make([]byte, 1024)
f, _ := os.Open("/etc/passwd")
defer f.Close()
r := bufio.NewReader(f)
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
for {
n, _ := r.Read(buf)
if n == 0 { break }
w.Write(buf[0:n])
}
}
创建目录
if _, e := os.Stat("name"); e != nil {
os.Mkdir("name", 0755)
} else {
// error
}
import "os"
import "flag"
import "fmt"
//注释
func main() {
dnssec := flag.Bool("dnssec", false, "Request DNSSEC records")
port := flag.String("port", "53", "Set the query port")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] [name ...]\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
println(*dnssec,*port)
}
go的特点就是高性能和高并发
测试用例:
从1加到1000,执行一百万次,计算需要的时间。
使用linux的time命令来进行计时。
sum.go
package main
func sum(num int)int{
sum:=0
for i:=1;i<=num;i++{
sum+=i
}
return sum
}
func main(){
sum_:=0
for j:=0;j<1000000;j++{
sum_+=sum(1000)
}
println(sum_)
println(sum(1000))
}
编译:
go build sum.go
执行
time ./sum
结果:
real 0m0.464s
user 0m0.460s
sys 0m0.001s
sum.c
#include <stdio.h>
long int sum(int num){
long int sum=0;
int i =0;
for( i=1;i<=num;i++){
sum=sum+i;
};
return sum;
}
int main(){
int i ;
long int sum_=0;
for (i=0;i<1000000;i++){
sum_+=sum(1000);
}
printf("%ld\n",sum(1000));
printf("%ld\n",sum_);
// printf(sum_);
return 0;
};
编译:
gcc sum.c -fPIC -shared -o sum.so
执行
time ./sumc
结果:
real 0m2.874s
user 0m2.856s
sys 0m0.000s
test_sum.py
def sum(num):
s = 0
for i in xrange(1,num+1):
s += i
return s
def main():
sum_=0
for i in range(1000000):
sum_+=sum(1000)
print sum_
if __name__ == ‘__main__‘:
main()
执行
time python test_sum.py
结果
real 0m35.146s
user 0m34.814s
sys 0m0.125s
from ctypes import cdll
c_lib = cdll.LoadLibrary(‘./sum.so‘)
if __name__ == ‘__main__‘:
c_lib.main()
执行
time python test_sum_c.py
结果
real 0m2.899s
user 0m2.874s
sys 0m0.006s
语言|go|C|Python|Python调用c
---|
用时:|0.464s|2.874s|35.146s|2.899s
原文:https://www.cnblogs.com/rxbook/p/10638179.html