«Go»: указатели и арифметика
Одна из особенностей «Go», о которой пишут в большинстве статей по языку, это наличие в языке указателей, но отсутствие арифметики с ними. Вот, например, цитата из статьи «Go For C++ Programmers»:
Go has pointers but not pointer arithmetic. You cannot use a pointer variable to walk through the bytes of a string.
В самом деле, если попробовать скомпилировать следующий пример, то нас ждёт неудача:
package main
func main() {
var pointer *[]uint8 = new([]uint8)
pointer++ // invalid operation: pointer += 1 (mismatched types *[]uint8 and int)
}
Никакими преобразованиями базовых типов не добиться чтобы язык разрешил прибавить хоть что-то к указателю. Я написал «базовых» и это неслучайно. Язык действительно не содержит арифметики с указателями, но в языке есть стандартные модули.
В модуле «unsafe» есть специальный тип «Pointer», в руководстве по использованию которого написано следующее:
1) A pointer value of any type can be converted to a Pointer.
2) A Pointer can be converted to a pointer value of any type.
3) A uintptr can be converted to a Pointer. 4) A Pointer can be converted to a uintptr.
В свою очередь, uintptr встроен в язык и это не указатель, это обычный числовой тип, пусть вас не смущает слово «ptr». Указатель напрямую нельзя преобразовать к типу uintptr или обратно, но можно это сделать через unsafe.Pointer, как видно из цитаты выше.
И вот что получается:
package main
import "fmt"
import "unsafe"
func main() {
e := [...]uint8{11,22,33} // создаю массив из трёх байт — 1, 2, 3
p := &e[0] // p — указатель на первый элемент массива (тип *uint8)
fmt.Println(*p) // выводит «11»
p = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1)) // указатель преобразован в число, прибавляется «1», преобразуем обратно в указатель
fmt.Println(*p) // выводит «22»
s := "12345" // строка, содержащая «12345»
*(*uintptr)(unsafe.Pointer(&s))++ // сдвигаем адрес, на который ссылается «s»
fmt.Println(s, len(s)) // «2345», но длина всё равно 5 (!), в последнем байте неотображаемый символ
}
Мне, кажется, что ты пытаешься писать на этом языке так же как на С++, к примеру, что не правильно.
Ну модуль же называется unsafe =)
Часто такое вижу, когда на ruby пишут как на C++ или как на Java, вроде код на Ruby, а все таки не на Ruby
Комментарий для twitter.com/timurv:
Я не знаю C++. Я не пытаюсь писать на этом языке как на C++ или чём-то подобном. Это статья просто показывает, что арифметики нет, но если очень надо, то она есть.
Мне это уже один раз понадобилось ( http://bolknote.ru/all/3197 ), когда я вызвал syscall ioctl, которому нужно было передать структуру, которую «Go» сдвигал на 4 байта в памяти (выравнивал по границе 8 байт). Вот тогда-то я и научился сдвигать указатель.
P.S. Кстати, прикрутил бы к своему блогу OpenID, это же просто ( http://bolknote.ru/all/1514/ ) и подписывался бы им?
Или граватарку прицепил. Я вот OpenID лишь недавно победил на этом сайте.
А длина строковой переменной вероятно на единичку больше из-за того, что там Ноль в конце? Как в Си реализовано хранение строковых переменных. По строкам мне больше паскаль нравится, в ntmt строки можно хранить нулевой символ и в первом байте всегда есть указание длинны строки.
Комментарий для orcinus.ru:
Для собственного сайта лучше использовать pavatar, мне кажется ( http://pavatar.com/ ).
Надо было всего лишь delegate сделать :)
Не-а, там не сишные строки. Длина переменной просто осталась без изменений, потому что «Гоу» хранит её в другом месте.
Комментарий для Евгения Степанищева:
То есть, если сдвинуть указатель на произвольное место, то там будет какой-нибудь мусор длины 5.
А почему выбор пал на полумертвый Go, а не на Scala/Clojure или F#/Haskell? Во всех есть клевые фишки для распределенных/параллельных приложений. Или просто эти языки вам известны? Про ерланг не спрашиваю, потому что простой и неинтересный, мозг им не размять. Спасибо.
Комментарий для anonymous:
F# не хочу, мне как-то совершенно не хочется писать софт для Windows, мне чужда эта архитектура и туда не хочется. Мне интересен веб или command line.
Haskell возможно будет следующим.
Clojure — это Лисп, с Лиспом я баловался немного.
О Scala не задумывался, присмотрюсь, единственное, что меня смущает, говорят, что он похож на Java.
От f# один шаг к его старшему и аскетичному брату окамлу :)
Мне вот clojure не понравилась, но кажется что это из-за нелюбви к лиспам вообще. Но в clojure есть STM, например, только ради этого можно с ним поиграться. Хотя, пожалуй, лучше правда сразу хаскелл.
А Scala хороший язык, очень хороший. На голову выше Java.
Буду ждать с нетерпением постов про хаскелл! :)
Комментарий для anonymous:
Я так и не сказал почему именно Go.
Во-первых, мне в кои-то веки захотелось что-нибудь компилируемого, я давно ничего не пишу на компилируемых языках (редкое баловство с Си не считается)
Во-вторых, мне очень понравилось как язык легко обходится с многозадачностью
В-третьих, GC в компириемом языке, для меня это что-то необычное
В-четвёртых, Ваня Сагалаев этот язык очень хвалил в разговоре, а у меня, напротив, впечатление было так себе, решил узнать что он в нём нашёл :)
Это не раньше чем пойму, что знаю «Гоу» на крепком среднем уровне. У меня там несколько пробелов даже на уровне языка, не говоря уже о каком-то мышлении в рамках этого языка или понимания кишок, доступных программисту.
Получается, что в выборе Go решающую роль сыграл разговор с Ваней Сагалаевым :) т. к. по первым трем пунктам ни скала, ни хаскелл не проигрывают :) спасибо, за развернутые ответы!