연산자

연산자 (Operators)

연산자는 피연산자(operand)들을 묶어서 하나의 표현식(expression)으로 만들어 주는 역할을 해요. Go 코드를 읽다 보면 +, *, == 같은 기호들이 어디에 쓰일 수 있고 어디에 못 쓰이는지가 꽤 엄격하게 정해져 있는데, 이 단원이 바로 그 규칙을 정리한 부분이에요.

출처: Go Specification

본문

연산자는 피연산자들을 조합해 표현식을 만든다.

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

비교(comparison) 연산에 대한 이야기는 다른 곳에서 다루고 있어요. 그 외의 이항 연산자들에서는, 연산이 시프트(shift)를 포함하거나 untyped 상수(constant)가 관여하지 않는 한 두 피연산자의 타입이 동일(identical)해야 해요. 상수만 관여하는 연산에 대해서는 상수 표현식(constant expressions) 단원을 보면 돼요.

시프트 연산을 제외하고, 한쪽 피연산자가 untyped 상수이고 다른 쪽이 아니라면, 그 상수는 다른 피연산자의 타입으로 암시적으로 변환(converted)돼요.

시프트 표현식에서 오른쪽 피연산자는 정수 타입(integer type)이어야 하거나(Go 1.13), uint 타입의 값으로 표현 가능(representable)한 untyped 상수여야 해요.

var a [1024]byte
var s uint = 33

// The results of the following examples are given for 64-bit ints.
var i = 1<<s                   // 1 has type int
var j int32 = 1<<s             // 1 has type int32; j == 0
var k = uint64(1<<s)           // 1 has type uint64; k == 1<<33
var m int = 1.0<<s             // 1.0 has type int; m == 1<<33
var n = 1.0<<s == j            // 1.0 has type int32; n == true
var o = 1<<s == 2<<s           // 1 and 2 have type int; o == false
var p = 1<<s == 1<<33          // 1 has type int; p == true
var u = 1.0<<s                 // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0           // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0           // illegal: 1 has type float64, cannot shift
var v1 float32 = 1<<s          // illegal: 1 has type float32, cannot shift
var v2 = string(1<<s)          // illegal: 1 is converted to a string, cannot shift
var w int64 = 1.0<<33          // 1.0<<33 is a constant shift expression; w == 1<<33
var x = a[1.0<<s]              // panics: 1.0 has type int, but 1<<33 overflows array bounds
var b = make([]byte, 1.0<<s)   // 1.0 has type int; len(b) == 1<<33

// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0<<s            // 1.0 has type int; mm == 0
var oo = 1<<s == 2<<s          // 1 and 2 have type int; oo == true
var pp = 1<<s == 1<<33         // illegal: 1 has type int, but 1<<33 overflows int
var xx = a[1.0<<s]             // 1.0 has type int; xx == a[0]
var bb = make([]byte, 1.0<<s)  // 1.0 has type int; len(bb) == 0

연산자 우선순위 (Operator precedence)

단항 연산자가 가장 높은 우선순위를 가져요. ++-- 연산자는 문(statement)을 이루지 표현식(expression)을 이루지 않기 때문에, 연산자 계층(hierarchy) 밖에 있어요. 그 결과로 문 *p++(*p)++과 같아요.

이항 연산자에는 다섯 단계의 우선순위가 있어요. 곱셈 연산자가 가장 강하게 결합하고, 그다음이 덧셈 연산자, 비교 연산자, &&(논리 AND), 마지막이 ||(논리 OR) 순서예요:

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

우선순위가 같은 이항 연산자들은 왼쪽에서 오른쪽으로 결합해요. 예를 들어 x / y * z(x / y) * z와 같아요.

+x                         // x
42 + a - b                 // (42 + a) - b
23 + 3*x[i]                // 23 + (3 * x[i])
x <= f()                   // x <= f()
^a >> b                    // (^a) >> b
f() || g()                 // f() || g()
x == y+1 && <-chanInt > 0  // (x == (y+1)) && ((<-chanInt) > 0)

더 알아보기