연산자
연산자 (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)