Chinaunix首页 | 论坛 | 博客
  • 博客访问: 641260
  • 博文数量: 363
  • 博客积分: 110
  • 博客等级: 民兵
  • 技术积分: 1347
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-22 16:07
文章分类

全部博文(363)

文章存档

2018年(83)

2016年(1)

2014年(2)

2013年(34)

2012年(236)

2011年(7)

分类: Erlang

2018-01-30 08:47:07

原文地址:Golang之语言基础 作者:scq2099yt

        Go语言是一门类C语言的编译型语言,这样对于第一语言为C/C++语言的同学们来说,这就是福利。Go语言简洁易懂,本文会概要介绍其基础知识。

一、Hello, World
        按照惯例,介绍语法前都会先来一发Hello, World,在此也不能免俗,具体代码如下:
        package main
        import "fmt"
        func main(){
            fmt.Printf("Hello, World\n")
        }
        编译、链接、运行后输出销魂的Hello, World。不会这个的同学,请参考我的前一篇关于Go的拙文《Golang之环境配置》。
        在Go语言中,程序是通过package来组织的。package 标识当前文件所属的包,比如:前述程序第1行package main表明本文件属于man包,包名则表示其是一个可独立运行的包,在编译后会生成可执行文件。通常除了main包外,其它的包最后都会生成*.a文件即包文件,并放置在$GOPATH/pkg/$GOOS_$GOARCH目录下,以如博文《Golang之环境配置》中环境变量设置为例就是在C:\go\pkg\windows_386目录下。
        Go语言中的包类似于Python中的module,这个可以参考我的Python系列博文之《Python之模块》,它们的优点在于程序的模块化和可重用性。包名和包所在的文件夹名可以不相同,包名通过package 声明而非文件夹名。
        每个可独立运行的Go语言程序,必须包含一个package main,而包main中必须包含一个入口函数main,这与C语言保持一致,而不同的是在Go语言中,main函数既没有参数,也没有返回值。在程序的第3行,用关键字func定义了main函数,如C语言一样main函数体放在{}中。Go语言中关键字共25个,具体如下表:
                                                            

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


        为了输出Hello, World,应用了函数Printf,其功能同C语言中的printf,该函数来自于fmt包,因此,在第2行中导入了fmt包,即import "fmt"。在调用包中函数时,采用.的方式调用,即fmt.Printf,这一点类似于Python。
        Go语言原生支持UTF-8,因此,可以用Printf函数输出任何非ASCII码字符,即任何字符都可以直接输出,甚至可以用UTF-8中的任何字符作为标识符,这点在Web编程中算得上是如鱼得水。

二、语言基础
        Go语言之所以简洁,是因为它有一些默认的行为:
        (1)大写字母开头的变量是可导出的,即其它包可以读取,是公有变量;小写字母开头的不可导出,是私有变量。
        (2)大写字母开头的函数也一样,相当于class中带public关键词的公有函数;小写字母开头的就是private关键词的私有函数。
        Go程序在设计时遵循这些原则。
1、变量
        Go语言定义变量方式有很多种,具体如下:
        (1)使用var关键字。
        使用var关键字是Go语言最基本的变量定义方式,Go语言把变量类型放在变量名后面,这点不同于C语言,具体如下:
        var variableName type        //variableName为变量名,type为变量类型
        var vname1, vname2, vname3 type        //定义多个变量
        var variableName type = value                //定义变量并初始化,value为初始化值
        var vname1, vname2, vname3 type= v1, v2, v3        //定义多个变量并初始化
        var vname1, vname2, vname3 = v1, v2, v3                //简化定义多个变量并初始化
        一般脚本语言都可以根据变量值来推导出对应的类型,因此,可以更加简化如下:
        vname1, vname2, vname3 := v1, v2, v3                    //根据值推导类型即简单声明
        简单声明只能用在函数内部,即只能用来定义局部变量;使用var才能在函数外部定义变量,即定义全局变量。
        _即下划线是一个特殊变量,任何赋予它的值都会被丢弃,就像黑洞一样,比如:
        _, b := 34, 35        //将值35赋给了b,同时丢弃34
        在Go语言中,已声明的变量必须使用,否则编译时会报错。 
