memsetは、第一引数に対して、第三引数ぶん第二引数で埋めるということをする。
なので最初の書き方だとobjを23個のyで埋めるということをする。

一方、「文字列」はヌル文字で終わるものをいう。obj.hoge_aを文字列として
扱いたいのであれば、obj.hoge_a[7] = '\0'としなければならない。
これは、memset(&obj, 'y', sizeof obj - 1)では実現できない。
なぜなら、memsetはobjの先頭(=obj.hoge_aの先頭)から23個'y'を設定するので
obj.hoge_a[7]も'y'になってしまうからだ。

結論としては、期待する結果を得たい場合には以下のようにする必要がある。
今回'\0'を設定しなくてもよかったのは「たまたま」で、普通はゴミが入ったりする。

memset(obj.hoge_a, 'y', 7);
obj.hoge_a[7] = '\0';
memset(obj.hoge_b, 'y', 7);
obj.hoge_b[7] = '\0';
memset(obj.hoge_c, 'y', 7);
obj.hoge_c[7] = '\0';

まとめて書くこともできる。
memset(&obj, 'y', sizeof obj);
obj.hoge_a[7] = '\0';
obj.hoge_b[7] = '\0';
obj.hoge_c[7] = '\0';

'7'が気にいらなければ、次のようにも書ける。
obj.hoge_a[sizeof obj.hoge_a - 1] = '\0';