【Golang】Golang学习笔记

文章介绍了字符与码值的一一对应关系,ASCII和Unicode字符集的区别,以及Go语言中字符串的双引号和反引号用法。此外,讨论了格式化输入输出的占位符,if...else...的语法规则,fmt.Scanf函数的使用,生成随机数的方法,以及如何通过标签使用break跳出特定循环。同时,文章阐述了Go中对外包访问私有函数的处理,数组和切片的不同,以及Go面向对象编程与C++对比的特点。

1、字符和码值

字符和码值之间有一一对应的关系,即每个字符都对应一个唯一的码值,而每个码值也对应一个唯一的字符。在计算机中,使用数字来表示字符,这个数字就是字符的码值,也称为字符编码或字符集编码。

举个例子,字母"A"在ASCII字符集中的码值是65,这意味着计算机在内存中使用数字65来表示字母"A"。同样,汉字“你”在Unicode字符集中的码值是U+4F60,这意味着计算机在内存中使用16进制数0x4F60(即二进制数0100111101100000)来表示汉字“你”。

当计算机需要将文本显示在屏幕上或者传输到其他计算机时,它会使用相应的字符编码方案将字符的码值转换为二进制码流。接收方可以使用相同的编码方案来解码这个二进制码流并将其转换回字符。这样就可以确保不同的计算机和软件系统之间可以正确地表示和处理文本。

2、ASCII字符集和Unicode字符集

ASCII字符集和Unicode字符集之间有关系,但是它们是两个不同的字符集。ASCII字符集是最初的7位字符编码方案,其中定义了128个字符,包括英文字母、数字和一些特殊字符。ASCII字符集广泛用于计算机和通信领域,它的最大优势在于简单、易于实现和兼容性强。

Unicode字符集则是一种用于表示全球所有符号、字母、数字和标点符号的字符编码方案,其中包含超过137,000个字符。Unicode字符集的设计旨在成为全球通用的字符编码方案,以便不同的计算机系统和软件可以互相通信和显示各种语言和符号。与ASCII字符集相比,Unicode字符集具有更大的容量、更广泛的字符覆盖范围和更好的国际化支持。

Unicode字符集中的一部分字符也被包含在ASCII字符集中,这是因为Unicode字符集的设计者考虑到了ASCII字符集的重要性,并将它作为Unicode字符集的基础部分。ASCII字符集中的字符在Unicode字符集中使用相同的编码方式,因此可以直接转换为Unicode字符集中的编码。

总的来说,Unicode字符集可以看作是ASCII字符集的扩展和超集,它继承了ASCII字符集的部分内容,并添加了更多的字符和功能。

3、Go 字符串 双引号 反引号

双引号字符串是普通的字符串字面量,它支持转义字符和字符串插值,类似于其他编程语言中的字符串表示方式。例如:

fmt.Println("Hello, world!")
fmt.Println("This is a string with a\ttab character.")
fmt.Printf("The value of x is %d.\n", x)

反引号字符串则是原始字符串字面量,它不支持转义字符和字符串插值。反引号字符串可以包含多行文本,而且文本中的空格、制表符、换行符等特殊字符都会被保留。例如:

fmt.Println(`This is a string
with multiple lines
and special characters like\t and \n.`)

下面的代码演示了双引号字符串和反引号字符串输出时的区别:

s := "Hello, \nWorld!"
fmt.Println(s)          // 输出:Hello,
                        //      World!
s = `Hello, \nWorld!`
fmt.Println(s)          // 输出:Hello, \nWorld!

4、占位符

(1)%q是一个格式化字符串占位符,在使用fmt.Printf()fmt.Sprintf()等函数时可以用来将字符串格式化为带有双引号的字符串字面值,并且将其中的非ASCII字符、不可打印字符和控制字符等转义为对应的转义序列。

str := "Hello, 世界"
fmt.Printf("%q\n", str)

输出:

"Hello, 世界"

(2)其他

%d 整型

%f 字符型

%c 字节

%t:用于输出布尔类型的值,输出格式为 true 或 false。

%T:用于输出值的数据类型。

%v:用于输出变量的值,输出格式根据变量的具体类型而定。可以输出地址。

5、if…else… else不能换行

else 语句可以与 if 语句一起使用,也可以独立使用。当 else 语句与 if 语句一起使用时,它必须紧跟在 if 语句块的右括号 } 之后,而且不能单独成行。示例代码如下:

