«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 (!), в последнем байте неотображаемый символ
}
5 мая 2011 09:57

twitter.com/timurv (инкогнито)
5 мая 2011, 11:53

Мне, кажется, что ты пытаешься писать на этом языке так же как на С++, к примеру, что не правильно.
Ну модуль же называется unsafe =)

Часто такое вижу, когда на ruby пишут как на C++ или как на Java, вроде код на Ruby, а все таки не на Ruby

bolk (bolknote.ru)
5 мая 2011, 12:00, ответ предназначен twitter.com/timurv

Мне, кажется, что ты пытаешься писать на этом языке так же как на С++, к примеру, что не правильно.
Я не знаю C++. Я не пытаюсь писать на этом языке как на C++ или чём-то подобном. Это статья просто показывает, что арифметики нет, но если очень надо, то она есть.

Мне это уже один раз понадобилось (http://bolknote.ru/2011/05/03/~3197), когда я вызвал syscall ioctl, которому нужно было передать структуру, которую «Go» сдвигал на 4 байта в памяти (выравнивал по границе 8 байт). Вот тогда-то я и научился сдвигать указатель.

P.S. Кстати, прикрутил бы к своему блогу OpenID, это же просто (http://bolknote.ru/2007/12/06/~1514/) и подписывался бы им?

Orcinus Orca (orcinus.ru)
5 мая 2011, 17:18

Или граватарку прицепил. Я вот OpenID лишь недавно победил на этом сайте.

А длина строковой переменной вероятно на единичку больше из-за того, что там Ноль в конце? Как в Си реализовано хранение строковых переменных. По строкам мне больше паскаль нравится, в ntmt строки можно хранить нулевой символ и в первом байте всегда есть указание длинны строки.

bolk (bolknote.ru)
5 мая 2011, 17:39, ответ предназначен Orcinus Orca (orcinus.ru):

Или граватарку прицепил.
Для собственного сайта лучше использовать pavatar, мне кажется (http://pavatar.com/).
Я вот OpenID лишь недавно победил на этом сайте.
Надо было всего лишь delegate сделать :)
А длина строковой переменной вероятно на единичку больше из-за того, что там Ноль в конце?
Не-а, там не сишные строки. Длина переменной просто осталась без изменений, потому что «Гоу» хранит её в другом месте.

bolk (bolknote.ru)
5 мая 2011, 17:41

То есть, если сдвинуть указатель на произвольное место, то там будет какой-нибудь мусор длины 5.

anonymous (инкогнито)
6 мая 2011, 14:54

  А почему выбор пал на полумертвый Go, а не на Scala/Clojure или F#/Haskell? Во всех есть клевые фишки для распределенных/параллельных приложений. Или просто эти языки вам известны? Про ерланг не спрашиваю, потому что простой и неинтересный, мозг им не размять. Спасибо.

bolk (bolknote.ru)
6 мая 2011, 19:36, ответ предназначен anonymous

F# не хочу, мне как-то совершенно не хочется писать софт для Windows, мне чужда эта архитектура и туда не хочется. Мне интересен веб или command line.

Haskell возможно будет следующим.

Clojure — это Лисп, с Лиспом я баловался немного.

О Scala не задумывался, присмотрюсь, единственное, что меня смущает, говорят, что он похож на Java.

anonymous (инкогнито)
6 мая 2011, 20:33

От f# один шаг к его старшему и аскетичному брату окамлу :)
Мне вот clojure не понравилась, но кажется что это из-за нелюбви к лиспам вообще. Но в clojure есть STM, например, только ради этого можно с ним поиграться. Хотя, пожалуй, лучше правда сразу хаскелл.
 
А Scala хороший язык, очень хороший. На голову выше Java.

Буду ждать с нетерпением постов про хаскелл! :)

bolk (bolknote.ru)
7 мая 2011, 00:29, ответ предназначен anonymous

Я так и не сказал почему именно Go.

Во-первых, мне в кои-то веки захотелось что-нибудь компилируемого, я давно ничего не пишу на компилируемых языках (редкое баловство с Си не считается)
Во-вторых, мне очень понравилось как язык легко обходится с многозадачностью
В-третьих, GC в компириемом языке, для меня это что-то необычное
В-четвёртых, Ваня Сагалаев этот язык очень хвалил в разговоре, а у меня, напротив, впечатление было так себе, решил узнать что он в нём нашёл :)
Буду ждать с нетерпением постов про хаскелл! :)
Это не раньше чем пойму, что знаю «Гоу» на крепком среднем уровне. У меня там несколько пробелов даже на уровне языка, не говоря уже о каком-то мышлении в рамках этого языка или понимания кишок, доступных программисту.

anonymous (инкогнито)
7 мая 2011, 12:51

Получается, что в выборе Go решающую роль сыграл разговор с Ваней Сагалаевым :) т.к. по первым трем пунктам ни скала, ни хаскелл не проигрывают :) спасибо, за развернутые ответы!

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)