typeofで型を参照する

 式の型を参照する方法の一つにtypeofがあります.このキーワードを使うときの構文はsizeofの構文に似ていますが,意味論的には,typedefにより定義された型名のような働きをします.

 typeofを使う宣言の意味と,なぜそれが役に立つことがあるのかを理解するために,これを次のようなマクロで書き換えてみましょう.

  #define pointer(T) typeof(T *)
  #define array(T, N) typeof(T [N])

 すると,上の宣言は次のように書き換えることができます.

  array (pointer (char), 4) y;

 この場合,array (pointer (char), 4)は,charへの四つのポインタから構成される配列の型です(リスト8リスト9).実行結果を以下に示します.

〔リスト8〕typeofでマクロを作ったソースtest47.c)
#include <stdio.h>
#define pointer(T)  typeof(T *)
#define array(T, N) typeof(T [N])
int main(void)
{
 int ix;
 array (pointer (char), 10) char_p;
 array (pointer (long), 10) long_p;
 for (ix=0;ix<10;ix++)
 {
  char_p[ix] = 'a'+ix;
  long_p[ix] = ix * 1000000;
 }
 for (ix=0;ix<10;ix++)
 {
  printf("char_p[%d] = %d\n",ix,char_p[ix]);
 }
 for (ix=0;ix<10;ix++)
 {
  printf("long_p=[%d] = %d\n",ix,long_p[ix]);
 }
 return;
}

〔リスト9〕展開されたアセンブラリストtest47.s)
 .file "test47.c"
 .version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
 .string "char_p[%d] = %d\n"
.LC1:
 .string "long_p=[%d] = %d\n"
.text
 .align 4
.globl main
 .type  main,@function
main:
 pushl %ebp
 movl %esp,%ebp
 subl $96,%esp
 pushl %esi
 pushl %ebx
 nop
 movl $0,-4(%ebp)
 .p2align 4,,7
.L3:
 cmpl $9,-4(%ebp)
 jle .L6
 jmp .L4
 .p2align 4,,7
.L6:
 movl -4(%ebp),%eax
 movl %eax,%edx
 leal 0(,%edx,4),%eax
 leal -44(%ebp),%edx
 movl -4(%ebp),%ecx
 addl $97,%ecx
 movl %ecx,(%eax,%edx)
 movl -4(%ebp),%eax
 movl %eax,%edx
 leal 0(,%edx,4),%eax
 leal -84(%ebp),%edx
 movl -4(%ebp),%ecx
 movl %ecx,%ebx
 movl %ebx,%esi
 sall $5,%esi
 subl %ecx,%esi
 movl %esi,%ebx
 sall $6,%ebx
 subl %esi,%ebx
 sall $3,%ebx
 addl %ecx,%ebx
 movl %ebx,%ecx
 sall $6,%ecx
 movl %ecx,(%eax,%edx)
.L5:
 incl -4(%ebp)
 jmp .L3
 .p2align 4,,7
.L4:
 nop
 movl $0,-4(%ebp)
 .p2align 4,,7
.L7:
 cmpl $9,-4(%ebp)
 jle .L10
 jmp .L8
 .p2align 4,,7
.L10:
 addl $-4,%esp
 movl -4(%ebp),%eax
 movl %eax,%edx
 leal 0(,%edx,4),%eax
 leal -44(%ebp),%edx
 movl (%eax,%edx),%eax
 pushl %eax
 movl -4(%ebp),%eax
 pushl %eax
 pushl $.LC0
 call printf
 addl $16,%esp
.L9:
 incl -4(%ebp)
 jmp .L7
 .p2align 4,,7
.L8:
 nop
 movl $0,-4(%ebp)
 .p2align 4,,7
.L11:
 cmpl $9,-4(%ebp)
 jle .L14
 jmp .L12
 .p2align 4,,7
.L14:
 addl $-4,%esp
 movl -4(%ebp),%eax
 movl %eax,%edx
 leal 0(,%edx,4),%eax
 leal -84(%ebp),%edx
 movl (%eax,%edx),%eax
 pushl %eax
 movl -4(%ebp),%eax
 pushl %eax
 pushl $.LC1
 call printf
 addl $16,%esp
.L13:
 incl -4(%ebp)
 jmp .L11
 .p2align 4,,7
.L12:
 jmp .L2
 .p2align 4,,7
.L2:
 leal -104(%ebp),%esp
 popl %ebx
 popl %esi
 movl %ebp,%esp
 popl %ebp
 ret
.Lfe1:
 .size  main,.Lfe1-main
 .ident "GCC: (GNU) 2.95.3 20010315 (release)"

  $ ./test47
  char_p[0] = 97
  char_p[1] = 98
  char_p[2] = 99
  char_p[3] = 100
  char_p[4] = 101
  char_p[5] = 102
  char_p[6] = 103
  char_p[7] = 104
  char_p[8] = 105
  char_p[9] = 106
  long_p=[0] = 0
  long_p=[1] = 1000000
  long_p=[2] = 2000000
  long_p=[3] = 3000000
  long_p=[4] = 4000000
  long_p=[5] = 5000000
  long_p=[6] = 6000000
  long_p=[7] = 7000000
  long_p=[8] = 8000000
  long_p=[9] = 9000000
  $

 リスト9のアセンブラソースを見てわかるとおり,ポインタの長さ分,領域が10個ずつ取られているのがわかります.GCCの標準機能で同等の機能を実装することはできません.

拡張されたlvalue

 複合式,条件式,および,キャストは,そこに含まれる式がlvalueであるかぎり,lvalueとして使うことができます.これは,アドレスを取ったり,値を格納することができるということを意味しています.

 複合式の中の最後の式がlvalueであれば,複合式に対して値を代入することができます.以下に示す二つの式は同等です.

  (a, b) += 5
  a, (b += 5)

 同様に,複合式のアドレスを取ることもできます.次の二つの式は同等です.

  &(a, b)
  a, &b

 条件式は,その型がvoidではなく,かつ,真のときに分岐する部分と偽のときに分岐する部分がともに正当なlvalueであれば,正当なlvalueです.たとえば,以下の二つの式は同等です.

  (a ? b : c) = 5
  (a ? b = 5 : (c = 5))

 見てのとおり言語仕様が違うのかと思うくらいに拡張されています.ただこれは,使用してもメリットはないと思います.後で混乱をまねくでしょう.GCCの標準機能を使って一行で同等の機能を実装することはできません.

*          *

 次回は,続けてGNU Cの拡張機能について詳細に説明と検証を行う予定です.

Copyright 2003 岸 哲夫

Copyright 1997-2017 CQ Publishing Co.,Ltd.

 

NEW記事内インデックス    連載インデックスはこちら   Interfaceのトップ
GCCの導入に際して
◆C言語におけるGCCの拡張機能
 式の中の文と宣言
 ローカルに宣言されたラベル/値としてのラベル/入れ子になった関数/
  関数呼び出しを構築する /型の代入
 typeofで型を参照する/拡張されたlvalue