It takes about 5 minutes to read this article.

This article summarizes some beginner’s mistakes. Author of this article: Mandarin Chinese secret.

NULL

In SQL, NULL is a special value that does not satisfy reflexivity. If column = NULL is used to query data whose value is NULL, it is doomed. So SQL has a separate IS NULL syntax for it.

NaN

IEEE754 states that reflexivity is not satisfied with NaN and only NaN. Suppose NaN stores an element as a key, and that element falls into a black hole, never to be found again. There is also a special method for NaN: IsNaN.

true ! = true

This kind of scene needs to be constructed deliberately, which is usually not encountered. Here’s an example:

func main() { var j1 int8 = 1 var b1 bool = *(*bool)(unsafe.Pointer(&j1)) var j2 int8 = 2 var b2 bool = *(*bool)(unsafe.pointer (&j2)); // unsafe.pointer (&j2)); // unsafe.pointer (&j2)); // unsafe.pointer (&j2)); CMPB println("b2 = true")} else {println("b1! = b2") } }Copy the code

Both b1 and b2 are bool “constructed” through broadening, and behave as bool when paired with the if or other operators that require a bool, and int8 when paired with the == operator or other operators that can be used with both bool and int8.

The root cause is that the CPU only recognizes instructions. There are no data types in this concept, let alone INT8 or bool. The so-called types are high-level language abstractions of CPU instructions. The TESTB operand, for example, is called bool by the compiler; The corresponding operand of CMPB, which the compiler calls int8 or uint8; CPU instructions have a set of signed and unsigned shifts, which the compiler abstracts as signed and unsigned.

Conversely, the compiler restores if to TESTB and == to CMP, so that the same value (the same memory) behaves differently under different instructions.

nil ! = nil

This is a scene beginners will often encounter, even if the big guy if not careful will be pit.

But if you understand the principles behind it, you can laugh it off.

package main
type MyErr struct {
}
func (MyErr) Error() string {
 return "MyErr"
}
func main() {
 var e error = GetErr()
 println(e == nil)
}
func GetErr() *MyErr {
 return nil
}

Copy the code
  • There must be a function call when such an “anomaly” occurs; there is no “anomaly” within a single function. For example, inside GetErr, nil will never appear! = nil geek.

  • This “abnormal” phenomenon must exist in the background of interface Type referencing Concrete Type. If var e error is var e *MyErr, nil will not appear! = nil geek.

  • When interface type refers to Concrete type, it implies convT2I[^1]. The main function of convT2I is to synthesize a new value from the interface type and concrete value.

  • If concrete Type is compared to nil, since type is certain, it can be identified as long as the memory corresponding to value is 0.

  • If interface Type is compared with nil, it can be identified only when the memory corresponding to type and value is 0. [^2] specifies whether type is specified or not.

  • Since nil is equivalent to a literal constant relative to the compiler, there are varying degrees of optimization at each stage. Let’s look at interface Type versus Interface Type and interface Type versus Concrete type to get a feel for how the compiler handles type and value.

  • Comparison between interface Type and interface Type

func walkcompareInterface(n *Node, init *Nodes) *Node { n.Right = cheapexpr(n.Right, init) n.Left = cheapexpr(n.Left, init) eqtab, eqdata := eqinterface(n.Left, n.Right) var cmp *Node if n.Op == OEQ { // x == y cmp = nod(OANDAND, eqtab, eqdata) // eqtab && eqdata } else { // x ! = y eqtab.Op = ONE cmp = nod(OOROR, eqtab, nod(ONOT, eqdata, nil)) // ! eqtab || ! eqdata } return finishcompare(n, cmp, init) }Copy the code
  • interface typeandconcrete type
if n.Left.Type.IsInterface() ! = cheapExpr (n.ight, init) r := cheapexpr(n.ight, init) // If necessary, swap left and right, make sure left is interface and right is concrete. If n.light.type.isInterface () {l, r = r, l} If I have x! = y, with | | connection. eq := n.Op andor := OOROR if eq == OEQ { andor = OANDAND }` // Check for types equal. // For empty interface, this is: // l.tab == type(r) // For non-empty interface, this is: // l.tab ! = nil && l.tab._type == type(r) var eqtype *Node tab := nod(OITAB, l, Rtyp := typename(r.type) // first compare type if l.type.isemptyInterface () {tab.type = types.newptr (types.types [TUINT8]) tab.SetTypecheck(1) eqtype = nod(eq, tab, rtyp) } else { nonnil := nod(brcom(eq), nodnil(), tab) match := nod(eq, Value eqData := nod(eq, ifaceData(n.pos, l, r.type), R) / / use && and | | connect two results expr: = nod (andor eqtype, eqdata) n = finishcompare (n, expr, init) return n}Copy the code

[^0]: For example, an unsigned long integer, C is called unsigned long int, Go is called uint64, and Rust is called u64. [^1]: convT2I is a class of function calls, such as convT2Inoptr, convT2E, convT2Enoptr, convT64, convT32, convT16, etc., including function calls optimized by the compiler. [^2]: For example, var err *MyErr indicates the type, but does not assign a value.


Phase to recommend

  • Build your own photo management artifact with Go

Welcome to polarisxu.

See here, with a thumbs up support!