golang中使用指针_了解Go中的指针

news/2024/7/7 4:10:12

golang中使用指针

介绍 (Introduction)

When you write software in Go you’ll be writing functions and methods. You pass data to these functions as arguments. Sometimes, the function needs a local copy of the data, and you want the original to remain unchanged. For example, if you’re a bank, and you have a function that shows the user the changes to their balance depending on the savings plan they choose, you don’t want to change the customer’s actual balance before they choose a plan; you just want to use it in calculations. This is called passing by value, because you’re sending the value of the variable to the function, but not the variable itself.

在Go中编写软件时,您将在编写函数和方法。 您将数据作为参数传递给这些函数。 有时,该功能需要数据的本地副本,而您希望原始数据保持不变。 例如,如果您是一家银行,并且具有一个功能,可以根据用户选择的储蓄计划向用户显示其余额的更改,那么您就不想在选择计划之前更改客户的实际余额; 您只想在计算中使用它。 这称为按值传递 ,因为您是将变量的值发送给函数,而不是变量本身。

Other times, you may want the function to be able to alter the data in the original variable. For instance, when the bank customer makes a deposit to their account, you want the deposit function to be able to access the actual balance, not a copy. In this case, you don’t need to send the actual data to the function; you just need to tell the function where the data is located in memory. A data type called a pointer holds the memory address of the data, but not the data itself. The memory address tells the function where to find the data, but not the value of the data. You can pass the pointer to the function instead of the data, and the function can then alter the original variable in place. This is called passing by reference, because the value of the variable isn’t passed to the function, just its location.

其他时候,您可能希望函数能够更改原始变量中的数据。 例如,当银行客户向他们的帐户存款时,您希望存款功能能够访问实际余额,而不是副本。 在这种情况下,您无需将实际数据发送到该函数。 您只需要告诉函数数据在内存中的位置即可。 称为指针的数据类型保存数据的内存地址,但不保存数据本身。 内存地址告诉函数在哪里可以找到数据,但不能告诉数据值。 您可以将指针传递给函数而不是数据,然后函数可以在适当位置更改原始变量。 这称为按引用传递 ,因为变量的值不会传递给函数,而只是传递给函数。

In this article, you will create and use pointers to share access to the memory space for a variable.

在本文中,您将创建并使用指针来共享对变量的内存空间的访问。

定义和使用指针 (Defining and Using Pointers)

When you use a pointer to a variable, there are a couple of different syntax elements that you need to understand. The first one is the use of the ampersand (&). If you place an ampersand in front of a variable name, you are stating that you want to get the address, or a pointer to that variable. The second syntax element is the use of the asterisk (*) or dereferencing operator. When you declare a pointer variable, you follow the variable name with the type of the variable that the pointer points to, prefixed with an *, like this:

当使用指向变量的指针时,需要理解几个不同的语法元素。 第一个是使用&符( & )。 如果在变量名称前放置一个&符号,则说明您要获取地址或指向该变量的指针。 第二个语法元素是使用星号( * )或取消引用运算符。 声明指针变量时,请在变量名后加上指针指向的变量的类型,并以*开头,如下所示:

var myPointer *int32 = &someint

This creates myPointer as a pointer to an int32 variable, and initializes the pointer with the address of someint. The pointer doesn’t actually contain an int32, just the address of one.

这将创建myPointer作为指向int32变量的指针,并使用someint的地址初始化该指针。 指针实际上并不包含int32 ,而仅包含一个地址。

Let’s take a look at a pointer to a string. The following code declares both a value of a string, and a pointer to a string:

让我们看一下一个指向string的指针。 以下代码声明了字符串的值和指向字符串的指针:

main.go
main.go
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)
}

Run the program with the following command:

使用以下命令运行程序:

  • go run main.go

    去运行main.go

When you run the program, it will print out the value of the variable, as well as the address of where the variable is stored (the pointer address). The memory address is a hexadecimal number, and not meant to be human-readable. In practice, you’ll probably never output a memory address to look at it. We’re showing you for illustrative purposes. Because each program is created in its own memory space when it is run, the value of the pointer will be different each time you run it, and will be different than the output shown here:

运行该程序时,它将打印出变量的值以及变量存储的地址(指针地址)。 内存地址是十六进制数,并不意味着人类可以读取。 实际上,您可能永远不会输出内存地址来查看它。 我们向您展示是出于说明目的。 因为每个程序在运行时都是在其自己的内存空间中创建的,所以每次运行时指针的值都将不同,并且将与此处显示的输出不同:


   
Output
creature = shark pointer = 0xc0000721e0

The first variable we defined we named creature, and set it equal to a string with the value of shark. We then created another variable named pointer. This time, we set the value of the pointer variable to the address of the creature variable. We store the address of a value in a variable by using the ampersand (&) symbol. This means that the pointer variable is storing the address of the creature variable, not the actual value.

我们定义的第一个变量我们命名为creature ,并将其设置为等于具有shark值的string 。 然后,我们创建了另一个名为pointer变量。 这次,我们将pointer变量的值设置为creature变量的地址。 我们通过使用符号(在一个变量中的值的地址存储& )符号。 这意味着pointer变量存储的是creature变量的地址 ,而不是实际值。

This is why when we printed out the value of pointer, we received the value of 0xc0000721e0, which is the address of where the creature variable is currently stored in computer memory.

这就是为什么当我们打印出pointer的值时,我们收到的值为0xc0000721e0 ,这是当前creature变量在计算机内存中存储的地址。

If you want to print out the value of the variable being pointed at from the pointer variable, you need to dereference that variable. The following code uses the * operator to dereference the pointer variable and retrieve its value:

如果要打印出从pointer变量pointer变量的值,则需要取消引用该变量。 以下代码使用*运算符取消引用pointer变量并检索其值:

main.go
main.go
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)
}

If you run this code, you’ll see the following output:

如果运行此代码,您将看到以下输出:


   
Output
creature = shark pointer = 0xc000010200 *pointer = shark

The last line we added now dereferences the pointer variable, and prints out the value that is stored at that address.

现在,我们添加的最后一行取消引用pointer变量,并打印出存储在该地址的值。

If you want to modify the value stored at the pointer variable’s location, you can use the dereference operator as well:

如果要修改存储在pointer变量位置的值,则也可以使用解引用运算符:

main.go
main.go
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)

    *pointer = "jellyfish"
    fmt.Println("*pointer =", *pointer)
}

Run this code to see the output:

运行以下代码以查看输出:


   
Output
creature = shark pointer = 0xc000094040 *pointer = shark *pointer = jellyfish

We set the value the pointer variable refers to by using the asterisk (*) in front of the variable name, and then providing a new value of jellyfish. As you can see, when we print the dereferenced value, it is now set to jellyfish.

我们通过在变量名称前面使用星号( * )设置pointer变量所引用的值,然后提供新的jellyfish值。 如您所见,当我们打印取消引用的值时,现在将其设置为jellyfish

You may not have realized it, but we actually changed the value of the creature variable as well. This is because the pointer variable is actually pointing at the creature variable’s address. This means that if we change the value pointed at from the pointer variable, we also change the value of the creature variable.

您可能没有意识到,但实际上我们也更改了creature变量的值。 这是因为pointer变量实际上指向creature变量的地址。 这意味着,如果我们更改pointer变量所pointer值,那么我们还将更改creature变量的值。

main.go
main.go
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)

    *pointer = "jellyfish"
    fmt.Println("*pointer =", *pointer)

    fmt.Println("creature =", creature)
}

The output looks like this:

输出如下所示:


   
Output
creature = shark pointer = 0xc000010200 *pointer = shark *pointer = jellyfish creature = jellyfish

Although this code illustrates how a pointer works, this is not the typical way in which you would use pointers in Go. It is more common to use them when defining function arguments and return values, or using them when defining methods on custom types. Let’s look at how you would use pointers with functions to share access to a variable.

尽管此代码说明了指针的工作方式,但这并不是在Go中使用指针的典型方式。 在定义函数参数和返回值时使用它们,或者在自定义类型上定义方法时使用它们更为常见。 让我们看看如何将指针与函数一起使用以共享对变量的访问。

