10.21 – Указатели на указатели и динамические многомерные массивы
Добавлено 9 июня 2021 в 21:53
Этот урок не является обязательным, он предназначен для продвинутых читателей, которые хотят узнать больше о C++. Никакие будущие уроки не будут основаны на этом уроке.
Указатель на указатель – это именно то, что можно ожидать из названия: указатель, содержащий адрес другого указателя.
Указатели на указатели
Обычный указатель на int
объявляется с помощью одной звездочки:
int *ptr; // указатель на int, одна звездочка
Указатель на указатель на int
объявляется с помощью двух звездочек
int **ptrptr; // указатель на указатель на int, две звездочки
Указатель на указатель работает так же, как обычный указатель – вы можете выполнять через него косвенное обращение, чтобы получить значение, на которое он указывает. А поскольку это значение само по себе является указателем, вы можете снова выполнить косвенное обращение уже через этот указатель, чтобы перейти к базовому значению. Эти косвенные обращения могут выполняться последовательно:
int value = 5; int *ptr = &value; // Косвенное обращение через указатель на int для получения значения int std::cout << *ptr; int **ptrptr = &ptr; // первое косвенное обращение для получения указателя на int, // второе косвенное обращение для получения значения int std::cout << **ptrptr;
Показанный выше код напечатает:
5 5
Обратите внимание, что вы не можете установить указатель на указатель, используя непосредственно значение:
int value = 5; int **ptrptr = &&value; // недопустимо
Это связано с тем, что оператор адреса (operator&
) требует l-значение (l-value), но &value
является r-значением (r-value).
Однако указатель на указатель может иметь значение null:
int **ptrptr = nullptr; // до C++11 используйте вместо этого 0
Массивы указателей
Указатели на указатели имеют несколько применений. Чаще всего они используется для динамического размещения массива указателей:
int **array = new int*[10]; // распределяем массив из 10 указателей int
Это работает так же, как обычный динамически размещаемый массив, за исключением того, что элементы массива имеют тип «указатель на int
», а не int
.
Двумерные динамически размещаемые массивы
Другое распространенное использование указателей на указатели – облегчение динамического размещения многомерных массивов (для обзора многомерных массивов смотрите урок «10.5 – Многомерные массивы»).
В отличие от двумерного фиксированного массива, который можно легко объявить следующим образом:
int array[10][5];
Динамическое размещение двумерного массива немного сложнее. У вас может возникнуть соблазн попробовать что-то вроде этого:
int **array = new int[10][5]; // не сработает!
Но это не сработает.
Здесь есть два возможных решения. Если крайнее правое измерение массива является константой времени компиляции, вы можете сделать так:
int (*array)[5] = new int[10][5];
Круглые скобки здесь необходимы для обеспечения правильного приоритета. В C++11 или новее это подходящий случай для использования автоматического определения типа:
auto array = new int[10][5]; // намного проще!
К сожалению, это относительно простое решение не работает, если какое-либо не крайнее левое измерение массива не является константой времени компиляции. В этом случае мы должны немного усложнить ситуацию. Сначала мы размещаем массив указателей (как показано выше). А затем мы перебираем этот массив указателей и для каждого элемента массива размещаем еще один динамический массив. Наш динамический двумерный массив – это динамический одномерный массив динамических одномерных массивов!
int **array = new int*[10]; // размещаем массив из 10 указателей int - это наши строки for (int count = 0; count < 10; ++count) array[count] = new int[5]; // это наши столбцы
Затем мы можем получить доступ к нашему массиву, как обычно:
array[9][4] = 3; // Это то же самое, что (array[9])[4] = 3;
С помощью этого метода, поскольку каждый столбец массива динамически размещается независимо, можно создавать динамически размещаемые двумерные массивы, которые не являются прямоугольными. Например, мы можем сделать массив в форме треугольника:
int **array = new int*[10]; // размещаем массив из 10 указателей int - это наши строки for (int count = 0; count < 10; ++count) array[count] = new int[count+1]; // это наши столбцы
Обратите внимание, что в приведенном выше примере array[0]
– это массив длиной 1, array[1]
— это массив длиной 2, и т.д.
Для освобождения памяти динамически размещенного с помощью этого метода двумерного массива также требуется цикл:
for (int count = 0; count < 10; ++count) delete[] array[count]; delete[] array; // это нужно сделать в последнюю очередь
Обратите внимание, что мы удаляем массив в порядке, обратном его созданию (сначала элементы, затем сам массив). Если мы удалим массив до элементов массива, тогда нам потребуется доступ к освобожденной памяти, чтобы удалить эти элементы. А это приведет к неопределенному поведению.
Поскольку выделение и освобождение памяти для двумерных массивов сложно, и в них легко ошибиться, часто бывает проще «сгладить» двумерный массив (размером x на y) в одномерный массив размером x * y:
// Вместо этого: int **array = new int*[10]; // размещаем массив из 10 указателей int - это наши строки for (int count = 0; count < 10; ++count) array[count] = new int[5]; // это наши столбцы // Сделаем так int *array = new int[50]; // массив 10x5, сведенный в единый массив
Затем, для преобразования индексов строки и столбца прямоугольного двумерного массива в один индекс одномерного массива можно использовать простую математику:
int getSingleIndex(int row, int col, int numberOfColumnsInArray) { return (row * numberOfColumnsInArray) + col; } // устанавливаем значение array[9,4] равным 3, используя наш плоский массив array[getSingleIndex(9, 4, 5)] = 3;
Передача указателя по адресу
Подобно тому, как мы можем использовать параметр-указатель для изменения фактического значения переданного базового аргумента, мы также можем передать в функцию указатель на указатель и использовать этот указатель для изменения значения указателя, на который он указывает (еще не запутались?).
Однако если мы хотим, чтобы функция могла изменять то, на что указывает аргумент-указатель, обычно лучше использовать ссылку на указатель. Поэтому мы не будем здесь больше об этом говорить.
Подробнее о передаче по адресу и передаче по ссылке мы поговорим в следующей главе.
Указатель на указатель на указатель на…
Также возможно объявить указатель на указатель на указатель:
int ***ptrx3;
Это можно использовать для динамического выделения памяти для трехмерного массива. Однако для этого потребуется цикл внутри цикла, что значительно усложняет создание корректного кода.
Вы даже можете объявить указатель на указатель на указатель на указатель:
int ****ptrx4;
Или больше, если хотите.
Однако на самом деле в них не видно особой пользы, потому что не часто вам нужно так много косвенных обращений.
Заключение
Если доступны другие варианты, мы рекомендуем избегать использования указателей на указатели, поскольку они сложны в использовании и потенциально опасны. Достаточно легко выполнить косвенное обращение через нулевой или висячий указатель с помощью обычных указателей – а с указателем на указатель это легче вдвойне, поскольку вам нужно выполнить двойное косвенное обращение, чтобы перейти к базовому значению!
Оригинал статьи:
- 9.21 — Pointers to pointers and dynamic multidimensional arrays
Теги
arrayC++ / CppLearnCppДинамическое распределение памятиДля начинающихМногомерный массив ОбучениеПрограммированиеУказатель / Pointer (программирование)Назад
Оглавление
Вперед
Указатель C++ на указатель (двойной указатель)
В C++ указатель — это переменная, которая используется для хранения адресов памяти других переменных. Это переменная, которая указывает на тип данных (например, int или string) того же типа и создается с помощью оператора *.
Синтаксис указателя в C++:
data_type_of_pointer *name_of_variable = & normal_variable;
Содержание
- Что такое указатель на указатель или двойной указатель в C++?
- Как объявить указатель на указатель в C++?
- Каков будет размер указателя на указатель в С++?
Что такое указатель на указатель или двойной указатель в C++?
Теперь мы уже знаем, что указатель хранит адрес памяти других переменных. Итак, когда мы определяем указатель на указатель, первый указатель используется для хранения адреса переменных, а второй указатель хранит адрес первого указателя. Именно по этой причине он известен как двойной указатель или указатель на указатель.
На приведенной ниже диаграмме объясняется концепция двойных указателей :
На приведенной выше диаграмме показано представление памяти указателя на указатель или двойного указателя, мы можем легко понять, что адрес переменной (т. е. адрес 1) хранится в указателе 1, а адрес указателя 1 (т. е. адрес 2) хранится в указателе 2. Это известно как двойные указатели или указатель на указатель.
Как объявить указатель на указатель в C++?
Объявление указателя на указатель аналогично объявлению указателя в C++. Разница в том, что мы должны использовать дополнительный оператор * перед именем указателя в C++.
Синтаксис указателя на указатель (двойной указатель) в С++:
data_type_of_pointer **name_of_variable = & normal_pointer_variable;
Пример:
int val = 169;
int *ptr = &val; // storing address of val to pointer ptr.
int **double_ptr = &ptr; // pointer to a pointer declared which is pointing to an integer.
На приведенной ниже диаграмме объясняется концепция двойных указателей:
На приведенной выше диаграмме показано представление в памяти указателя на указатель. Первый указатель ptr1 хранит адрес переменной, а второй указатель ptr2 хранит адрес первого указателя.
Ниже приведена программа C++ для реализации Pointer to Pointer :
С++
#include <bits/stdc++.h>
using
namespace
std;
int
main()
{
int
variable = 169;
int
* pointer1;
int
** pointer2;
pointer1 = &variable;
pointer2 = &pointer1;
cout <<
"Value of variable :- "
<<
variable <<
"\n"
;
cout <<
"Value of variable using single pointer :- "
<<
*pointer1 <<
"\n"
;
cout <<
"Value of variable using double pointer :- "
<<
**pointer2 <<
"\n"
;
return
0;
}
Выход
Value of variable :- 169 Value of variable using single pointer :- 169 Value of variable using double pointer :- 169
Каков будет размер указателя на указатель в С++?
В языке программирования C++ двойной указатель ведет себя аналогично обычному указателю. Таким образом, размер переменной двойного указателя и размер переменной обычного указателя всегда равны.
Ниже приведена программа на C++ для проверки размера двойного указателя:
С++
#include <bits/stdc++.h>
using
namespace
std;
int
main()
{
int
val = 169;
int
* ptr = &val;
int
** double_ptr = &ptr;
cout <<
" Size of normal Pointer: "
<<
sizeof
(ptr) <<
"\n"
;
cout <<
" Size of double Pointer: "
<<
sizeof
(double_ptr) <<
"\n"
;
return
0;
}
Выход
Size of normal Pointer: 8 Size of double Pointer: 8
Примечание. Вывод приведенного выше кода также зависит от типа используемой машины. Размер указателя не фиксирован в языке программирования C++ и полностью зависит от других факторов, таких как архитектура ЦП и используемая ОС. Обычно для 64-битной операционной системы назначается размер памяти 8 байт, а для 32-битной операционной системы размер памяти 4 байта.
1. Разместите два указателя x и y . Выделение указателей не выделить любые указатели. | |
2. Выделите указатель и установите x , чтобы он указывал на него. Каждый язык имеет для этого свой синтаксис. Важно то, что память динамически выделяется для одного указателя, и x указывают на этот указатель. | |
3. Разыменовать x , чтобы сохранить 42 в его указателе. Это базовый пример операции разыменования. Начните с x , следуйте стрелке, чтобы получить доступ к ее указателю. | |
4. Попробуйте разыменовать и , чтобы сохранить 13 в его указателе. Это дает сбой, потому что y не имеет указателя — он никогда не был назначен. | |
5. Присвоить y = x; , так что y указывает на х пуант. Теперь x и y указывают на одну и ту же точку — они «совместно используются». | |
6. Попробуйте разыменовать y , чтобы сохранить 13 в своем указателе. На этот раз это работает, потому что предыдущее задание дало и пуант. |
Указатели в Go-Go 101
Главная новинка!
GO 101
Go Generics 101
GO.
Go Agg 101
Go 101 Блог
Go 101 Приложения и библиотеки
Тема: темный/светлый
Три новые книги Go Optimizations 101, Подробности и советы 101 и Go Generics 101 публикуются сейчас. Наиболее выгодно покупать их все через этот комплект книг. в книжном магазине Leanpub.
Хотя Go вбирает в себя многие функции из всех видов других языков, Go в основном рассматривается как язык семейства C. Одним из доказательств является то, что Go также поддерживает указатели. Указатели Go и указатели C очень похожи во многих аспектах. но есть также некоторые различия между указателями Go и указателями C. В этой статье будут перечислены все виды концепций и деталей, связанных с указателями в Go.
Адреса памяти
Адрес памяти означает определенную ячейку памяти в программировании.
Как правило, адрес памяти хранится в виде собственного (целого) слова без знака. Размер родного слова составляет 4 (байта) на 32-битных архитектурах и 8 (байт) на 64-битных архитектурах. Таким образом, теоретический максимальный размер памяти составляет 2 32 байт, он же 4 ГБ (1 ГБ == 2 30 байт), в 32-разрядных архитектурах, и составляет 2 64 байт, также известный как 16EB (1EB == 1024PB, 1PB == 1024TB, 1TB == 1024GB), на 64-битных архитектурах.
Адреса памяти часто представляются шестнадцатеричными целочисленными литералами,
например 0x1234CDEF
.
Значение Адреса
Адрес значения означает начальный адрес сегмента памяти. занимает прямая часть значения.
Что такое указатели?
Указатель — это один из типов в Go. Значение указателя используется для хранения адреса памяти, который обычно является адресом другого значения.
В отличие от языка C, из соображений безопасности для указателей Go наложены некоторые ограничения. Подробности см. в следующих разделах.
Типы и значения указателя перехода
В Go безымянный тип указателя может быть представлен как *T
,
где T
может быть произвольным типом.
Тип T
называется базовым типом указателя типа *T
.
Мы можем объявлять именованные типы указателей, но обычно не рекомендуется использовать именованные типы указателей, для безымянных типов указателей лучше читаемы.
Если базовый тип
именованного типа указателя *T
,
тогда базовый тип именованного типа указателя равен T
.
Два безымянных типа указателя с одним и тем же базовым типом являются одним и тем же типом.
Пример:
*int // Тип безымянного указателя, базовым типом которого является int. **int // Безымянный тип указателя, базовый тип которого *int. // Ptr — это именованный тип указателя, базовый тип которого — int. введите Ptr *int // PP — это тип именованного указателя, базовым типом которого является Ptr. тип ПП *Ptr
Нулевые значения любых типов указателей представлены предварительно объявленным nil
.
Никакие адреса не сохраняются в нулевых значениях указателя.
Значение типа указателя, базовый тип которого T
, может хранить только адреса значений типа T
.
О слове «Справка»
В Go 101 слово «ссылка» указывает на отношение. Например, если значение указателя хранит адрес другого значения, тогда мы можем сказать, что значение указателя (непосредственно) ссылается на другое значение, а другое значение имеет хотя бы одну ссылку. Использование слова «ссылка» в Go 101 соответствует спецификации Go.
Когда значение указателя ссылается на другое значение, мы также часто говорим, что значение указателя указывает на другое значение.
Как получить значение указателя и что такое адресуемые значения?
Есть два способа получить ненулевое значение указателя.
- Встроенная функция
new
может использоваться для выделения памяти для значения любого типа.new(T)
выделит память для значенияT
(анонимная переменная) и вернуть адресЗначение T
. Выделенное значение является нулевым значением типаT
. Возвращенный адрес рассматривается как значение указателя типа*T
. - Мы также можем взять адреса значений, к которым можно обращаться в Go.
Для адресуемого значения
t
типаT
, мы можем использовать выражение&t
, чтобы взять адресt
, гдеи
— оператор для получения адресов значений. Тип&t
рассматривается как*T
.
Вообще говоря, адресуемое значение означает значение, которое размещено где-то в памяти. В настоящее время нам просто нужно знать, что все переменные являются адресуемыми, тогда как константы, вызовы функций и явные результаты преобразования не адресуются. Когда переменная объявляется, среда выполнения Go выделяет часть памяти для этой переменной. Начальный адрес этого фрагмента памяти является адресом переменной.
Мы узнаем другие адресуемые и неадресуемые значения из других статей позже. Если вы уже знакомы с Go, вы можете прочитать это резюме, чтобы получить списки адресуемых и неадресуемых значений в Go.
В следующем разделе будет показан пример получения значений указателя.
Разыменование указателя
Учитывая значение указателя p
типа указателя, базовый тип которого T
,
как вы можете получить значение по адресу, хранящемуся в указателе (также известном как значение
на который ссылается указатель)? Просто используйте выражение *р
,
где *
называется оператором разыменования. *p
называется разыменованием указателя p
.
Разыменование указателя — это процесс, обратный взятию адреса.
Результатом *p
является значение типа T
(базовый тип типа p
).
Разыменование нулевого указателя вызывает панику во время выполнения.
Следующая программа показывает, как принимается адрес и примеры разыменования указателя:
основной пакет импортировать "фмт" основная функция () { p0 := new(int) // p0 указывает на нулевое значение int. fmt.Println(p0) // (шестнадцатеричная адресная строка) fmt.Println(*p0) // 0 // x является копией значения в // адрес, хранящийся в p0. х := *p0 // Оба берут адрес x. // x, *p1 и *p2 представляют одно и то же значение. р1, р2 := &х, &х fmt.Println(p1 == p2) // правда fmt.Println(p0 == p1) // ложь p3 := &*p0 // p3 := &(*p0) p3 := p0 // Теперь p3 и p0 хранят один и тот же адрес. fmt.Println(p0 == p3) // правда *p0, *p1 = 123 789fmt.Println(*p2, x, *p3) // 789 789 123 fmt.Printf("%T, %T \n", *p0, x) // целое, целое fmt.Printf("%T, %T\n", p0, p1) // *int, *int }
На следующем рисунке изображены отношения значений используется в вышеуказанной программе.
Зачем нужны указатели?
Сначала рассмотрим пример.
основной пакет импортировать "фмт" функция двойной (x int) { х += х } основная функция () { переменная а = 3 двойной (а) fmt.Println(a) // 3 }
Ожидается, что функция double
в приведенном выше примере изменит
входной аргумент, удвоив его. Однако это не удается. Почему?
Потому что все присвоения значений, включая передачу аргументов функции, в Go являются копированием значений.
Функция double
изменила копию ( x
) переменной на
, но не переменную на
.
Одно из решений для исправления вышеуказанной функции double
— позволить ей вернуться
результат модификации. Это решение не всегда работает для всех сценариев.
В следующем примере показано другое решение с использованием параметра указателя.
основной пакет импортировать "фмт" функция double(x *int) { *х += *х x = nil // строка предназначена только для пояснения } основная функция () { переменная а = 3 двойной (&а) fmt. Println(a) // 6 р := &а двойной (р) fmt.Println(a, p == nil) // 12 ложь }
Мы можем обнаружить, что, изменив параметр на тип указателя,
переданный аргумент указателя &a
и его копия x
, используемые в теле функции, ссылаются на одно и то же значение,
так модификация на *x
эквивалентно модификации *p
, также известной как переменная a
.
Другими словами, изменение тела функции double
теперь может быть отражено вне функции.
Наверняка модификация самой копии переданного аргумента указателя
по-прежнему не может быть отражено в переданном аргументе указателя.
После второго вызова функции double
локальный указатель p
не модифицируется до ноль
.
Короче говоря, указатели предоставляют косвенные способы доступа к некоторым значениям. Многие языки не имеют концепции указателей. Однако указатели просто скрыты под другими понятиями в этих языках.
Указатели возврата локальных переменных безопасны в Go
В отличие от языка C, Go поддерживает сборку мусора. так что возвращать адрес локальной переменной в Go абсолютно безопасно.
функция newInt() *int { а := 3 возврат &a }
Ограничения на указатели в Go
Из соображений безопасности Go накладывает некоторые ограничения на указатели (по сравнению с указателями в языке C). Применяя эти ограничения, Go сохраняет преимущества указателей, и в то же время избегает опасности указателей.
Значения указателя Go не поддерживают арифметические операции
В Go указатели не могут выполнять арифметические операции.
Для указателя p
, p++
и p-2
оба незаконны.
Если p
является указателем на числовое значение, компиляторы увидят *p++
является юридическим утверждением и обрабатывается как (*p)++
. Другими словами, приоритет оператора разыменования указателя *
больше, чем оператор приращения ++
и оператор декремента -
.
Пример:
основной пакет импортировать "фмт" основная функция () { а := int64(5) р := &а // Следующие две строки не компилируются. /* р++ р = (&а) + 8 */ *р++ fmt.Println(*p, а) // 6 6 fmt.Println(p == &a) // верно *&а++ *&*&а++ **&р++ *&*р++ fmt.Println(*p, а) // 10 10 }
Значение указателя не может быть преобразовано в произвольный тип указателя
В Go значение указателя типа T1
может быть напрямую и явно преобразовано.
к другому типу указателя T2
, только если одно из следующих двух условий
получить удовлетворение.
- Базовые типы типа
T1
иT2
идентичны (игнорируя теги структуры), в частности, если либоT1
, либоT2
это безымянный тип и их базовые типы идентичны (учитывая теги структуры), тогда преобразование может быть неявным. Типы и значения структур будут объяснены в следующая статья. - Типы
T1
иT2
являются безымянными типами указателей. базовые типы их базовых типов идентичны (игнорируя теги структуры).
Например, для показанных ниже типов указателей:
тип MyInt int64 введите Та *int64 введите Tb *MyInt
существуют следующие факты:
- значения типа
*int64
могут быть неявно преобразованы в типTa
, и наоборот, поскольку их базовые типы оба*int64
. - значения типа
*MyInt
могут быть неявно преобразованы в типTb
, и наоборот, так как их базовые типы оба*МойИнт
. - значения типа
*MyInt
могут быть явно преобразованы в тип*int64
, и наоборот, потому что они оба безымянные, а базовые типы их базовых типов обаint64
. - значения типа
Ta
не могут быть напрямую преобразованы в типTb
, даже если явно. Однако, по только что перечисленным первым трем фактам, значениеpa
типTa
можно косвенно преобразовать в типTb
путем вложения трех явных преобразованийTb((*MyInt)((*int64)(pa)))
.
Никакие значения этих типов указателей не могут быть преобразованы в тип *uint64
любыми безопасными способами.
Значение указателя нельзя сравнивать со значениями произвольного типа указателя
В Go указатели можно сравнить с операторами ==
и !=
.
Два значения указателя Go можно сравнивать только в том случае, если выполняется одно из следующих трех условий.
удовлетворены.
- Типы двух указателей Go идентичны.
- Одно значение указателя может быть неявно преобразовано в тип указателя другого. Другими словами, базовые типы двух типов должны быть идентичными. и любой из двух типов двух указателей Go является безымянным типом.
- Один и только один из двух указателей представлен голым (нетипизированным) идентификатором
nil
.
Пример:
основной пакет основная функция () { введите MyInt int64 введите Та *int64 введите Tb *MyInt // 4 нулевых указателя разных типов. вар па0 Та переменная pa1 *int64 вар pb0 Tb переменная pb1 *MyInt // Следующие 6 строк компилируются нормально. // Все результаты сравнения верны. _ = pa0 == pa1 _ = pb0 == pb1 _ = pa0 == ноль _ = pa1 == ноль _ = pb0 == ноль _ = pb1 == ноль // Ни одна из следующих 3 строк не компилируется нормально. /* _ = pa0 == pb0 _ = pa1 == pb1 _ = pa0 == Tb(ноль) */ }
Значение указателя не может быть присвоено значениям указателя других типов указателя
Условиями присвоения значения указателя другому значению указателя являются такие же, как условия для сравнения значения указателя с другим значением указателя, которые перечислены выше.
Можно обойти ограничения указателя перехода
Как упоминалось в начале этой статьи,
механизмы (в частности, тип unsafe.Pointer
)
предоставлено небезопасный
стандартный пакет
можно использовать для снятия ограничений, наложенных на указатели в Go.
Тип unsafe.Pointer
подобен void*
в C.
Вообще небезопасные способы использовать не рекомендуется.
Индекс↡
Цифровые версии этой книги доступны в следующих местах:
- магазин Линпаб, $19,99+ .
- Магазин Amazon Kindle, (в настоящее время недоступен) .
- магазин Apple Books, 19,99 $ .
- Google Play магазин, 19,99 $ .
- бесплатные электронные книги, включая форматы pdf, epub и azw3.
Тапир, автор Go 101, писал книги из серии Go 101. и поддержка сайта go101.org с июля 2016 года. Новое содержание будет постоянно добавляться в книгу и на веб-сайт время от времени. Tapir также является независимым разработчиком игр. Вы также можете поддержать Go 101, играя в игры Tapir. (сделано как для Android, так и для iPhone/iPad):
- Color Infection (★★★★★), основанная на физике оригинальная казуальная игра-головоломка. 140+ уровней.
- Rectangle Pushers (★★★★★), оригинальная казуальная игра-головоломка. Два режима, 104+ уровней.
- Let’s Play With Particles, оригинальная казуальная игра в жанре экшн. Включены три мини-игры.
Индекс:
- О Go 101 — почему написана эта книга.
- Благодарности
- Введение в Go — почему стоит изучать Go.
- The Go Toolchain — как компилировать и запускать программы Go.
- Ознакомьтесь с кодом Go
- Введение элементов исходного кода
- Ключевые слова и идентификаторы
- . Основные типы и литералы их значений .
- Константы и переменные — также вводит нетипизированные значения и выводы типа.
- Общие операторы — также вводятся дополнительные правила вывода типов.
- Объявления функций и вызовы
- Кодовые пакеты и импорт пакетов
- Выражения, операторы и простые операторы
- Основные потоки управления
- Горутины, отложенные вызовы функций и паника/восстановление
- Перейти Тип системы
- Обзор системы Go Type — необходимо прочитать, чтобы освоить программирование Go.
- Стрелки
- Конструкции
- Value Parts — для более глубокого понимания ценностей Go.
- Массивы, фрагменты и карты — первоклассные типы контейнеров граждан.
- Струны
- Функции — типы и значения функций, включая вариативные функции. Каналы
- — лучший способ выполнять параллельную синхронизацию.
- Методы
- Интерфейсы — поля значений, используемые для отражения и полиморфизма.
- Type Embedding — расширение типа Go way.
- Указатели небезопасного типа
- Обобщения — использование и чтение составных типов
- Reflections — стандартный пакет
Reflect
.
- Некоторые специальные темы
- Правила разрыва строки
- Подробнее об отложенных вызовах функций
- Некоторые варианты использования при панике/восстановлении
- Подробное объяснение механизма паники/восстановления — также объясняет завершающие этапы вызовов функций.
- Кодовые блоки и области идентификаторов
- Заказы на оценку выражений
- Стоимость копирования стоимости в Go
- Устранение проверки границ
- Параллельное программирование
- Обзор параллельной синхронизации
- Варианты использования канала
- Как изящно закрыть каналы
- Другие методы параллельной синхронизации —
стандартный пакет синхронизации
.