(3). GO嵌入式开发之 --- 基本程序结构

前言

        本章节开始,通过不断实践和试错来对GO语言加快学习、加深了解;为了便于时间和试错,我们先来学习如何在GO语言中编写测试程序:

1. 源码文件以_test结尾:xxx_test.go
2. 测试方法名以Test开头:func TestXXX(t testing.T){ ... }

package try_test

import "testing"

func TestForTyrTest(t *testing.T) {
    t.Log("Hi Go try test")
}

保存(ctrl+s)后,将自动于控制台(Output)输出:

=== RUN TestForTyrTest 
TestForTyrTest: try_test.go:6: Hi Go try test 
--- PASS: TestForTyrTest (0.00s) 
PASS

备注:IDE使用的Atom,需先安装go-plus插件:
确认安装了go-plus插件
Atom->preferences->packages
搜索找到go-plus
在settings中,Test配置中选中 Run with verbose flag setting

如果在命令行里运行测试文件,需要执行命令,才能输出 t.Log 里的文字:
go test -v xxx_test.go

变量

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

变量声明

对于变量声明,典型的有两种:

  1. 使用 var 关键字声明:
    var name = xxxxxx
    其中,“xxxxxx”既可以是个数值,也可以是表达式执行后的返回结果;可以给name赋值整形、浮点数及字符串等。
var name = "zhangsan"
var age = 28
  1. 使用短变量声明:
name := "zhangsan"
age := 28

这两种方式各有千秋,有着各自的特点和适用场景。前者可以被用在任何地方,而后者只能被用在函数或者其他更小的代码块中。

在这里,你可能会有个错觉,认为GO语言是弱类型语言;
其实不是的,变量的初始化时省略变量的类型而由系统自动推断,也就是Go 语言中的类型推断功能;类型推断是一种编程语言在编译期自动解释表达式类型的能力。

Go 语言是静态类型的,所以一旦在初始化变量时确定了它的类型,之后就不可能再改变。这就避免了在后面维护程序时的一些问题。

Go 语言的类型推断可以明显提升程序的灵活性,使得代码重构变得更加容易,同时又不会给代码的维护带来额外负担(实际上,它恰恰可以避免散弹式的代码修改),更不会损失程序的运行效率。

变量赋值
  • 赋值时可以进行自动类型推断
  • 在一个赋值语句中可以对多个变量进行同时赋值
a, b, c := 5, 7, "abc"  //一个赋值语句中对多个变量进行赋值

/*交换两个变量的值*/
func TestExChange(t *testing.T){
/*
    //常规写法:
    a := 1
    b := 2
    tmp := a
    a = b
    b = tmp
    t.Log(a,b)
*/

   //改进写法
    a := 1
    b := 2
    a, b = b, a //一个赋值语句中对多个变量进行赋值
    t.Log(a,b)
}

常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量的声明:
const c_name1, c_name2 = value1, value2

package try_test

import "testing"

/// 测试常量一般声明
func TestNormalConstant(t *testing.T) {
    //!普通常量定义
    const LENGTH = 256
    //!多重赋值
    const i, b, str = 28, false, "string"

    t.Log("LENGTH = ", LENGTH, ";i = ", i, ";b = ", b, ";str = ", str)
    /*
       === RUN TestNormalConstant 
      TestNormalConstant: try_test.go:12: LENGTH = 256 ;i = 28 ;b = false ;str = string 
      --- PASS: TestNormalConstant (0.00s)   
   */
}

///测试常量作为枚举使用
func TestConstantUseOfEnum(t *testing.T) {
    //!作为枚举使用
    const (
        Jan = iota + 1
        Feb
        Mar
        Apr
        May
        Jun
        Jul
        Aug
        Sep
        Oct
        Nov
        Dec
    )

    t.Log("Jan =", Jan, "; Feb =", Feb, "; Mar =", Mar, "...", "Dec = ", Dec)
    /*
      === RUN TestConstantUseOfEnum 
      TestConstantUseOfEnum: try_test.go:33: Jan = 1 ; Feb = 2 ; Mar = 3 ... Dec = 12 
      --- PASS: TestConstantUseOfEnum (0.00s)
   */
}

///测试常量作为连续位使用
func TestConstUseOfBinary(t *testing.T) {
    //!二进制位表示可读,可写,可执行
    const (
        Readable = 1 << iota
        Writable
        Excutable
    )

    t.Log("Readable = ", Readable, ";Writeable = ", Writable, ";Excutable = ", Excutable)
    /*
      === RUN TestConstUseOfBinary 
      TestConstUseOfBinary: try_test.go:45: Readable = 1 ;Writeable = 2 ;Excutable = 4
      --- PASS: TestConstUseOfBinary (0.00s)
   */
}

其中,iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

数据类型

  • GO语言不支持隐式类型转换;
  • 别名和原有类型也不能进行隐式类型转换;
