+9

[Series] Learn Go Day 2

Giới thiệu

Hello everyone, chúng ta tiếp tục học Go ngày 2 nhé. Let's go!!!

Nội dung

Pointers

Pointer là một biến chứa địa chỉ của một vùng nhớ trong bộ nhớ. Thay vì lưu trực tiếp giá trị vào biến, ta có thể lưu trữ địa chỉ của giá trị đó và sử dụng con trỏ để truy cập đến giá trị đó. Việc sử dụng con trỏ trong Go có thể giúp cải thiện hiệu suất và tiết kiệm bộ nhớ so với việc sao chép dữ liệu giữa các biến.

Để tạo một con trỏ, bạn sử dụng toán tử "&" để lấy địa chỉ của một biến, và toán tử "*" để truy cập giá trị tại địa chỉ đó.

cú pháp:

  • Toán tử "&":
    i := 42
    p = &i
    
  • Toán tử "*":
    g := *p
    

Ví dụ:

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point trỏ tới địa chỉ ô nhớ của i
	fmt.Println(*p) // kết quả => 42
	*p = 21         // gán giá trị vào ô nhớ
	fmt.Println(i)  // kết quả => 21

	p = &j         // gán point trỏ tới địa chỉ ô nhớ của j
	*p = *p / 37   // gán giá trị vào ô nhớ
	fmt.Println(j) // kết quả => 73
}

Structs

Struct là một kiểu dữ liệu (composite data type) cho phép bạn định nghĩa một tập hợp các trường (fields) có kiểu dữ liệu khác nhau. Nó giúp bạn tổ chức và lưu trữ các dữ liệu có liên quan với nhau trong cùng một đối tượng.

type Vertex struct {
	X int
	Y int
}

func main() {
	fmt.Println(Vertex{1, 2})
}

Chúng ta có thể tạo một đối tượng Vertex bằng cách gán giá trị cho các trường dữ liệu của struct.

vertex := Vertex{
  Y: 100,
  X: 200,
}

Chúng ta có thể truy cập và gán giá trị cho các trường dữ liệu của struct bằng cách sử dụng toán tử ".".

vertex.Y = 200
vertex.X = 300

Chúng ta có thể truy cập và sửa đổi nội dung của struct bằng cách sử dụng Pointer.

v := Vertex{1, 2}
p := &v
p.X = 400

Arrays

Một mảng được khai báo bằng cách chỉ định loại dữ liệu được lưu trữ và độ dài của mảng.

Cú pháp:

var <name> [lenght]<type>

ví dụ: Tạo mảng a có chiều dài là 10 với các phần tử kiểu int.

var a [10]int

Lưu ý: Một khi khai báo, chiều dài và kiểu dữ liệu của mảng thì không thể thay đổi. Nếu muốn thay đổi chiều dài, phải tạo mảng mới và sao chép dữ liệu từ mảng cũ.

Zero value

Khi khai báo một mảng mà không gán giá trị, toàn bộ phần tử sẽ được gán giá trị mặc định theo kiểu dữ liệu (int = 0, string = "").

Ví dụ: mảng [5]int sẽ có tất cả các phần tử có giá trị 0.

init-array.png

Literal

array := [5]int{10, 20, 30, 40, 50}

Calculating Size

Nếu độ dài được cho là "...", Go sẽ xác định độ dài của mảng dựa trên số phần tử được khởi tạo.

array := [...]int{10, 20, 30, 40, 50}

Specific elements

Nếu bạn biết độ dài của mảng mình cần, nhưng chỉ khởi tạo các phần tử cụ thể, bạn có thể sử dụng cú pháp này.

array := [5]int{1: 10, 2: 20}

Pointer elements

Bạn có thể có một mảng các con trỏ. Bạn sử dụng toán tử * để truy cập giá trị mỗi con trỏ mà phần tử trỏ tới.

array := [5]*int{0: new(int), 1: new(int)}

*array[0] = 10
*array[1] = 20

Các giá trị của mảng được khai báo trong array trông giống như hình dưới đây: Screenshot 2023-02-11 at 18.13.11.png

Một mảng là một giá trị trong Go. Điều này có nghĩa là bạn có thể sử dụng nó trong một thao tác gán. Do đó, một mảng có thể được gán cho các mảng khác cùng loại và cùng độ dài.

var array1 [5]int
array2 := [5]int{1, 2, 3, 4, 5}
array1 = array2

Còn đối với Pointer thì khi chúng ta gán là gán các địa chỉ ô nhớ của con trỏ chứ không phải các giá trị mà con trỏ đang trỏ tới.

var array1 [3]*string

array2 := [3]*string{new(string), new(string), new(string)}

*array2[0] = "Red"
*array2[1] = "Blue"
*array2[2] = "Green"

array1 = array2

Nó trông giống như hình dưới đây: Screenshot 2023-02-11 at 18.29.00.png

Multidimensional array ( mảng đa chiều )

Multidimensional là một cấu trúc dữ liệu cho phép lưu trữ các phần tử dưới dạng bảng nhiều chiều. Mỗi phần tử trong mảng đa chiều được xác định bởi nhiều chỉ số được gọi là các chỉ mục.

 // initialize
var array [4][2]int

// literal & initialize
array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} 

// Declare & initialize outer array
array := [4][2]int{1: {20, 21}, 3: {40, 41}}

// Declare & initialize outer and inner array
array := [4][2]int{1: {0: 20}, 3: {1: 41}}

Giống như hình dưới đây: Screenshot 2023-02-11 at 18.46.12.png

Passing arrays between functions

Khi truyền một mảng giữa các hàm, có thể tốn nhiều bộ nhớ và ảnh hưởng đến hiệu suất. Mảng được truyền bằng cách sao chép toàn bộ mảng vào hàm với bất kể kích thước nào của nó.

// Khai báo 1 mảng 8 megabytes.
var num [1e6]int

handleNum(num)

func handleNum(num [1e6]int) {
    ...
}

Khi gọi hàm handleNum với một mảng 8 megabytes, toàn bộ mảng sẽ được sao chép và cấp phát trên ngăn xếp, tốn nhiều bộ nhớ và ảnh hưởng đến hiệu suất. Cách tốt hơn là truyền một con trỏ đến mảng và để chỉ sao chép 8 byte.

var num [1e6]int

handleNum(&num)

func handleNum(num *[1e6]int) {
    ...
}

Tổng kết

Vậy là sau 1 ngày mình đã học được:

  • Pointers
  • Structs
  • Arrays

Nếu có thắc mắc hoặc cần giải thích rõ thêm chỗ nào thì các bạn có thể comment ở dưới. Cám ơn các bạn đã theo dõi bài viết của mình. Chúc các bạn có 1 ngày vui vẻ. Tks


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí