1. 程式人生 > >網際網路世界中的C語言——我的golang學習筆記:1(基礎語法快速過)

網際網路世界中的C語言——我的golang學習筆記:1(基礎語法快速過)

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);
}
View Code

這種方式類似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;
}
View Code

而且,字串資料型別和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) 
}
View Code

資料型別

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
}
View Code

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);    
}
View Code

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 
}
View Code

注意

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);
}
View Code

在看下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 
}
View Code

小結

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
}
View Code

map

類比Java的hashmap,在go中,常用於json解碼等。map和Java的雜湊表一樣,必須申請記憶體,使用make,否則報錯

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

執行出錯,nil就是null,零值。

相關推薦

網際網路世界C語言——golang學習筆記1基礎語法快速

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

Objective-CC語言使用初步學習

最近在進行iOS的深入學習,看過一些部落格後意識到自己的程式碼寫的過於臃腫。我從開始學習到現在的業務熟練都是使用的最基本的MVC模式,於是MVC中的C--controller程式碼十分臃腫,讓接手程式碼的人無從下手。在網上查閱多篇部落格後,才瞭解到有多種設計模式,雖然各有缺點,但是能讓我借鑑到許多方

計算機專業C語言編程學習重點指針化難為易

就是 設計 分布式系 lnp 運行 內存地址 實現 ++ 數據平臺 C語言是面向過程的,而C++是面向對象的 C和C++的區別: C是一個結構化語言,它的重點在於算法和數據結構。C程序的設計首要考慮的是如何通過一個過程,對輸入(或環境條件)進行運算處理得到輸出(或實現過程(

《面向物件程式設計——C++語言描述》學習筆記

第二章 從C到C++  2.1main函式 1.C++中需要寫int main()不能只是main() 2.main()函式裡面最後有一個return 0; 3.main()被啟動程式碼呼叫,而啟動程式碼是編譯器新增到程式中的,是程式和作業系統的橋樑。 4.C++中main()中

C語言動態記憶體學習筆記

一、malloc返回引數有兩種情況 1,當分配的記憶體池是空的時候返回一個NULL指標。 2,當可用記憶體無法滿足要求,malloc向作業系統請求,要求更多記憶體,如果它無法向malloc提供更多記憶體就返回一個NULL指標 二、free的引數 free的引數必須是NULL或mall

C語言學習筆記printf函式詳解

C語言中有關printf()函式的詳細使用方法: 修飾符: - digit(s) :欄位寬度的最小值。如果該欄位不能容納要列印的數或者字串,系統就會使更寬的欄位。 如%4d。 - .digit(s):精度,將結果保留到小數點後的多少位。 - h: 和整數轉

C語言入門教程-學習筆記

變數儲存類別 C語言根據變數的生存週期來劃分,可以分為靜態儲存方式和動態儲存方式。 靜態儲存方式:是指在程式執行期間分配固定的儲存空間的方式。靜態儲存區中存放了在整個程式執行過程中都存在的變數,如全域性變數。 動態儲存方式:是指在程式執行期間根據需要進行動態的分配儲存空

王寶明C語言視訊教程學習筆記--DYA5

malloc與free產生野指標 malloc的記憶體在使用後,程式設計師需要手動釋放記憶體,但是往往會存在下例中重複釋放的情況,從而導致宕機。 void main() {     char *p = NULL;     p = (char *)malloc(100

C語言經典問題學習筆記之二

1. 請你談談引用和指標的區別。 【參考答案】 (1)引用被建立的同時必須被初始化(指標則可以在任何時候被初始化) 。 (2)不能有 NULL 引用,引用必須與合法的儲存單元關聯(指標則可以是NULL) 。 (3)一旦引用被初始化,就不能改變引用的關係(指標則可以隨時改

C語言雙向循環鏈表api源自gluster源碼

void 操作 i+1 null 刪除 strcpy har 定義 判斷 C語言雙向循環鏈表api(源自gluster源碼)基本的操作如增加、刪除和遍歷等 #include <stdio.h> #include <stdlib.h> #includ

c語言實現linux下高危函式system 簡易V1.0版本

system這個函式真的是要慎用,一不小心就會留下漏洞。 下面是用c語言簡易的實現了一下system函式 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<err

井字棋遊戲C語言簡單思路人人對戰版無圖形化介面

#include<stdio.h> #include<stdlib.h> #include<conio.h> #defineROW 3 #defineLINE 3 intsymbol1, symbol2, step; char a[10]; void

C#學習筆記——MDI窗體多文件介面

1、設定父窗體: 如果要將某個窗體設定為父窗體,只需將該窗體的IsMdiContainer屬性設定為True即可。 2、設定子窗體: 通過設為某個窗體的MdiParent屬性來確定該窗體是那個窗體的子窗體。 語法如下: 1: public Form MdiParent

C和指標》學習筆記---資料1

第3章 資料 3.1 常量 ANSI C允許你宣告常量,常量宣告方式和普通變數宣告方式一樣,不同點是常量宣告之後,其值不可以再修改,否則編譯時會報錯。 3.1.1常量宣告方式 常量宣告時使用關鍵字:const進行宣告。例如: int const a = 0; 或者

c語言實現linux下高危函式system 簡易V1.0版本

system這個函式真的是要慎用,一不小心就會留下漏洞。 下面是用c語言簡易的實現了一下system函式 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #inc

C語言程式設計第八章8.13

** 利用矩陣相乘公式程式設計計算mn階矩陣A和nm階矩陣B之積 ** #include<stdio.h> #define ROW 2 #define COL 3 /*函式功能:計算矩陣相乘之積,結果存於二維陣列c中*/ void MultiplyM

Golang學習筆記--log模組

Golang的log模組可謂是語言層面上非常基礎的一層庫,反應的是語言本身的特徵而不是一個服務應該怎樣怎樣。為了方便自己寫應用,我按照自己的需求提出一些log的API並重新實現。和Golang的log模組形成互補。 自己的log模組設計 對於我自己的log模組,暫時不需要

Golang學習筆記--log模組

前一篇文章我們看到了Golang標準庫中log模組的使用,那麼它是如何實現的呢?下面我從log.Logger開始逐步分析其實現。 其原始碼可以參考官方地址 1.Logger結構 首先來看下型別Logger的定義: type Logger struct { mu sync.

Golang學習筆記--log模組

Golang的標準庫提供了log的機制,但是該模組的功能較為簡單(看似簡單,其實他有他的設計思路)。不過比手寫fmt. Printxxx還是強很多的。至少在輸出的位置做了執行緒安全的保護。其官方手冊見Golang log。這裡給出一個簡單使用的例子: package main import (

C語言實現雙向非迴圈連結串列帶頭結點尾結點的基本操作

       我在之前一篇部落格中《C語言實現雙向非迴圈連結串列(不帶頭結點)的基本操作》中詳細實現了不帶頭尾節點的雙向非迴圈連結串列的很多操作。其實同單鏈表一樣,不帶頭結點的連結串列很多操作都是比較麻