符号 描述
bool 布尔值
string 字符串
int int8 int16 int32 int64 有符号整形
uint uint8 uint16 uint32 uint64 无符号整形
uintptr 无符号整型,用于存放一个指针
byte 类似 uint8
rune 类似 int32 ,represent a Unicode code point
float32 float64 32/64位浮点型数
complex64 complex128 32/64 位实数和虚数
Go 语言支持指针,但不支持指针运算
package type_test

import "testing"

/// 类型的预定义值(math的package中可找到这些值)
//! math.MaxInt64
//! math.MaxFloat64
//! math.MaxUint32

/// string 是值类型,其默认的初始化值为空字符串,而不是nil

func TestPoint(t *testing.T) {
    var arry = [5]int{1, 2, 3, 4, 5}
    ptr := &arry[1]
    t.Log(*ptr)
    t.Logf("%T %T", arry[1], ptr)
  /*
  === RUN   TestPoint
      TestPoint: type_test.go:14: 2
      TestPoint: type_test.go:15: int *int
  --- PASS: TestPoint (0.00s)
  */
    //ptr += 1 //报错:invalid operation: ptr += 1 (mismatched types *int and int)
}


运算符

算术运算符
运算符 描述 实例
+ 相加 A + B输出结果 30
- 相减 A - B 输出结果 -10
* 相乘 A * B 输出结果 200
/ 相除 B / A 输出结果 2
% 求余 B % A 输出结果 0
++ 自增 A++ 输出结果 11
-- 自减 A-- 输出结果 9

GO语言没有前置的++,--

比较运算符
运算符 描述 实例
== 检查两个值是否相等,如果相等返回 True 否则返回 False。 (A == B) 为 False
!= 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 (A != B) 为 True
> 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 (A > B) 为 False
< 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 (A < B) 为 True
>= 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 (A >= B) 为 False
<= 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 (A <= B) 为 True

用==比较数组
在很多主流语言中,数组是个引用类型,不是值类型;所以在相比较的时候是比较数组的引用而不是里面的值;
这点在GO上是完全不同的:

  • 相同维数且含有相同个数元素的数组才可以进行比较
  • 每个元素都相同才相等
逻辑运算符

下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False

运算符 描述 实例
&& 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 (A && B) 为 False
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 (A ||B) 为 True
! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 !(A && B) 为 True
位运算符

&、|、^、<<、>>与主流编程语言没有区别

&^:按位置零
1 &^ 0 --- 1
1 &^ 1 --- 0
0 &^ 1 --- 0
0 &^ 0 --- 0
如果右边的操作数位上为1,不管左边操作数为多少,结果都为0
如果右边的操作数位上为0,那么其左边操作数为什么,结果就是什么

const (
  Readable = 1< true, true, true

  a = a&^Excutable //去掉可执行权限
  t.Log(a&Readable == Readable, a&Writable == Writable, a&Excutable == Excutable) 
// ---> true, true, false
}
运算符优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。
下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:

优先级 运算符
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||

条件和循环

循环

GO仅支持循环关键字for

for i:=0; i<5; i++ {
}

///代码示例 
package main

import "fmt"

/*while 条件循环*/
 //while(n<5)
  n := 5
  for n<5 {
      n++
      fmt.Print(n)
  }

  /*while 无限循环*/
   ///while(true)
   for true  {
        fmt.Printf("这是无限循环。\n");
    }

条件语句

if 语句:

  1. condition表达式结果必须为布尔值
  2. 支持变量赋值
    if var declaration; condition {
            //code to be executed if condition is true
    }
package condition_test

import "testing"

func TestIfMultiSec(t *testing.T) {
     if a:=1 == 1; a {
        t.Log("1==1")  /// 输出:1==1   
    }
}

由于Go方法支持多返回值,因此特性为编程带来了便捷;在后面的学习章节中在详细介绍,这里先简单抛出个例子:

package condition_test

import "testing"

func TestIfMultiSec(t *testing.T) {
     if ret, err:=function; err==nil {
        ......   
    }else{
       ......
    }
}

switch语句:

  1. 条件表达式不限制为常量或者表达式
  2. 单个case中,可以出现多个结果选项,使用逗号分隔
  3. 与C语言等规则相反,Go语言不需要用break来明确退出一个case
  4. 可以不设定switch之后的条件表达式,在这种情况下,整个switch结构与多个if ... else ... 的逻辑作用相同

代码示例1:

package condition_test

func TestSwitchMultiCase(t *testing.T) {
     for i :=0; i<5; i++ {
         switch i {
           case 0,2:
              t.Log("Event")
           case 1,3:
              t.Log("Odd")
           default:
              t.Log("it is not 0-3")
         }  
     }
}

代码示例2:

package condition_test

func TestSwitchMultiCase(t *testing.T) {
     for i :=0; i<5; i++ {
         switch  {
           case i%2==0:
              t.Log("Event")
           case i%2==1:
              t.Log("Odd")
           default:
              t.Log("it is not 0-3")
         }  
     }
}

你可能感兴趣的:((3). GO嵌入式开发之 --- 基本程序结构)