if x > 10 {
    fmt.Println("x is greater than 10")
} else {
    fmt.Println("x is less than or equal to 10")
}

需要注意的是,当 if 语句包含多个条件时,else 子句仅与与其直接相邻的 if 语句匹配。如果要让 else 与 if 语句中的某个条件匹配,可以使用花括号将条件和执行语句括起来。示例代码如下:

if x > 10 {
    fmt.Println("x is greater than 10")
} else if x == 10 {
    fmt.Println("x is equal to 10")
} else {
    fmt.Println("x is less than 10")
}

6、fmt.Scanf(format string, a …interface{}) (n int, err error) 介绍

fmt.Scanf(format string, a ...interface{}) (n int, err error) 是 Go 语言标准库中 fmt 包提供的一个函数,用于从标准输入中读取格式化数据。

下面是对这个函数各个参数的介绍:

  • format string:这是一个格式化字符串,用于指定输入数据的格式。它可以包含普通字符和格式化动词(例如 %d%f%s 等),表示读取不同类型的数据。
  • a ...interface{}:这是一个空接口类型的变量,用于接收读取到的数据。在 format 字符串中,每一个格式化动词都会对应一个参数,因此在函数调用时,必须提供与格式化动词数量相等的参数。
  • (n int, err error):这是函数的返回值,其中 n 表示成功读取的数据个数,err 表示可能出现的错误。如果读取成功,则 err 的值为 nil

下面是一个简单的示例,演示如何使用 fmt.Scanf 函数读取用户输入的姓名和年龄:

goCopy codepackage main

import "fmt"

func main() {
    var name string
    var age int
    fmt.Printf("Please enter your name and age: ")
    n, err := fmt.Scanf("%s %d", &name, &age)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Printf("Hello, %s, you are %d years old.\n", name, age)
        fmt.Printf("Read %d items from input.\n", n)
    }
}

在运行程序时,会提示用户输入姓名和年龄,程序会将这两个值读取到变量 nameage 中,并输出到标准输出。如果读取过程中出现错误,会打印出错误信息。最后,程序还会输出成功读取的数据个数。

7、随机数

在计算机中生成的随机数通常都是伪随机数,也就是通过一个确定的算法产生的看似随机的数列。而这个算法的起点称为随机种子。

在 Go 语言中,使用 math/rand 包中的 Rand 类型生成伪随机数。要让每次程序运行时生成的随机数序列都不同,需要为 Rand 类型提供不同的种子值。一种常见的做法是使用当前时间的纳秒数作为种子值,可以通过 time.Now().UnixNano() 来获取。

以下是一个示例,生成 0 到 100 之间的随机数:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    // 为 rand 类型提供种子
    rand.Seed(time.Now().UnixNano())

    // 生成 0 到 100 之间的随机数
    fmt.Println(rand.Intn(101))
}

每次运行程序时,由于种子值不同,生成的随机数序列也不同。这样做的目的是增加随机性,使得程序生成的随机数更加不可预测。

8、break跳转出不同循环(Continue同理)

