The expression ptr has type int (*)[2][3] (pointer to 2-element array of 3-element array of int) - it is a pointer type, so it's however big a pointer type is on your system (in this case, 8 bytes).
The expression *ptr has type int [2][3] - it's a 2x3 array of int, meaning it's big enough to store 6 int objects. An int is 4 bytes wide on your system, for a total size of 24 bytes.
If the operand of sizeof is an expression instead of a type name, it goes by the type of the expression. For example, the expression 1 has type int, the expression 2 has type int, and the expression 1 + 2 has type int, so
sizeof 1 == sizeof 2 == sizeof (1 + 2) == sizeof (int)
Remember that sizeof is an operator, not a function - you only need to use parentheses if the operand is a type name (like (int)) or if the operand is an expression containing operators of lower precedence. For example
sizeof 1 + 2
will be parsed as
(sizeof 1) + 2
because the arithmetic + operator has lower precedence than the unary sizeof operator, which is probably not what you want. However, expressions like
sizeof *foo[N]
will be parsed as
sizeof (*(foo[N]))
so parentheses aren't necessary in that case. Some people will advise you to always use parentheses regardless, which isn't bad advice; eventually you'll learn when they aren't necessary and can be comfortable leaving them out.
sizeof(*ptr)gives you the size of whatptrpoints to, which is an array of 2×3 ints. So it should be the same asint a[2][3]; sizeof(a), as it looks like it is.