Again, keep in mind that we are printing the value of pointer to illustrate that it is a pointer. In practice, you wouldn’t use the value of a pointer, other than to reference the underlying value to retrieve or update that value.

同样,请记住,我们正在打印pointer的值以说明它是一个指针。 实际上,除了引用基础值来检索或更新该值外,您不会使用指针的值。

功能指针接收器 (Function Pointer Receivers)

When you write a function, you can define arguments to be passed ether by value, or by reference. Passing by value means that a copy of that value is sent to the function, and any changes to that argument within that function only effect that variable within that function, and not where it was passed from. However, if you pass by reference, meaning you pass a pointer to that argument, you can change the value from within the function, and also change the value of the original variable that was passed in. You can read more about how to define functions in our

编写函数时,可以定义要通过引用传递的参数。 按传递表示将该的副本发送到函数,并且对该函数内该参数的任何更改影响该函数内的该变量,而不影响它从何处传递。 但是,如果您通过引用传递,这意味着您将指针传递给该参数,则可以在函数内更改值,还可以更改传入的原始变量的值。您可以阅读有关如何定义函数的更多信息。在我们的

Deciding when to pass a pointer as opposed when to send a value is all about knowing if you want the value to change or not. If you don’t want the value to change, send it as a value. If you want the function you are passing your variable to be able to change it, then you would pass it as a pointer.

确定何时传递指针而不是何时传递值是关于知道是否要更改值的全部。 如果您不希望值更改,则将其作为值发送。 如果希望函数传递变量能够对其进行更改,则可以将其作为指针传递。

To see the difference, let’s first look at a function that is passing in an argument by value:

为了了解区别,让我们首先看一下一个按value传递参数的value

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

The output looks like this:

输出如下:


   
Output
1) {Species:shark} 2) {Species:jellyfish} 3) {Species:shark}

First we created a custom type named Creature. It has one field named Species, which is a string. In the main function, we created an instance of our new type named creature and set the Species field to shark. We then printed out the variable to show the current value stored within the creature variable.

首先,我们创建了一个名为Creature的自定义类型。 它有一个名为Species字段,它是一个字符串。 在main函数中,我们创建了一个新类型的实例,名为creature ,并将Species字段设置为shark 。 然后,我们打印出变量以显示存储在creature变量中的当前值。

Next, we call changeCreature and pass in a copy of the creature variable.

接下来,我们调用changeCreature并传入creature变量的副本。

The function changeCreature is defined as taking one argument named creature, and it is of type Creature that we defined earlier. We then change the value of the Species field to jellyfish and print it out. Notice that within the changeCreature function, the value of Species is now jellyfish, and it prints out 2) {Species:jellyfish}. This is because we are allowed to change the value within our function scope.

函数changeCreature被定义为接受一个名为creature参数,并且它是我们先前定义的Creature类型。 然后,将“ Species字段的值更改为jellyfish并打印出来。 请注意,在changeCreature函数中,“ Species的值现在为jellyfish ,并打印出2) {Species:jellyfish} 。 这是因为允许我们在函数范围内更改值。

However, when the last line of the main function prints the value of creature, the value of Species is still shark. The reason that the value didn’t change is because we passed the variable by value. This means that a copy of the value was created in memory, and passed to the changeCreature function. This allows us to have a function that can make changes to any arguments passed in as needed, but will not affect any of those variables outside of the function.

然而,当最后一行main功能打印的价值creature ,价值Species仍然是shark 。 值不变的原因是因为我们按传递了变量。 这意味着在内存中创建了该值的副本,并将其传递给changeCreature函数。 这使我们可以使用一个函数,该函数可以根据需要更改传入的任何参数,但不会影响该函数外部的任何变量。

Next, let’s change the changeCreature function to take an argument by reference. We can do this by changing the type from creature to a pointer by using the asterisk (*) operator. Instead of passing a creature, we’re now passing a pointer to a creature, or a *creature. In the previous example, creature is a struct that has a Species value of shark. *creature is a pointer, not a struct, so its value is a memory location, and that’s what we pass to changeCreature().

