Структуры в Си и CGO

По горячим следам хочу написать какая забытая мною особенность языка Си вчера ввела меня в заблуждение и почему несмотря на то, что я работал с сишной структурой, CGO так как будто не считало.

Для начала посмотрим как определяются и передаются структуры в Си. Полную спецификацию я приводить не буду, посмотрим на два распространённых способа. Первый используется в этом небольшом примере:

#include <stdio.h>
#include <math.h>

struct point {
	double x, y;
};

double point(struct point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", point((struct point){1, 2}));
}

Тут можно обратить внимание на две особенности — во-первых, везде, где мы используем структуру, приходится использовать и ключевое слово struct, во-вторых, в коде существует и структура point, и функция с таким названием.

Это происходит потому, что имена структур обитают в своём собственном пространстве имён, на его использование и указывает struct. Там же живут имена объединений (union) и перечислений (enum). Имена функций находятся в общем пространстве, так что два таких определения с одним именем друг другу не мешают.

Структуры, объявленные таким образом видны через CGO компилятора Гоу как C.struct_name (C.struct_point в данном случае). К сожалению, тут есть особенность, о которую я споткнулся — если запрашиваемая структура не определена, то Гоу не выдаст ошибки, мы просто получим пустую структуру:

package main

import ("C"; . "fmt")

func main() {
	Println(C.struct_undefined{}) // ошибки не будет
}

Но программисты — люди ленивые, писать всюду struct неудобно. К счастью есть способ этого не делать — можно завести структуру, а потом дать ей имя в пространстве всех остальных типов. Это делается при помощи ключевого слова typedef позволяющего создавать алиасы типов:

struct point {
	double x, y;
};

typedef struct point point;

Теперь у нас есть два имени для одного типа — один в пространстве структур и прочего, второе — в общем пространстве. Можно даже сделать их одинаковыми, что очень удобно. Если второе имя нам ни к чему (бывают случаи, когда оно необходимо, но я не буду их тут рассматривать), то можно определить анонимную структуру и сразу дать имя этому типу в общем пространстве, так тоже будет работать.

Это нас подводит ко второму способу работы со структурами:

#include <stdio.h>
#include <math.h>

typedef struct {
	double x, y;
} point;

double veclen(point p) {
	return sqrt(pow(p.x, 2) + pow(p.y, 2));
}

int main() {
	printf("%g\n", veclen((point){1, 2}));
}

Ключевое слово struct перед типом уже не нужно, раз все имена в общем пространстве. Это же касается и CGO.

Как раз на эти грабли я и наступил — позабыл про эти все особенности. Когда я ещё писал на Си, всегда пользовался вторым способом, с именем в общем пространстве и привык к тому, что это и есть структуры.

2 комментария

Крутая пасхалка

Приборная панель автомобиля с зелёной подсветкой: тахометр, спидометр, датчики температуры и топлива, центральный дисплей с надписью «PLAY» и схемой.

Посмотрел сегодня «Первому игроку приготовиться», там сюжет строится вокруг «пасхального яйца» в программе виртуальной реальности, и вспомнил, что самое крутая «пасхалка», которую я видел за последние несколько лет — это тетрис в «Газели Некст».

Специально выкладываю это сегодня, а то завтра первое апреля и люди подумают, что это розыгрыш. На ютубе около двух тысяч роликов на эту тему, поищите.

Скриншот выше я взял из ролика на официальном канале кампании «Группа ГАЗ» и в комментарии к нему поясняется, что тетрис используется как тест бортового компьютера — можно быстро погонять и процессор, и все зоны дисплея. Наверное так оно и есть, но обычно такие тесты выглядят намного утилитарнее, тут видимо кто-то из инженеров заскучал и решил сделать что-нибудь интересное.

Говорят на тетрис кто-то случайно наткнулся. Вполне может быть, запускается довольно тривиально — надо поднять обороты двигателя выше 2000 об/мин и повернуть левую крутилку приборной панели влево.

После запуска игры, правой крутилкой можно двигать фигуры, а левой — вращать и бросать вниз. Выйти из игры можно либо выключив зажигание, либо начав движение.

Оставить комментарий