C 구조체 선언

C 구조체 선언 (struct)

lib 안에서 struct를 선언하면 C 구조체를 선언하는 셈입니다:

lib C
  # In C:
  #
  #  struct TimeZone {
  #    int minutes_west;
  #    int dst_time;
  #  };
  struct TimeZone
    minutes_west : Int32
    dst_time : Int32
  end
end

같은 타입의 필드를 여러 개 묶어서 지정할 수도 있어요:

lib C
  struct TimeZone
    minutes_west, dst_time : Int32
  end
end

재귀 구조체도 예상한 그대로 동작합니다:

lib C
  struct LinkedListNode
    prev, _next : LinkedListNode*
  end

  struct LinkedList
    head : LinkedListNode*
  end
end

lib 안에 정의된 구조체는 모듈처럼 다른 lib 구조체 내부에 include 할 수 있어요. 예를 들면:

lib Lib
  struct Foo
    x : Int32
    y : Int16
  end

  struct Bar
    include Foo
    z : Int8
  end
end

Lib::Bar.new # => Lib::Bar(@x=0, @y=0, @z=0)

구조체의 인스턴스를 만들려면 new를 씁니다:

tz = C::TimeZone.new

이러면 구조체가 스택(stack)에 할당돼요.

C 구조체는 모든 필드가 "0"으로 초기화된 상태로 시작합니다. 정수와 부동소수점은 0에서, 포인터는 주소 0에서 시작하는 식이죠.

이런 초기화를 피하고 싶다면 uninitialized를 쓸 수 있어요:

tz = uninitialized C::TimeZone
tz.minutes_west # => some garbage value

프로퍼티를 설정하고 읽을 수도 있습니다:

tz = C::TimeZone.new
tz.minutes_west = 1
tz.minutes_west # => 1

할당하는 값이 프로퍼티의 타입과 정확히 같지 않다면, to_unsafe가 시도됩니다.

명명 인자(named arguments)와 비슷한 문법으로 일부 필드를 초기화할 수도 있어요:

tz = C::TimeZone.new minutes_west: 1, dst_time: 2
tz.minutes_west # => 1
tz.dst_time     # => 2

C 구조체는 함수나 메서드에 값(복사본)으로 전달되고, 메서드에서 반환될 때도 값으로 반환됩니다:

def change_it(tz)
  tz.minutes_west = 1
end

tz = C::TimeZone.new
change_it tz
tz.minutes_west # => 0

구조체 필드 타입에 쓰이는 표기는 타입 문법(type grammar)을 참고하세요.

출처: Crystal 공식 문서

더 알아보기 (Learn more)

  • lib — C 라이브러리 바인딩의 시작
  • union — C 공용체 선언
  • to_unsafe — 포인터로 변환되는 규칙