2、常量
        在Go语言中,常量可定义为数值、布尔值或字符串等类型,具体语法如下:
        const constantName = value        
        const Pi float32 = 3.1415926        //也可以明确指定常量类型,类似于C++中的const
3、内置基础类型
        (1)Boolean
        在Go语言中,布尔值得类型为bool,值为true或false,默认为false。
        var isActive bool
        var enabled, disabled = true, false
        (2)数值类型
                (A)整数
                整数类型有无符号和有符号两种,分别是对应int和uint,这两种类型的精度与编译器相关。Go语言也内置了位数固定的类型:rune, int8, in16, int32, int64和byte, uint8, uint16, uint32, uint64,其中,rune是int32的别称,byte是uint8的别称,有了这些后跨平台时就不需要单独在定义一面了,非常人性化。需要注意的是,这些类型的变量之间不允许互相赋值,否则编译时会报错,即int和int32、int8和int32之间都不可互用。
                (B)浮点数
                浮点数类型有float32和float64两种,但没有float类型,默认是float64。
                (C)复数
                复数形式为RE+IMi,其中,RE为实数部分,IM为虚数部分,i是虚数单位,默认类型是complex128,即64位实数+64位虚数。也有complex64,即32位实数+32位虚数。
                var c complex64 = 5+5i
                fmt.Printf("Value is: %v", c)        //输出(5+5i)
        (3)字符串
        在Go语言中,字符串都采用UTF-8字符集编码,用双引号""或者反引号``括起来定义,其类型为string。
        var emptyString string = ""
        在Go语言中字符串是不可变的,否则编译时会报错:
        var s string = "hello"
        s[0] = 'c'
        如果需要这么做,需要将字符串s转换为[]byte类型后,在按照数组赋值,然后转换回string:
        s := "hello"
        c := []byte(s)
        c[0] = 'c'
        s2 := string(c)
        在Go语言中,可以使用+操作符连接两个字符串:
        s := "hello "
        m := "world"
        a := s + m