接下来,让我们将changeCreature函数更改为通过reference接受参数。 我们可以通过使用星号( * )运算符将类型从creature更改为指针来实现。 现在不传递creature ,而是传递指向creature*creature的指针。 在前面的例子中, creature是一种struct ,其具有一Species的值shark*creature是一个指针,而不是一个结构,因此它的值是一个内存位置,这就是我们传递给changeCreature()

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(&creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

Run this code to see the following output:

运行此代码以查看以下输出:


   
Output
1) {Species:shark} 2) &{Species:jellyfish} 3) {Species:jellyfish}

Notice that now when we change the value of Species to jellyfish in the changeCreature function, it changes the original value defined in the main function as well. This is because we passed the creature variable by reference, which allows access to the original value and can change it as needed.

请注意,现在当我们在changeCreature函数changeCreature Species的值更改为jellyfish时,它也会同时更改main函数中定义的原始值。 这是因为我们通过reference传递了creature变量,该变量允许访问原始值并可以根据需要对其进行更改。

Therefore, if you want a function to be able to change a value, you need to pass it by reference. To pass by reference, you pass the pointer to the variable, and not the variable itself.

因此,如果希望函数能够更改值,则需要通过引用传递它。 要通过引用传递,请将指针传递给变量,而不是变量本身。

However, sometimes you may not have an actual value defined for a pointer. In those cases, it is possible to have a panic in the program. Let’s look at how that happens and how to plan for that potential problem.

但是,有时您可能没有为指针定义实际值。 在这种情况下,程序可能会出现恐慌 。 让我们看看这是如何发生的,以及如何计划该潜在问题。

零指针 (Nil Pointers)

All variables in Go have a zero value. This is true even for a pointer. If you declare a pointer to a type, but assign no value, the zero value will be nil. nil is a way to say that “nothing has been initialized” for the variable.

Go中的所有变量的值为零 。 即使对于指针也是如此。 如果您声明一个指向类型的指针,但未分配任何值,则零值将为nilnil是表示变量“尚未初始化”的一种方式。

In the following program, we are defining a pointer to a Creature type, but we are never instantiating that actual instance of a Creature and assigning the address of it to the creature pointer variable. The value will be nil and we can’t reference any of the fields or methods that would be defined on the Creature type:

在下面的程序中,我们定义了一个指向Creature类型的指针,但是我们从未实例化该Creature实际实例并将其地址分配给creature指针变量。 该值将为nil并且我们无法引用在Creature类型上定义的任何字段或方法:

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

The output looks like this:

输出如下所示:


   
Output
1) <nil> panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x109ac86] goroutine 1 [running]: main.changeCreature(0x0) /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:18 +0x26 main.main() /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:13 +0x98 exit status 2

When we run the program, it printed out the value of the creature variable, and the value is <nil>. We then call the changeCreature function, and when that function tries to set the value of the Species field, it panics. This is because there is no instance of the variable actually created. Because of this, the program has no where to actually store the value, so the program panics.

当我们运行该程序时,它会打印出creature变量的值,该值为<nil> 。 然后,我们调用changeCreature函数,当该函数尝试设置Species字段的值时,它会恐慌 。 这是因为没有实际创建的变量的实例。 因此,程序没有实际存储值的位置,因此程序感到恐慌。

It is common in Go that if you are receiving an argument as a pointer, you check to see if it was nil or not before performing any operations on it to prevent the program from panicking.

在Go语言中,通常会收到一个参数作为指针,然后在对其执行任何操作之前检查它是否为nil,以防止程序崩溃。

This is a common approach for checking for nil:

这是检查nil的常用方法:

if someVariable == nil {
    // print an error or return from the method or fuction
}

Effectively you want to make sure you don’t have a nil pointer that was passed into your function or method. If you do, you’ll likely just want to return, or return an error to show that an invalid argument was passed to the function or method. The following code demonstrates checking for nil:

实际上,您想确保没有传递给函数或方法的nil指针。 如果这样做,您可能只想返回,或者返回一个错误,以表明将无效的参数传递给了函数或方法。 以下代码演示了nil检查:

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    if creature == nil {
        fmt.Println("creature is nil")
        return
    }

    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

