On Thu, Aug 23, 2018 at 6:29 PM Nevin Liber <nevin@eviloverlord.com> wrote:
It was still kinda frowned upon in K&R1:  "It is the responsibility of the programmer to keep track of what type is currently stored in a union; the results are machine dependent if something is stored as one type and extracted as another."

No.  It was not frowned upon, it was widely used.  The message was just just Dennis and Brian giving you fair warning. Remember K&R1 came out approx the same time as 7th edition [I saw the proofs for the book with UNIX/TS which was a little earlier, but pretty much the same kernel].

From V7th editions buf.h (which I show a date of May 5, 1979) and Ron is correct void*, caddr_t, daddr_t, dev_t and the like; were all 7th edition-isms that BSD picked up)
...
struct buf
{
        int     b_flags;                /* see defines below */
        struct  buf *b_forw;            /* headed by d_tab of conf.c */
        struct  buf *b_back;            /*  "  */
        struct  buf *av_forw;           /* position on free list, */
        struct  buf *av_back;           /*     if not BUSY*/
        dev_t   b_dev;                  /* major+minor device name */
        unsigned b_bcount;              /* transfer count */
        union {
            caddr_t b_addr;             /* low order core address */
            int *b_words;               /* words for clearing */
            struct filsys *b_filsys;    /* superblocks */
            struct dinode *b_dino;      /* ilist */ls -l buf.h
            daddr_t *b_daddr;           /* indirect block */
        } b_un;
        daddr_t b_blkno;                /* block # on device */
        char    b_xmem;                 /* high order core address */
        char    b_error;                /* returned after I/O */
        unsigned int b_resid;           /* words not transferred after error */
};
...