4、一些技巧
         (1)分组声明
         在Go语言中,可以采用分组的方式同时声明多个常量、变量或者导入多个包:
         import "fmt"
         import "os"
 
        const i = 100
        const pi = 3.1415
 
        var i int
        var ppi float32
        可以分组写出如下形式:
        import(
          "fmt"
          "os"
        )
 
        const(
          i = 100
          pi = 3.1415
        )
 
        var(
          i int
          pi float32
        )
        除非被显示设置初值或者iota,则每个const分组的第一常量被默认设置为0值,第二及后续常量与第一个常量值相同,如果前面那个常量的值是itoa,则它也被设置为iota。
        (2)iota枚举
        Go语言里关键字iota在声明enum时使用,默认起始值是0,每调用一次加1,但每遇到一个const关键字,则iota会重置为0:
        const(
          x = iota //x==0
          y = iota //y==1
          z   //z==2
        )
        const v = iota //v==0
        (3)array
        array即数组,其定义方式如下:
        var arr [n]type  //n表示数组长度,type表示数组元素类型
        数组操作方式同其它语言,通过[]读取或者赋值数组元素,数组下标从0开始,数组长度不可变:
        var arr [10]int   //声明长度为10,元素类型为int的数组
        arr[0] = 20    //数组元素赋值,下标从0开始
        a := [3]int{1, 2, 3} //声明长度为3的int数组,元素依次初始化为1,2,3
        b := [10]int{1, 2, 3} //声明长度为10的int数组,前3个元素依次初始化为1,2,3,其它默认为0
        c := [...]int{4, 5, 6} //可以用...省略长度,Go语言会自动根据元素个数计算长度
        Go语言支持嵌套数组即多维数组,以二维数组为例:
        doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} //声明一个二维数组,每个以2个数组为元素,每个数组又有4个int型元素
        easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}  //如果内部元素和外部一样,可以直接忽略内部元素类型
        数组之间的赋值是值得赋值,即当把一个数组作为参数传入函数的时候,传入的是该数组的副本,而不是指针,如果要使用指针,则需要用到slice类型。
        (4)slice
        有时候我们并不知道数组多大合适,这就需要“动态数组”了,在Go语言中这个任务由slice来完成,但是slice并不是真正意义上的动态数组,而是一个引用类型,slice总是指向一个底层array,sliced的声明方式与array类似,只是不需要长度:
        var fslice []int  //声明一个slice与数组类似,但不需要指定长度
        slice := []byte{'a', 'b', 'c', 'd'}  //声明并初始化slice元素
        slice还可以从一个数组或slice中再次声明,slice通过array[i:j]来获取,其中i是数组的开始位置,j是结束位置,但不包含array[j],其长度为j-i:
        var ar = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} // 声明一个含有8个byte元素的数组
        var a, b []byte  //声明2个含所有byte的slice
        a = ar[2:5]  //a指向数组的第3个元素开始,并到第5个元素结束,即a含有ar[2]、ar[3]和ar[4]
        b = ar[3:5]  //b含有ar[3]和ar[4]
        slice有一些简便操作:
        (A)slice的默认开始位置为0,ar[:n]等价于ar[0:n]。
        (B)slice的第2个序列默认是数组的长度,ar[n:]等价于ar[n:len(ar)]。
        (C)如果从一个数组里直接获取slice,可以这样ar[:],等价于ar[0:len(ar)],因为默认第一个序列是0,第二个是数组的长度。
        slice是引用类型,所以当被引用元素的值改变时,其它的所有引用都会改变该值。
        从概念上来说slice像一个结构体,这个结构体包含三元素:
        (A)一个指针,指向数组中slice指定的开始位置。
        (B)长度,即slice的长度。
        (C)最大长度,也就是slice开始位置到数组的最后位置的长度。
        slice有几个有用的内置函数:
        (A)len获取slice的长度。
        (B)cap获取slice的最大容量。
        (C)append向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice。
        (D)copy函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数。
        需要注意的是:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。但当slice中没有剩余空间时即cap==len,此时将动态分配新的数组空间。返回的slice数组指针指向这个空间,而原数组的内容将保持不变,其它引用此数组的slice则不受影响。
         (5)map
        map类似于Python中的字典,其格式为map[keyType]valueType。
        map的读取和设置与slice类似,都是通过key来操作,不同的是slice的index只能是int类型,而map只要完全定义了==与!=操作的类型都支持。
        声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前用make初始化:
        var numbers map[string] int
        numbers := make(map[string]int)
        numbers["one"] = 1
        numbers["ten"] = 10
        numbers["three"] = 3
        fmt.Println("第三个数字是", numbers["three"])         //打印出:第三个数字是3
        使用map时需要注意如下几点:
            (A)map是无序的,不能通过index获取,而必须通过key获取。
            (B)map的长度是不固定的,即和slice一样,也是一种引用类型。
            (C)内置的len函数同样适用于map,即返回map拥有的key的数量。
            (D)map的值可以通过key来修改。比如:numbers["one"]=11。
        map的初始化也可以通过key:val来完成,并内置有判断是否存在key的方式,通过delete删除map中的元素:
        rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2}        //初始化map
        csharpRating, ok := rating["C#"]        //map返回有两个值,第一个值返回ok,即如果key不存在,则ok为false,否则为true,第二个返回value值
        delete(rating, "C")                //删除key为C的元素
        由于map也是一种引用类型,那么如果两个map同时指向一个底层,则一个改变,另一个随之也会改变。
        (6)make/new操作
        make用于内建类型的内存分配,包括map、slice和channel。
        new用于各种类型的内存分配。Go语言中的new同C++一样,即new(T)分配T类型的存储空间并初始化为零值,同时返回一个指向其地址的指针。
        make(T, args)只能创建map、slice和channe并且返回一个非零初始值的T类型而不是指针l。之所以会返回一个非零值,是因为这三个数据类型指向的都是数据结构的引用,因此使用前必须被初始化,make初始化了该数据结构并填充了适当的值。
        所谓零值,并非是空值,而是一种变量未填充前的默认值,通常为0,但bool是false,string是“”。
        






阅读(637) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~