We added a check in the changeCreature to see if the value of the creature argument was nil. If it was, we print out “creature is nil”, and return out of the function. Otherwise, we continue and change the value of the Species field. If we run the program, we will now get the following output:

我们在changeCreature添加了一个检查,以查看creature参数的值是否为nil 。 如果是,则打印出“ creature is nil”,然后返回该函数。 否则,我们继续并更改“ Species字段的值。 如果运行该程序,现在将获得以下输出:


   
Output
1) <nil> creature is nil 3) <nil>

Notice that while we still had a nil value for the creature variable, we are no longer panicking because we are checking for that scenario.

请注意,尽管我们仍然对creature变量使用nil值,但我们不再感到恐慌,因为我们正在检查该场景。

Finally, if we create an instance of the Creature type and assign it to the creature variable, the program will now change the value as expected:

最后,如果我们创建Creature类型的实例并将其分配给creature变量,则程序现在将按预期更改值:

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature
    creature = &Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    if creature == nil {
        fmt.Println("creature is nil")
        return
    }

    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

Now that we have an instance of the Creature type, the program will run and we will get the following expected output:

现在,我们有了Creature类型的实例,程序将运行,并且将获得以下预期输出:


   
Output
1) &{Species:shark} 2) &{Species:jellyfish} 3) &{Species:jellyfish}

When you are working with pointers, there is a potential for the program to panic. To avoid panicking, you should check to see if a pointer value is nil prior to trying to access any of the fields or methods defined on it.

当您使用指针时,程序可能会出现紧急情况。 为避免恐慌,在尝试访问在其上定义的任何字段或方法之前,应检查指针值是否为nil

Next, let’s look at how using pointers and values affects defining methods on a type.

接下来,让我们看看使用指针和值如何影响在类型上定义方法。

方法指针接收器 (Method Pointer Receivers)

A receiver in go is the argument that is defined in a method declaration. Take a look at the following code:

go中的接收者是方法声明中定义的参数。 看下面的代码:

type Creature struct {
    Species string
}

func (c Creature) String() string {
    return c.Species
}

The receiver in this method is c Creature. It is stating that the instance of c is of type Creature and you will reference that type via that instance variable.

这种方法的接收者是c Creature 。 声明c的实例是Creature类型的,您将通过该实例变量引用该类型。

Just like the behavior of functions is different based on whether you send in an argument as a pointer or a value, methods also have different behavior. The big difference is that if you define a method with a value receiver, you are not able to make changes to the instance of that type that the method was defined on.

就像函数的行为根据您将参数作为指针还是值发送而有所不同一样,方法也具有不同的行为。 最大的区别是,如果使用值接收器定义方法,则无法更改定义该方法的类型的实例。

There will be times that you would like your method to be able to update the instance of the variable that you are using. To allow for this, you would want to make the receiver a pointer.

有时候,您希望您的方法能够更新您正在使用的变量的实例。 为此,您需要使接收器成为指针。

Let’s add a Reset method to our Creature type that will set the Species field to an empty string:

让我们向Creature类型添加一个Reset方法,该方法会将Species字段设置为一个空字符串:

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func (c Creature) Reset() {
    c.Species = ""
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    creature.Reset()
    fmt.Printf("2) %+v\n", creature)
}

If we run the program, we will get the following output:

如果运行程序,将得到以下输出:


   
Output
1) {Species:shark} 2) {Species:shark}

Notice that even though in the Reset method we set the value of Species to an empty string, that when we print out the value of our creature variable in the main function, the value is still set to shark. This is because we defined the Reset method has having a value receiver. This means that the method will only have access to a copy of the creature variable.

注意,即使在Reset方法中,将Species的值设置为空字符串,当在main函数中打印出creature变量的值时,该值仍设置为shark 。 这是因为我们定义的Reset方法具有一个value接收器。 这意味着该方法将只能访问该creature变量的副本

If we want to be able to modify the instance of the creature variable in the methods, we need to define them as having a pointer receiver:

如果我们希望能够在方法中修改creature变量的实例,则需要将它们定义为具有pointer接收器:

main.go
main.go
package main

