Введение
Мир вокруг можно моделировать различными способами. Самым естественным из них является представление о нём, как о наборе объектов. У каждого объекта есть свои свойства.
Например, для человека это возраст, пол, рост, вес и т.д. Для велосипеда – тип, размер колёс, вес, материал, изготовитель и пр. Для товара в магазине – идентификационный номер,
название, группа, вес, цена, скидка и т.д.
У классов объектов набор этих свойств одинаковый: все собаки могут быть описаны, с той или иной точностью, одинаковым набором свойств, но значения этих свойств будут разные.
Все самолёты обладают набором общих свойств в пределах одного класса. Если же нам надо более точное описание, то можно выделить подклассы: самолёт амфибии, боевые
истребители, пассажирские лайнеры – и в пределах уже этих классов описывать объекты.
Например, нам необходимо хранить информацию о сотрудниках компании. Каждый сотрудник, в общем, обладает большим количеством разных свойств.
Мы выберем только те, которые нас интересуют для решения прикладной задачи: пол, имя, фамилия, возраст, идентификационный номер. Для работы с таким
объектом нам необходима конструкция, которая бы могла агрегировать различные типы данных под одним именем. Для этих целей в си используются структуры.
Вложенные структуры
Помимо определения нового типа для создания структуры вы можете также определить вложенную структуру. Подобные выполняемые на ходу определения структуры могут быть полезными в ситуациях, когда придумывание новых имен для типов структур будет пустой тратой времени. Например, тесты часто используют структуру для определения всех параметров, которые составляют конкретный тестовый случай. Было бы излишним работать с новыми именами, наподобие , когда структура используется только в одном месте.
Вложенные структуры появляются с правой стороны присвоения переменной. Вы должны незамедлительно создать для них экземпляр, добавив дополнительную пару скобок со значениями для каждого из заданных полей. В примере ниже приводится определение вложенной структуры:
Вывод для данного примера будет выглядеть следующим образом:
Вместо того, чтобы определять новый тип, описывающий структуру с ключевым словом , в данном примере вложенная определяется сразу же после короткого оператора присваивания . Мы определяем поля структуры, как и в предыдущих примерах, но теперь мы должны немедленно предоставить другую пару скобок и значения, которые будут присваиваться каждому полю. Использование структуры остается прежним, мы можем обратиться к именам полей с помощью записи с точкой. Чаще всего вы будете видеть вложенные структуры в тестах, так как часто используемые один раз структуры определяются для хранения данных и ожиданий для конкретного тестового случая.“”“
Accessing fields of a Struct
Accessing fields of the struct are really simple. We use the dot operator for that. When a nested structure is there we use dot inside the nested struct as well.
// declaration type Car struct { name string cost int } var c Car = Car{"Mercedes", 3500000} // accessing carMake := c.name carPrice := c.cost
Now, for a nested struct, we can do like this:
package main import ( "fmt" ) type User struct { name string } type Service struct { name string user User } func main() { google := Service{ name: "Google", user: User{ name: "John Doe", }, } // accessing from nested struct fmt.Println(google.user.name) // prints "John Doe" }
Передача объекта как ссылки
Итак, мы увидели, что обновления объекта никак не отразятся на функции, потому что в функцию не была передана ссылка. Для передачи объекта как ссылки можно вместо значений передать ссылку на объект: просто ставим в начале оператор взятия адреса . Чтобы принялась ссылка на объект, а не значение, вызываемую функцию тоже надо обновить.
func UpdateEmployee(empDetials *Employee) { empDetails.Name = "Anshul";}var newEmployee = Employee{Name: "Mayank", Age: 40, Designation: "Developer"}UpdateEmployee(&newEmployee)// Исходный объект отправлен в обновлённую функцию...fmt.Println(newEmployee.Name)
Здесь функцию (и вызов функции) обновили, чтобы можно было отправлять и получать не значение объекта, а адрес в памяти. Теперь, если обновятся данные в вызываемой функции, то же самое произойдёт и в исходном объекте данных.
В чём проблема этого подхода? В том, что приходится использовать оператор взятия адреса, чтобы вытащить адрес объекта и отправить его в функцию.
Использование ключевого слова для создания объектов
Следующий способ создания объекта из структуры — использовать ключевое слово . При этом Golang создаёт новый объект типа и возвращает переменной его адрес в памяти. То есть, ключевое слово возвращает адрес этого объекта.
Используя ссылку, возвращаемую от ключевого слова , можно присваивать значения параметрам вновь созданного объекта. Все параметры по умолчанию принимают неявные значения, и для каждого конкретного типа это будет своё стандартное значение.
- Стандартным значением для булева типа будет .
- Стандартным значением для целочисленного типа будет .
- Стандартным значением для строкового типа будет (пустая строка).
Для лучшего понимания снова обратимся к коду:
type Employee struct { Name string Age int Designation string Salary int}var newEmployee = new(Employee)fmt.Println(newEmployee.Name)
Мы создаём новый объект с помощью ключевого слова , которое не позволяет передавать параметрам объекта стандартные значения. При создании объекта это делает за нас Golang. В нашем случае обращение к параметру вернёт пустую строку ().
В коде с помощью ключевого слова возвращается адрес вновь созданного объекта. Переменная выступает здесь в роли указателя на объект .
Указатели на структуру
Также возможно создать указатели на структуру. Посмотрите код ниже.
// hello.go package main import "fmt" // Student structure type Student struct { firstName, lastName string age int } func main() { stud7 := &Student{"Wade", "Wilson", 28} fmt.Println("First Name:", (*stud7).firstName) fmt.Println("Last Name:", (*stud7).lastName) fmt.Println("Age:", (*stud7).age) }
Stud7 в вышеприведенной программе является указателем на структуру Student . (* Stud7) .firstName синтаксис для доступа к полю FirstName из stud7 структуры. Смотрите вывод ниже.
Golang дает нам возможность использовать stud7.firstName вместо явного разыменования (* stud7) .firstName для доступа к полю firstName .
Go struct simple example
The following is a Go struct simple example.
simple.go
package main import "fmt" type User struct { name string occupation string age int } func main() { u := User{"John Doe", "gardener", 34} fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
We define a struct with three fields.
type User struct { name string occupation string age int }
We declare the struct.
u := User{"John Doe", "gardener", 34}
We initialize the struct.
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
We print the contents of the struct.
$ go run simple.go John Doe is 34 years old and he is a gardener
This is the output.
Go struct pointer
A pointer to the struct can be created with the operator or
the keyword. A pointer is dereferenced with the
operator.
pointer.go
package main import "fmt" type Point struct { x int y int } func main() { p := Point{3, 4} p_p := &p (*p_p).x = 1 p_p.y = 2 fmt.Println(p) }
In the example, we create a pointer to the struct.
p_p := &p
The operator returns a pointer to the
structure.
(*p_p).x = 1 p_p.y = 2
A pointer is dereferenced with the operator. Go also allows to
use the dot operator directly.
Alternatively, we can create a pointer to a struct with the
keyword.
pointer2.go
package main import "fmt" type User struct { name string occupation string age int } func main() { u := new(User) u.name = "Richard Roe" u.occupation = "driver" u.age = 44 fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
The example creates a new pointer to the struct with the
keyword.
Композиция структур в Golang
Отчет о погоде включает разнообразные данные вроде самых низких и высоких температур, указания текущего марсианского дня (его называют сол) и местности. Простым решением будет определение всех необходимых полей в единственной структуре , как показано в следующем примере:
Листинг 1
Go
type report struct {
sol int
high, low float64
lat, long float64
}
1 |
typereportstruct{ sol int high,low float64 lat,longfloat64 } |
При разборе Листинга 1 можно заметить, что является миксом из несопоставимых данных. Он становится более громоздким при включении большего количества информации вроде скорости и направления ветра, давления, влажности, сезона, восхода и заката.
К счастью, можно сгруппировать связанные поля вместе с помощью структур и композиции. В следующем примере определяется структура , что состоит из структур для температуры и местности.
Листинг 2
Go
type report struct {
sol int
temperature temperature // Поле temperature является структурой типа temperature
location location
}
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
1 |
typereportstruct{ sol int temperature temperature// Поле temperature является структурой типа temperature location location } typetemperaturestruct{ high,low celsius } typelocationstruct{ lat,longfloat64 } typecelsius float64 |
С определенными типами, отчет о погоде строится из данных о местности и температуры, как показано далее:
Go
bradbury := location{-4.5895, 137.4417}
t := temperature{high: -1.0, low: -78.0}
report := report{sol: 15, temperature: t, location: bradbury}
fmt.Printf(«%+v\n», report) // Выводит: {sol:15 temperature:{high:-1 low:-78} location:{lat:-4.5895 long:137.4417}}
fmt.Printf(«a balmy %v° C\n», report.temperature.high) // Выводит: a balmy -1° C
1 |
bradbury=location{-4.5895,137.4417} t=temperature{high-1.0,low-78.0} report=report{sol15,temperaturet,locationbradbury} fmt.Printf(«%+v\n»,report)// Выводит: {sol:15 temperature:{high:-1 low:-78} location:{lat:-4.5895 long:137.4417}} fmt.Printf(«a balmy %v° C\n»,report.temperature.high)// Выводит: a balmy -1° C |
Взгляните на Листинг 2
Обратите внимание, здесь сразу понятно, что параметры и касаются температур, в то время как те же самые поля из Листинга 1 не столь очевидны
Создавая отчет о погоде из более мелких типов, можно организовать код путем прикрепления методов каждого типа. Например, чтобы рассчитать среднюю температуру, можно написать метод, подобный данному ниже:
Листинг 3
Go
func (t temperature) average() celsius {
return (t.high + t.low) / 2
}
1 |
func(ttemperature)average()celsius{ return(t.high+t.low)2 } |
Тип и метод можно использовать независимо от отчета о погоде, как показано ниже:
Go
t := temperature{high: -1.0, low: -78.0}
fmt.Printf(«average %v° C\n», t.average()) // Выводит: -39.5° C
1 |
t=temperature{high-1.0,low-78.0} fmt.Printf(«average %v° C\n»,t.average())// Выводит: -39.5° C |
При создании отчета о погоде метод доступен через объединение с полем :
Go
report := report{sol: 15, temperature: t}
fmt.Printf(«average %v° C\n», report.temperature.average()) // Выводит: average -39.5° C
1 |
report=report{sol15,temperaturet} fmt.Printf(«average %v° C\n»,report.temperature.average())// Выводит: average -39.5° C |
Если вы хотите показать среднюю температуру напрямую через тип , нет нужды дублировать логику из Листинга 3. Вместо этого напишите метод, что встраивает реальную имплементацию:
Go
func (r report) average() celsius {
return r.temperature.average()
}
1 |
func(rreport)average()celsius{ returnr.temperature.average() } |
С методом для вставки из отчета в температуру вы получаете удобный доступ к , все еще структурируя код вокруг более мелких типов. Далее в уроке мы рассмотрим особенность Go, что позволяет сделать встраивание метода максимально простым.
Вопрос для проверки:
Сравните Листинг 1 и Листинг 2. Какой код удобнее и почему?
Интерфейсы
Вы могли заметить, что названия методов для вычисления площади круга и
прямоугольника совпадают. Это было сделано не случайно. И в реальной жизни и в
программировании отношения могут быть очень похожими. В Go есть способ сделать
эти случайные сходства явными с помощью типа называемого интерфейсом. Пример
интерфейса для фигуры ():
Как и структуры, интерфейсы создаются с помощью ключевого слова , за
которым следует имя интерфейса и ключевое слово . Однако, вместо того
чтобы определять поля, мы определяем «множество методов» . Множество методов это
список методов, которые будут использоваться для «реализации» интерфейса.
В нашем случае у и есть метод , которые возвращает
, получается они оба реализуют интерфейс . Само по себе это не
очень полезно, но мы можем использовать интерфейсы как аргументы в функциях:
Мы будет вызывать эту функцию так:
Интерфейсы также могут быть использованы в качестве полей:
Мы можем даже хранить в данные , определив в ней метод
:
Теперь может содержать , и даже другие
.
Go struct is a value type
Go structs are value types. When we assign a struct variable to another struct
variable, a new copy of the struct is created. Likewise, when we pass a struct
to another function, the function receives a new copy of the struct.
valuetype.go
package main import "fmt" type User struct { name string occupation string age int } func main() { u1 := User{"John Doe", "gardener", 34} u2 := u1 u2.name = "Richard Roe" u2.occupation = "driver" u2.age = 44 fmt.Printf("%s is %d years old and he is a %s\n", u1.name, u1.age, u1.occupation) fmt.Printf("%s is %d years old and he is a %s\n", u2.name, u2.age, u2.occupation) }
In the example, we assign a struct to another struct. Changing the fields of the
new struct does not affect the original struct.
$ go run valuetype.go John Doe is 34 years old and he is a gardener Richard Roe is 44 years old and he is a driver
The two structs are distinct entities.
Динамическое выделение памяти для структур
Динамически выделять память под массив структур необходимо в том случае, если заранее неизвестен размер массива. Для определения размера структуры в байтах используется операция sizeof(ИмяСтруктуры).Пример Библиотека из 3 книг
12345678910111213141516171819202122232425262728293031323334
#include <stdio.h>#include <stdlib.h>#include <malloc.h>struct book{ char title; char author; int value;};int main(){ struct book *lib; int i; system(«chcp 1251»); system(«cls»); lib = (struct book*)malloc(3 * sizeof(struct book)); for (i = 0; i<3; i++) { printf(«Введите название %d книги : «, i + 1); gets_s((lib + i)->title); printf(«Введите автора %d книги : «, i + 1); gets_s((lib + i)->author); printf(«Введите цену %d книги : «, i + 1); scanf_s(«%d», &(lib + i)->value); getchar(); } for (i = 0; i<3; i++) { printf(«\n %d. %s «, i + 1, (lib + i)->author); printf(«%s %d», (lib + i)->title, (lib + i)->value); } getchar(); return 0;}
Язык Си
Методы
Не смотря на то, что программа стала лучше, мы все еще можем значительно её
улучшить, используя метод — функцию особого типа:
Между ключевым словом и именем функции мы добавили «получателя».
Получатель похож на параметр — у него есть имя и тип, но объявление функции
таким способом позволяет нам вызывать функцию с помощью оператора :
Это гораздо проще прочесть, нам не нужно использовать оператор (Go
автоматически предоставляет доступ к указателю на для этого метода), и
поскольку эта функция может быть использована только для мы можем
назвать её просто .
Давайте сделаем то же самое с прямоугольником:
В будет написано:
Нулевое значение структуры в Go
Когда структура определена, и она явно не инициализируется каким-либо значением, свойствам структуры по умолчанию присваиваются их нулевые значения. Смотрите пример кода ниже.
// hello.go package main import ( "fmt" ) // Student struct type Student struct { firstName, lastName string age int } func main() { var stud4 Student //zero valued structure fmt.Println("Student 4", stud4) }
Вышеприведенная программа определяет stud4, но она не инициализируется никаким значением. Следовательно, firstName и lastName присваиваются нулевые значения строки, которая равна «», а возрасту присваиваются нулевые значения int, равные 0. Смотрите вывод ниже.
Примеры использования интерфейсов в коде на Golang
Вместе с Go вы можете начать имплементировать код и познавать интерфейсы по ходу дела. Любой код имплементирует интерфейс, даже тот код, что уже существует. Разберем тему на примере.
В следующем листинге выводится выдуманная дата, что состоит из дня года и часа дня.
Листинг 8
Go
package main
import (
«fmt»
«time»
)
// stardate возвращает выдуманное измерение времени для указанной даты.
func stardate(t time.Time) float64 {
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}
func main() {
day := time.Date(2012, 8, 6, 5, 17, 0, 0, time.UTC)
fmt.Printf(«%.1f Curiosity has landed\n», stardate(day)) // Выводит: 1219.2 Curiosity has landed
}
1 |
packagemain import( «fmt» «time» ) funcstardate(ttime.Time)float64{ doy=float64(t.YearDay()) h=float64(t.Hour())24.0 return1000+doy+h } funcmain(){ day=time.Date(2012,8,6,5,17,,,time.UTC) fmt.Printf(«%.1f Curiosity has landed\n»,stardate(day))// Выводит: 1219.2 Curiosity has landed } |
Функция из Листинга 8 ограничивается земными датами. Чтобы исправить это, следующий листинг объявляет интерфейс, который может использовать но уже по своему усмотрению:
Листинг 9
Go
type stardater interface {
YearDay() int
Hour() int
}
// stardate возвращает выдуманное измерение времени.
func stardate(t stardater) float64 {
doy := float64(t.YearDay())
h := float64(t.Hour()) / 24.0
return 1000 + doy + h
}
1 |
typestardaterinterface{ YearDay()int Hour()int } funcstardate(tstardater)float64{ doy=float64(t.YearDay()) h=float64(t.Hour())24.0 return1000+doy+h } |
Новая функция в Листинге 9 продолжает оперировать земными датами, потому что тип из стандартной библиотеке удовлетворяет требования интерфейса . Интерфейсы в Go являются удовлетворяемыми косвенным образом, что особенно полезно при работе с чужим кодом.
С находящимся на месте интерфейсом Листинг 9 может быть расширен с типом , что удовлетворяет требования интерфейса с методами и , как показано в следующем коде.
Листинг 10
Go
type sol int
func (s sol) YearDay() int {
return int(s % 668) // Марсианской год состоит из 668 дней
}
func (s sol) Hour() int {
return 0 // Неизвестный час
}
1 |
typesol int func(ssol)YearDay()int{ returnint(s%668)// Марсианской год состоит из 668 дней } func(ssol)Hour()int{ return// Неизвестный час } |
Функция оперирует как земными датами, так и марсианскими днями, как показано в следующем листинге.
Листинг 11
Go
day := time.Date(2012, 8, 6, 5, 17, 0, 0, time.UTC)
fmt.Printf(«%.1f Curiosity has landed\n», stardate(day)) // Выводит: 1219.2 Curiosity has landed
s := sol(1422)
fmt.Printf(«%.1f Happy birthday\n», stardate(s)) // Выводит: 1086.0 Happy birthday
1 |
day=time.Date(2012,8,6,5,17,,,time.UTC) fmt.Printf(«%.1f Curiosity has landed\n»,stardate(day))// Выводит: 1219.2 Curiosity has landed s=sol(1422) fmt.Printf(«%.1f Happy birthday\n»,stardate(s))// Выводит: 1086.0 Happy birthday |
Вопрос для проверки:
В чем преимущество неявно удовлетворяемых интерфейсов?
Вложенные структуры
Структура сама может являться полем структуры. Пример: структура Model – модель автомобиля, имеет название, номер, год выпуска и поле Make, которое в
свою очередь хранит номер марки и её название.
#include <conio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define YEAR_OFFSET 1890 typedef struct Model { int id; struct { int id; char *name; } make; char *name; unsigned char year; //year is an offset to 1890 } Model; char* mallocByString(const char *str) { char* p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; } void freeModel(Model* model) { free(model->make.name); free(model->name); } void xmlModel(Model *model) { printf( "<model id=\"%d\">\n" " <make id=\"%d\">\n" " <name>%s</name>\n" " </make>\n" " <year>%d</year>\n" " <name>%s</nam>>\n" "</model>", model->id, model->make.id, model->make.name, model->year, model->name); } int main() { Model cl; cl.id = 1; cl.make.id = 1; cl.make.name = mallocByString("Acura"); cl.name = mallocByString("CL"); cl.year = (2003 - YEAR_OFFSET); xmlModel(&cl); freeModel(&cl); getch(); }
Вложенные структуры инициализируются как многомерные массивы. В предыдущем примере можно произвести начальную инициализацию следующим образом:
#include <stdio.h> #include <conio.h> typedef struct Model { int id; struct { int id; char *name; } make; char *name; unsigned char year; //year is an offset to 1890 } Model; void main() { Model m = {10, {10, "Acura"}, "CL", 112}; printf("Model name = %s\n", m.name); printf("Make name = %s\n", m.make.name); getch(); }
P.S. подобным образом инициализировать строки не стоит, здесь так сделано только для того, чтобы упростить код.
Оператор равенства ==
Оператор равенства возвращает значение , если его операнды равны. В противном случае возвращается значение .
Равенство типов значений
Операнды равны, если равны их значения.
Примечание
У операторов , , , и , если какой-то из операндов не является числом (Double.NaN или Single.NaN), результатом операции является . Это означает, что значение не больше, не меньше и не равно любому другому значению (или ), включая . Дополнительные сведения и примеры см. в справочных статьях по Double.NaN или Single.NaN.
Два операнда одного типа enum равны, если равны соответствующие значения базового целочисленного типа.
По умолчанию пользовательские типы struct не поддерживают оператор . Чтобы поддерживать оператор , пользовательская структура должна перегружать его.
Начиная с версии C# 7.3 операторы и поддерживаются кортежами C#. Дополнительные сведения см. в разделе статьи Типы кортежей.
Равенство ссылочных типов
По умолчанию два операнда ссылочного типа, отличные от записи, являются равными, если они ссылаются на один и тот же объект.
Как показано в примере, определяемые пользователем ссылочные типы поддерживают оператор по умолчанию. Однако ссылочный тип может перегружать оператор . Если ссылочный тип перегружает оператор , воспользуйтесь методом Object.ReferenceEquals, чтобы проверить, что две ссылки этого типа указывают на один и тот же объект.
Равенство типов записей
Типы записей, доступные в C# 9.0 и более поздних версий, поддерживают операторы и , которые по умолчанию обеспечивают семантику равенства значений. То есть два операнда записи равны, когда оба они равны или равны соответствующие значения всех полей и автоматически реализуемых свойств.
Как показано в предыдущем примере, в случае с элементами ссылочного типа, отличными от записей, сравниваются их ссылочные значения, а не экземпляры, на которые они ссылаются.
Равенство строк
Два операнда равны, если они оба имеют значение или оба экземпляра строки имеют одинаковую длину и идентичные символы в каждой позиции символа.
Это порядковое сравнение, учитывающее регистр. Дополнительные сведения о том, как сравнивать строки, см. в статье Сравнение строк в C#.
Равенство делегатов
Два операнда делегатов одного типа среды выполнения равны, если оба из них имеют значение или их списки вызовов имеют одинаковую длину и содержат одинаковые записи в каждой позиции:
Подробные сведения см. в разделе (Операторы равенства делегатов) в спецификации языка C#.
Делегаты, созданные в результате оценки семантически идентичных лямбда-выражений не будут равны, как показано в примере ниже:
Go nested structs
Go structs can be nested.
nested.go
package main import "fmt" type Address struct { city string country string } type User struct { name string age int address Address } func main() { p := User{ name: "John Doe", age: 34, address: Address{ city: "New York", country: "USA", }, } fmt.Println("Name:", p.name) fmt.Println("Age:", p.age) fmt.Println("City:", p.address.city) fmt.Println("Country:", p.address.country) }
In the example, the struct is nested inside the
struct.
fmt.Println("City:", p.address.city) fmt.Println("Country:", p.address.country)
To access the fields of the nested struct, we access first the inner struct
with the dot operator; then we access the respective fields.
$ go run nested.go Name: John Doe Age: 34 City: New York Country: USA
This is the output.
Интерфейсы
Вы могли заметить, что названия методов для вычисления площади круга и
прямоугольника совпадают. Это было сделано не случайно. И в реальной жизни и в
программировании отношения могут быть очень похожими. В Go есть способ сделать
эти случайные сходства явными с помощью типа называемого интерфейсом. Пример
интерфейса для фигуры ():
Как и структуры, интерфейсы создаются с помощью ключевого слова , за
которым следует имя интерфейса и ключевое слово . Однако, вместо того,
чтобы определять поля, мы определяем «множество методов». Множество методов — это
список методов, которые будут использоваться для «реализации» интерфейса.
В нашем случае у и есть метод , который возвращает
, получается они оба реализуют интерфейс . Само по себе это не
очень полезно, но мы можем использовать интерфейсы как аргументы в функциях:
Мы будем вызывать эту функцию так:
Интерфейсы также могут быть использованы в качестве полей:
Мы можем даже хранить в данные , определив в ней метод
:
Теперь может содержать , и даже другие
.
Creating and initializing a Struct in GoLang
Now, we will create structs and initialize them with values. There are a few ways we could do that.
1. Using struct Literal Syntax
Struct literal syntax is just assigning values when declaring and it is really easy.
package main import ( "fmt" ) type Fruit struct { name string } func main() { var apple = Fruit{"Apple"} // struct literal syntax fmt.Println(apple) // prints {Apple} }
2. Using the new keyword
We can use the new keyword when declaring a struct. Then we can assign values using dot notation to initialize it.
package main import ( "fmt" ) type Fruit struct { name string } func main() { var banana = new(Fruit) banana.name = "Banana" fmt.Println(banana) // prints &{Banana} }
3. Using pointer address operator
The pointer address operator(&) can be used to declare a struct. Let’s see how we would do that.
package main import ( "fmt" ) type Fruit struct { name string } func main() { var mango = &Fruit{"Mango"} fmt.Println(mango) // prints &{Mango} }
Пример структур Go
Структура Go — это определяемый пользователем тип, представляющий коллекция полей.
Его можно использовать там, где имеет смысл сгруппировать данные в один блок, а не поддерживать каждый из их как отдельные типы.
Например, у студента есть имя, фамилия и возраст. Имеет смысл сгруппировать эти три поля в единую структуру
type Student struct {firstName string lastName string age int}
В приведенном выше фрагменте кода объявляется тип структуры Student , у которого есть поля first_name, last_name и age. Структуру также можно сделать более компактной, объявив поля, принадлежащие к одному типу, в одной строке, за которой следует имя типа.
В приведенной выше программе структуры firstName и lastName принадлежат к одной строке типа, age как целое число, и, следовательно, структура может быть переписана следующим образом.
type Student struct {firstName, lastName string age int}
Массивы структур
Работа с массивами структур аналогична работе со статическими массивами других типов данных.
Пример Библиотека из 3 книг
1234567891011121314151617181920212223242526272829303132
#include <stdio.h>#include <stdlib.h>struct book{ char title; char author; int value;};int main(){ struct book libry; int i; system(«chcp 1251»); system(«cls»); for (i = 0; i<3; i++) { printf(«Введите название %d книги : «, i + 1); gets_s(libry.title); printf(«Введите автора %d книги : «, i + 1); gets_s(libry.author); printf(«Введите цену %d книги : «, i + 1); scanf_s(«%d», &libry.value); getchar(); } for (i = 0; i<3; i++) { printf(«\n %d. %s «, i + 1, libry.author); printf(«%s %d», libry.title, libry.value); } getchar(); return 0;}
Результат выполнения
Компилятор не меняет порядок полей в структуре и не может оптимизировать такие случае. А мы можем.
Смежные поля могут быть объедены, если их сумма не превышает выравнивания структуры.
type someDataV2 struct{ a int8 // 1 bytec int8 // 1 byteb int64 // 8 byte}
Переместив поля, можно уменьшить размер структуры до 16 байт.
Type someDataV2 is 16 bytes longa at offset 0, size=1, align=1c at offset 1, size=1, align=1b at offset 8, size=8, align=8someDataV2 align is 8Bytes are &uint8{0x1, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
6 байт все еще не используются, но с этим уже ничего не поделаешь. Туда всегда можно добавить данных до 6 байт, не изменив размер структуры.
Почти всегда, гораздо важней читаемость кода, чем такие оптимизации
Важно понимать по какой причины у значения типа именно такой размер и что вообще происходит, а уже в случае необходимости заниматься оптимизацией
Линтеры, которые могут помочь:
- https://gitlab.com/opennota/check
- https://github.com/mdempsky/maligned
Код примеров play.golang или github
На сегодня все. Спасибо!