//这里演示一下指定标签的形式来使用 break
lable2: 
for i := 0; i < 4; i++ {
	//lable1: // 设置一个标签
	for j := 0; j < 10; j++ {
		if j == 2 {
			//break // break 默认会跳出最近的for循环
			//break lable1 
			break lable2 // j=0 j=1
		}
		fmt.Println("j=", j) 
	}

9、外包访问本包函数方法

如果一个函数的函数名的首字母不是大写的,那么它在 Go 语言中的作用域只限于本包内部。也就是说,其他包无法通过导入本包来直接访问该函数,只能通过导出函数名的方式来调用它。

在 Go 语言中,通过将一个函数名的首字母大写,可以将该函数导出,从而让其他包能够访问到它。被导出的函数可以被其他包直接调用,而不需要通过任何中间层。

举个例子,假设我们有一个名为 myutils 的包,其中包含一个名为 add 的函数,代码如下:

package myutils

func add(a, b int) int {
    return a + b
}

在上面的代码中,函数名 add 的首字母是小写的,因此它只能在本包内部被访问。如果其他包要使用该函数,可以通过将函数名首字母大写的方式将其导出,代码如下:

package myutils

func Add(a, b int) int {
    return a + b
}

现在,其他包就可以通过导入 myutils 包并调用 myutils.Add 函数来使用它了。

为了解决这个问题,我们需要在 mypackage 包中导出一个公开的函数,该函数通过调用 privateFunc 来实现功能。例如:

// mypackage 包
package mypackage

func privateFunc() {
    // 私有函数实现
}

func PublicFunc() {
    privateFunc() // 调用私有函数 privateFunc
}

// otherpackage 包
package otherpackage

import "mypackage"

func main() {
    // 通过公开函数 PublicFunc 来调用 privateFunc
    mypackage.PublicFunc() // 可以正常编译和运行
}

在上面的代码中,我们在 mypackage 包中导出了一个名为 PublicFunc 的公开函数,并在该函数中调用了 privateFunc 函数。在 otherpackage 包中,我们可以通过导入 mypackage 包并调用 mypackage.PublicFunc 函数来使用 privateFunc 函数的功能。这样,我们就成功地在 otherpackage 包中访问了 mypackage 包中的私有函数。

10、数组在函数参数传递时要注意长度

长度是数组类型的一部分,在传递函数参数时需要考虑数组的长度。

11、切片和数组区别

在 Go 中,数组和切片都可以用来表示一系列元素。但是,它们的定义和使用方法有一些区别。

1、定义方式不同:

数组的定义方式如下:

var arr [5]int // 定义一个包含5个整数的数组

切片的定义方式如下:

var slice []int // 定义一个切片

2、大小和容量不同:

数组的大小是固定的,定义时需要指定数组的长度,而且在运行时无法改变数组的大小。比如上面的例子中,arr 的长度是 5,这个长度不能改变。

切片的长度和容量是可以改变的。切片的长度表示切片中元素的个数,而容量表示底层数组中可以容纳的元素个数。切片可以通过 append() 函数来增加元素。比如下面的代码就是创建一个初始长度为0,容量为5的切片,然后通过 append() 函数添加了三个元素:

goCopy codeslice := make([]int, 0, 5)
slice = append(slice, 1, 2, 3)

3、传递方式不同:

当数组作为函数参数传递时,实际上是传递了数组的副本,函数中对数组的修改不会影响原始数组。而切片作为参数传递时,传递的是切片的指针,函数中对切片的修改会影响原始切片。

4、初始化方式不同:

数组可以通过字面量的方式进行初始化,比如:

goCopy code
var arr = [5]int{1, 2, 3, 4, 5}

而切片只能通过 make 函数进行初始化,比如:

goCopy code
var slice = make([]int, 5)

总的来说,数组和切片都是用来表示一系列元素的数据类型,但是它们有着不同的定义和使用方式,需要根据具体的需求来选择使用哪个数据类型。

12、Go面向对象编程与C++比的特点

1、类型系统

在 C++ 中,类型是非常重要的概念,因为它允许你在编译时检查代码。C++ 支持多态,即通过基类指针或引用访问派生类对象,这需要使用虚函数和 RTTI。C++ 还支持模板元编程(template metaprogramming),这是一种在编译时生成代码的技术,可以在一定程度上实现泛型编程。

在 Go 中,类型是一种轻量级的概念,因为它是在运行时动态检查的。Go 支持接口,这是一种描述对象行为的机制,而不是类型本身。Go 中没有继承,但可以通过组合来实现类似的功能。Go 也没有模板元编程,但是有一个基于代码生成的技术,可以使用代码模板来生成重复的代码。

2、内存管理

在 C++ 中,你需要手动管理内存,包括分配和释放对象的内存空间。C++ 有一个 RAII(Resource Acquisition Is Initialization)技术,它允许你使用构造函数和析构函数自动管理内存。C++ 也支持智能指针,它可以自动管理内存并在不再需要时自动释放内存。

在 Go 中,你不需要手动管理内存,因为它具有自动垃圾回收机制。这意味着当你不再需要对象时,Go 会自动释放其内存空间。

3、并发编程

在 C++ 中,你可以使用线程和锁来实现并发编程。C++11 引入了一组标准库,包括原子操作、线程和锁,使并发编程更加容易。

在 Go 中,你可以使用协程(goroutine)和通道(channel)来实现并发编程。协程是一种轻量级的线程,它可以在单个线程中并发执行多个任务。通道是一种用于协程间通信和同步的机制。

4、其他

  • Golang没有类,但是有struct,跟类地位差不多。

与C++语法不同

(1) else { } 不能换行

(2)++、–只能单独使用,没有++i、–i

(3)switch没有break

(4)switch 穿透-fallthrough,可以不断往下执行

(5)Go 没有 while do…whlie 通过for if 组合实现

(6)break可以通过标签选择跳出哪个循环

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值