Lio con tamaño variables char

Efectivamente, veo que lo entiendes y me alegro de no haberte liado.
Si vas a acceder a esas posiciones de memoria para escribir o leer un byte, un char o un int, lo lógico es que realices la asignación de dicha dirección a un puntero del tipo adecuado (en algunos compiladores más permisivos te permitiría usar el mismo puntero, dándote un warning, pero eso es un arma de doble filo). Por otro lado, el hecho de que condenses el código de conversión en una sola línea, no significa que el código resultante vaya a ser necesariamente de menor tamaño o más rápido, pues el compilador internamente va a tener que crear una variable temporal, así que por legibilidad del código, quizás sea mejor declarar el puntero a byte (al ser local, también es una variable temporal) y asignarle la dirección del char (byte dato3*=(byte*) pi3), aunque tu forma condensada es correcta.
En la instrucción byte *Dato3 = (byte *) &pi3;, te sobra el operador de indirección (&), ya que pi3 es ya literalmente un puntero a char que apunta a esos 20 bytes reservados. Puedes hacer la prueba y verás que la instrucción char* copiacadena=pi3 no te da ningún error. Lo correcto sería, entonces, byte *Dato3 = (byte *) pi3;.
RECUERDA: un array es un puntero asignado a ese array. Lo que pasa es que si utilizas un puntero seguido del índice de elemento, te devuelve el valor de ese elemento, es decir pi3[0] es lo mismo que decir pi3. Por ejemplo, en el caso que te proponía __char copiacadena=pi3__, podría obtener el caracter en la 3ª posición de la cadena, tanto usando copiacadena[2] como pi3[2] y rizando el rizo, *(copiacadena + 2) o *(pi3 + 2).
... Y vuelvo a cortar, que estoy yéndome más allá de lo que preguntabas e igual te lío.

EDITO: sólo para decirte que con el int sí que tienes que usar la indirección & porque el int, al contrario que un array, no es un puntero. Sí lo sería si hubieras declarado int pi2[1], y entonces la asignación byte *Dato2 = (byte *) pi2 sería igualmente correcta.
Saludos