import "fmt"

type Creature struct {
    Species string
}

func (c *Creature) Reset() {
    c.Species = ""
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    creature.Reset()
    fmt.Printf("2) %+v\n", creature)
}

Notice that we now added an asterisk (*) in front of the Creature type in when we defined the Reset method. This means that the instance of Creature that is passed to the Reset method is now a pointer, and as such when we make changes it will affect the original instance of that variables.

请注意,当我们定义Reset方法时,我们现在在Creature类型的前面添加了一个星号( * )。 这意味着传递给Reset方法的Creature实例现在是一个指针,因此,当我们进行更改时,它将影响该变量的原始实例。


   
Output
1) {Species:shark} 2) {Species:}

The Reset method has now changed the value of the Species field.

现在,“ Reset方法已更改“ Species字段的值。

结论 (Conclusion)

Defining a function or method as a pass by value or pass by reference will affect what parts of your program are able to make changes to other parts. Controlling when that variable can be changed will allow you to write more robust and predictable software. Now that you have learned about pointers, you can see how they are used in interfaces as well.

将函数或方法定义为按传递或按引用传递将影响程序的哪些部分能够对其他部分进行更改。 控制何时可以更改该变量将使您能够编写更健壮和可预测的软件。 现在,您已经了解了指针,您还可以看到它们如何在接口中使用。

翻译自: https://www.digitalocean.com/community/conceptual_articles/understanding-pointers-in-go

golang中使用指针


http://www.niftyadmin.cn/n/3649717.html

相关文章

RSS和社会性书签Chicklet创建器

“RSS and Social Bookmarking Chicklet Creator”&#xff0c;从名称上好像看不出来是做什么的&#xff0c;进去之后&#xff0c;你就明白了&#xff0c;那摸多的web 2.0参与者们&#xff0c;你们不正需要这套工具吗&#xff1f;它可以一次性帮你解决订阅按钮问题&#xff1a;…

命令行curl上传文件_命令行基础知识:使用cURL下载文件

命令行curl上传文件Client URL, or simple cURL is a library and command-line utility for transferring data between systems. It supports a myriad of different protocols and tends to be installed by default on many Unix-like operating systems. Because of it’s…

认识JSON Web令牌(JWT)

什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准&#xff08;(RFC 7519).该token被设计为紧凑且安全的&#xff0c;特别适用于分布式站点的单点登录&#xff08;SSO&#xff09;场景。JWT的声明一般被用来在身份提供者和服务提…

盘点我这三年

2003年&#xff1a;2003年伊始&#xff0c;就双喜临门&#xff0c;1月1日被正式任命为部门技术总监&#xff0c;同天搬入新家。这一时期必须要感谢田总邓总的悉心栽培和信任。2003年12月底&#xff0c;自己的blogcn网志“跟随大象的舞步”被当时刚刚成立一周年的中国博客网评选…

canvas动画:黑客帝国_使用Canvas API进行动画处理-第2部分:基本碰撞

canvas动画:黑客帝国In Part 1 of this series we went over the basics of rendering reusable objects to the canvas, using our GUI for more intuitive controls, and creating the illusion of basic movement with our animation loop. In this part we’ll get comfort…

体验Windows Live Mail Beta?

Webleon的《立即体验Windows Live Mail Beta》和Joey的《Live Mail beta初体验》&#xff0c;都写了如何升级hotmail到Windows Live Mail Beta来体验微软的最新战略。我倒是这么做了&#xff0c;先是只得到了25MB->250MB的容量升级&#xff0c;没见到界面改变为神奇的“信息…

Spring安全框架——jwt的集成(服务器端)

新建用户表——users&#xff0c;并完成数据库增删查改 一、新建表 二、持久化映射&#xff0c;建立模型类&#xff0c;添加主键生成器 //指定生成器名称GeneratedValue(generator "uuid2" )GenericGenerator(name "uuid2", strategy "org.hiber…

在Go中处理恐慌

介绍 (Introduction) Errors that a program encounters fall into two broad categories: those the programmer has anticipated and those the programmer has not. The error interface that we have covered in our previous two articles on error handling largely deal…