mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 01:21:38 +08:00
54
eBook/04.8.md
Normal file
54
eBook/04.8.md
Normal file
@@ -0,0 +1,54 @@
|
||||
#4.8 时间和日期
|
||||
|
||||
时间包给我们一个数据类型类型**time.Time** (当一个值使用) 以及显示和测量时间和日期的功能函数。
|
||||
|
||||
当前的时间可以使用**time.Now()**,想得到时间的一部分,可以使用 t.Day(),t.Minute()等等;你也可以使用自己的时间格式化如: **fmt.Printf("%02d.%02d.%4d\n”, t.Day(), t.Month(), t.Year())** // 例如:21.07.2011
|
||||
|
||||
Duration类型表示连续时间的两个时刻的纳秒数。Location类型映射了那个时区的时间,UTC表示通用协调世界时。
|
||||
|
||||
有一个预定义的函数`func (t Time) Format(layout string) string` 可以根据一个格式字符串,将一个时间 t 格式化为一个字符串,格式使用预定义的格式如 `time.ANSIC` 或 `time.RFC822`。
|
||||
|
||||
一般的格式化,定义了标准的时间格式,它将被用于描述被格式化的时间格式;这看来很奇怪,但是例子可以使他更清晰:
|
||||
|
||||
fmt.Println(t.Format(“02 Jan 2006 15:04”)) // outputs now: 21 Jul 2011 10:31
|
||||
|
||||
(参见程序 time.go,更多信息在 http://golang.org/pkg/time/)
|
||||
|
||||
Listing 4.20—time.go :
|
||||
|
||||
package main
|
||||
import (
|
||||
“fmt”
|
||||
“time”
|
||||
)
|
||||
|
||||
var week time.Duration
|
||||
func main() {
|
||||
t := time.Now()
|
||||
fmt.Println(t) // e.g. Wed Dec 21 09:52:14 +0100 RST 2011
|
||||
fmt.Printf(“%02d.%02d.%4d\n”, t.Day(), t.Month(), t.Year())
|
||||
// 21.12.2011
|
||||
t = time.Now().UTC()
|
||||
fmt.Println(t) // Wed Dec 21 08:52:14 +0000 UTC 2011
|
||||
fmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011
|
||||
// calculating times:
|
||||
week = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec
|
||||
week_from_now := t.Add(week)
|
||||
fmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011
|
||||
// formatting times:
|
||||
fmt.Println(t.Format(time.RFC822)) // 21 Dec 11 0852 UTC
|
||||
fmt.Println(t.Format(time.ANSIC)) // Wed Dec 21 08:56:34 2011
|
||||
fmt.Println(t.Format(“02 Jan 2006 15:04”)) // 21 Dec 2011 08:52
|
||||
s := t.Format(“20060102”)
|
||||
fmt.Println(t, “=>”, s)
|
||||
// Wed Dec 21 08:52:14 +0000 UTC 2011 => 20111221
|
||||
}
|
||||
|
||||
输出在每行 `//` 的后面。
|
||||
|
||||
如果你需要让应用程序中经过一定时间或定期发生一些事情(事件处理的一个特例)`time.After` `time.Ticker` 是你所需要的:§14.5将讨论这些有趣的事情。还有一个的功能 `time.Sleep(Duration d)`,它会暂停当前进程(在goroutine中,参见§14.1)一个持续时间 d。
|
||||
|
||||
##链接
|
||||
- [目录](directory.md)
|
||||
- 上一节:[strings 和 strconv 包](04.7.md)
|
||||
- 下一节:[指针](04.9.md)
|
125
eBook/04.9.md
Normal file
125
eBook/04.9.md
Normal file
@@ -0,0 +1,125 @@
|
||||
#4.9 指针
|
||||
|
||||
不像 Java 和 .NET,Go 语言为程序员提供了控制数据结构的指针;但是,你不能进行指针计算。通过给程序员控制基本内存布局,Go 语言允许你控制特定集合的数据结构,分配的数量,和内存访问模式,这些对构建性能良好的系统是非常重要的:如果你想要做的系统编程,操作系统和网络,指针对性能表现是非常重要且不可缺少的。
|
||||
|
||||
由于指针对当代面向对象编程的程序员是未知的,我们将在这里解释他们,并在接下来的章节中继续深入。
|
||||
|
||||
程序在内存中存储它的值,每个内存块(或词)有一个地址,通常用十六进制数表示,如 0x6b0820 或 0xf84001d7f0
|
||||
|
||||
Go 语言的地址操作符是 &,放到一个变量前,会返回我们这个变量的内存地址。
|
||||
|
||||
如下的代码片段(参见 Listing 4.9 pointer.go)输入如下例:“An integer: 5, its location in memory: 0x6b0820(这个值随着你每次运行程序而变化)
|
||||
|
||||
var i1 = 5
|
||||
fmt.Printf(“An integer: %d, it’s location in memory: %p\n”, i1, &i1)
|
||||
|
||||
这个地址可以存储在一个叫指针的特殊数据类型中,在本例中这是一个指向 int 的指针,这里是 i1:这里使用 *int 表示。如果我们想调用指针 intP,我们可以这样声明它:
|
||||
|
||||
var intP *int
|
||||
|
||||
然后,下面这样写是正确的:intP = &i1, intP 指向 i1。
|
||||
|
||||
(因为它声明了一个指针以 %p 这个格式化字符串表示)
|
||||
|
||||
intP 存储了 i1 的内存地址;它指向了 i1 的位置,它引用了变量 i1。
|
||||
|
||||
一个指针变量可以包含另一个值得内存地址:它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。当然,可以声明指针指向任何类型的值,表明它的原始性或结构性; 在类型的值卡面加 * 号(前缀),这里的 * 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。
|
||||
|
||||
一个新的被定义的指针,当没有分配给它变量时,他的值是 nil 。
|
||||
|
||||
一个指针变量通常缩写为 ptr 。
|
||||
|
||||
!!在一个表达式如 var p *type 总是在 * 号和指针名间留有一个空间 - var p*type 是语法正确的,但是在更复杂的表达式中,它容易被误认为是一个乘法表达式!!
|
||||
|
||||
符号 * 可以放在一个指针前,如 *intP,那么它将得到这个指针指向地址的值;它被称为反引用(或者 contents 或者间接引用)操作符;另一种说法是指针转移。
|
||||
|
||||
对于多个变量 var, 如下是正确的: var == *(&var)
|
||||
|
||||
现在,我们能理解整个的pointer.go程序和他的输出:
|
||||
|
||||
Listing 4.21—[pointer.go](examples/chapter_4/pointer.go):
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
var i1 = 5
|
||||
fmt.Printf("An integer: %d, its location in memory: %p\n", i1, &i1)
|
||||
var intP *int
|
||||
intP = &i1
|
||||
fmt.Printf("The value at memory location %p is %d\n", intP, *intP)
|
||||
}
|
||||
|
||||
输出:
|
||||
|
||||
An integer: 5, its location in memory: 0x24f0820
|
||||
The value at memory location 0x24f0820 is 5
|
||||
|
||||
我们可以表示内存的使用情况如下:
|
||||
|
||||

|
||||
|
||||
string_pointer.go 个我们展示了指针对string的例子。
|
||||
|
||||
它展示了分配一个新的值给 *p 并且更改这个变量自己的值(这里是一个字符串)
|
||||
|
||||
Listing 4.22—[string_pointer.go](examples/chapter_4/string_pointer.go):
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
func main() {
|
||||
s := "good bye"
|
||||
var p *string = &s
|
||||
*p = "ciao"
|
||||
fmt.Printf("Here is the pointer p: %p\n", p) // prints address
|
||||
fmt.Printf("Here is the string *p: %s\n", *p) // prints string
|
||||
fmt.Printf("Here is the string s: %s\n", s) // prints same string
|
||||
}
|
||||
|
||||
输出:
|
||||
|
||||
Here is the pointer p: 0x2540820
|
||||
Here is the string *p: ciao
|
||||
Here is the string s: ciao
|
||||
|
||||
通过对 *p 赋另一个值来更改“对象”, s 也会更改。
|
||||
|
||||
内存示意图如下:
|
||||
|
||||

|
||||
|
||||
注意:你不能得到一个文字或常量的地址,如下代码片段:
|
||||
|
||||
const i = 5
|
||||
ptr := &i //error: cannot take the address of i
|
||||
ptr2 := &10 //error: cannot take the address of 10
|
||||
|
||||
所以 Go 语言,和其他低级(系统)语言,如 C, C++ 和 D 语言一样,有指针的概念。但是对指针进行计算(称为指针运算,例如 pointer + 2, 移动指向字符串的字节数,或指向一个数组的位置),这样做在 C 语言里经常发生内存访问错误导致程序崩溃,为了语言的内存安全,在 Go 语言中是不允许的。Go 语言的指针更像引用,如 Java, C# 和 VB.NET 。
|
||||
|
||||
所以: c = *p++ 在 Go 语言的代码中是不允许的。
|
||||
|
||||
一个指针的高级应用是你可以传递一个引用到一个变量(如一个函数的参数),这样不会传递变量的拷贝。指针传递是很廉价的,只有 4 个或 8 个字节。当程序在工作中需要占用大量的内存,或很多变量,或者两者都有,使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中,至少使用一个变量指向他们,所以从它们被创建开始,他们的周期范围是独立的。
|
||||
|
||||
另一方面(虽然不太可能),因为一个指针导致的间接引用(一个进程执行了另一个地址),频繁使用会导致性能下降。
|
||||
|
||||
指针也可以指向另一个指针,并且这种可以任意深度的嵌套,导致你可以有多级的间接引用,但在大多数情况这会使你的代码结构不清晰。
|
||||
|
||||
如我们所见,在大多数情况下 Go 语言可以是程序员轻松创建指针,并且隐藏间接引用,如,自动反向引用。
|
||||
|
||||
对一个空指针反向引用,如下面的 2 行(参见程序 testcrash.go),是不合法的,并且会使程序崩溃:
|
||||
|
||||
Listing 4.23 [testcrash.go](examples/chapter_4/testcrash.go):
|
||||
|
||||
package main
|
||||
func main() {
|
||||
var p *int = nil
|
||||
*p = 0
|
||||
}
|
||||
// in Windows: stops only with: <exit code="-1073741819" msg="process crashed"/>
|
||||
// runtime error: invalid memory address or nil pointer dereference
|
||||
|
||||
问题 4.2:列举 Go 语言中使用 * 号的所有用法
|
||||
|
||||
##链接
|
||||
- [目录](directory.md)
|
||||
- 上一节:[时间和日期](04.8.md)
|
||||
- 下一节:[控制结构](05.0.md)
|
BIN
eBook/images/4.4.9_fig4.4.png
Normal file
BIN
eBook/images/4.4.9_fig4.4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
eBook/images/4.4.9_fig4.5.png
Normal file
BIN
eBook/images/4.4.9_fig4.5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Reference in New Issue
Block a user