Linux講座にようこそ。このページは「C言語プログラミング入門 - 第14章.ライブラリ関数 - その他のライブラリ」です。

C言語プログラミング入門

14. ライブラリ関数(36/36) - その他のライブラリ(4/4)

14.50 可変個引数操作関数

可変個引数の操作はva_start関数・va_arg関数・va_end関数で行うことが出来ます。これらの関数を使う場合は次の点に注意が必要です。

  • 引数のうち、最低1つは固定引数(通常の引数)が必要です。この引数により、受取側(呼び出された関数)で可変部分の数と内容が分かるようにします。
  • va_start関数・va_arg関数・va_end関数は同じ関数内で、この順番で呼び出さなければなりません。

これらの関数はマクロとして定義されている可能性がありますが、特に意識する必要はありませんので、ここではすべて関数という表現をしています。

14.50.1 va_start関数

va_start関数は可変個引数を取り扱うための引数リストの初期化(準備)を行ないます。

【表14-8-12】 va_start関数
形式#include <stdarg.h>
void va_start(va_list ap, last);
返り値ありません。
引数
va_list ap
引数リストを格納する領域(引数リストオブジェクト)を指定します。以後、使用するva_arg関数とva_end関数では、この引数リストを使用して引数の操作を行います。
last
固定引数の最後の引数を指定します。

14.50.2 va_arg関数

va_arg関数は引数リストから値を取得します。この関数を繰り返し呼び出すことにより、引数の値を順番に取得する事が出来ます。なお、va_start関数直後の呼び出し(最初の呼び出し)時には、最後の固定引数(va_start関数の第2引数)の次の引数の値を取得します。

【表14-8-13】 va_arg関数
形式#include <stdarg.h>
type va_arg(va_list ap, type);
返り値引数の値を返します。
引数
va_list ap
va_start関数で作成した引数リストオブジェクトを指定します。
type
取得する引数の型名を指定します。*を付けて指定するとポインタとして取得出来ます。

14.50.3 va_end関数

va_end関数は引数リストオブジェクトをリセットします。リセットした引数リストオブジェクトは、va_start関数で初期化することにより再使用可能です。

【表14-8-14】 va_end関数
形式#include <stdarg.h>
void va_end(va_list ap);
返り値ありません。
引数
va_list ap
va_start関数で作成した引数リストオブジェクトを指定します。

14.50.4 例題

肥満度(BMI)の計算を行い結果を表示します。肥満度が異常の場合は「再検査要です」のメッセージを付け加えます。結果の表示はVarMessage関数で行いますが、肥満度が正常な場合と異常の場合でメッセージが異なりますので、可変個引数の関数にしています。なお、第1引数は固定引数で、可変引数の型名を文字列で指定します。(sは文字列、fはdouble型を表します)

  1. #include <stdio.h>
  2. #include <stdarg.h>
  3. /* 肥満状態を判定するための定数 */
  4. #define BMI_LIMIT_UNDER    18.5
  5. #define BMI_LIMIT_OVER     25.0
  6.  
  7. int main(void)
  8. {
  9.     /* 結果の表示間数 */
  10.     void VarMessage(char *Format, ...);
  11.  
  12.     char    cont;
  13.     double  weight;
  14.     double  height;
  15.     double  bmi;
  16.  
  17.     printf("肥満度(BMI)の計算を行います。\n");
  18.     cont = 'Y';
  19.     while(cont == 'Y' || cont == 'y')
  20.     {
  21.         printf("体重(kg)と身長(m)を入力してください ==> ");
  22.         scanf("%lf%lf", &weight, &height);
  23.         /* 肥満度(BMI) = 体重(kg) / 身長(m) / 身長(m) */
  24.         bmi = weight / height / height;
  25.         if(bmi < BMI_LIMIT_UNDER || bmi > BMI_LIMIT_OVER)
  26.         {
  27.             /* BMIが異常 */
  28.             VarMessage("sfss", "BMIは", bmi, "です。", "再検査要です。");
  29.         }
  30.         else
  31.         {
  32.             VarMessage("sfs", "BMIは", bmi, "です。");
  33.         }
  34.  
  35.         printf("繰り返し肥満度の計算をしますか(Y/N) ==> ");
  36.         scanf("%*c%c", &cont);
  37.     }
  38.  
  39.     return 0;
  40. }
  41.  
  42. /* 結果の表示関数 */
  43. void VarMessage(char *p_format, ...)
  44. {
  45.     va_list ap;
  46.  
  47.     /* 可変引数リストの初期化 */
  48.     va_start(ap, p_format);
  49.  
  50.     while (*p_format) {
  51.         switch (*p_format) {
  52.             case 'f':
  53.                 /* double型の表示 */
  54.                 printf("%.2f", va_arg(ap, double));
  55.                 break;
  56.             case 's':
  57.                 /* 文字列の表示 */
  58.                 printf("%s", va_arg(ap, char *));
  59.                 break;
  60.             default:
  61.                 printf("引数の型名が不当です。\n");
  62.         }
  63.  
  64.         ++p_format;
  65.     }
  66.     printf("\n");
  67.  
  68.     /* 可変引数リストのリセット */
  69.     va_end(ap);
  70.  
  71.     return;
  72. }
$ ./ex14_8_5.prg
肥満度(BMI)の計算を行います。
体重(kg)と身長(m)を入力してください ==> 63.5 1.75
BMIは20.73です。
繰り返し肥満度の計算をしますか(Y/N) ==> y
体重(kg)と身長(m)を入力してください ==> 63.5 1.56
BMIは26.09です。再検査要です。
繰り返し肥満度の計算をしますか(Y/N) ==> n
$
2行目
可変個引数関数を使いますのでstdarg.hファイルを取り込みます。
10行目
可変個引数のVarMessage関数のプロトタイプ宣言です。第1引数は固定引数で、可変引数の型名を文字列で指定します。ちなみに、sは文字列、fはdouble型を表します。
28行目
VarMessage関数で結果を表示します。可変引数は「文字列・double型・文字列・文字列」の4つです。
32行目
VarMessage関数で結果を表示します。可変引数は「文字列・double型・文字列」の3つです。
48行目
引数リストを初期化(準備)します。
54行目
double型の引数の値を取得します。
58行目
文字列(char *)の引数の値を取得します。
69行目
引数リストをリセットします。

14.51 構造体関数

14.51.1 offsetof関数

offsetof関数は構造体メンバーの位置(構造体の先頭からのバイト数)を取得します。型名の長さは処理系により異なりますのでメンバーの位置も処理系により異なる可能性があります。また、通常、各メンバは2あるいは4の整数倍の位置に配置されますが、これも処理系やコンパイルオプションにより異なる可能性があります。

【表14-8-15】 offsetof関数
形式#include <stddef.h>
size_t offsetof(type, member);
返り値構造体メンバーの位置を先頭からのバイト数で返します。
引数
type
構造体を指定します。
member
構造体メンバーを指定します。

14.51.2 例題

BMI_REC構造体とLIMIT型のメンバーの位置を表示します。

  1. #include <stdio.h>
  2. #include <stddef.h>
  3. /* 肥満度データを格納する構造体 */
  4. struct BMI_REC
  5. {
  6.     int     number;        /* 番号 */
  7.     char    judge;         /* 肥満状態 */
  8.     short   age;           /* 年齢 */
  9.     float   weight;        /* 体重 */
  10.     double  height;        /* 身長 */
  11.     char    sex;           /* 性別 */
  12.     double  bmi;           /* 肥満度 */
  13. };
  14.  
  15. /* 肥満状態を判定するための値 */
  16. typedef struct
  17. {
  18.     double  limit_under;   /* 低 */
  19.     double  limit_over;    /* 高 */
  20. } LIMIT;
  21.  
  22. int main(void)
  23. {
  24.     printf("BMI_REC構造体の大きさ : %d\n", sizeof(struct BMI_REC));
  25.     printf("number  (int)         : %d\n", offsetof(struct BMI_REC, number));
  26.     printf("judge   (char)        : %d\n", offsetof(struct BMI_REC, judge));
  27.     printf("age     (short)       : %d\n", offsetof(struct BMI_REC, age));
  28.     printf("weight  (float)       : %d\n", offsetof(struct BMI_REC, weight));
  29.     printf("height  (double)      : %d\n", offsetof(struct BMI_REC, height));
  30.     printf("sex     (char)        : %d\n", offsetof(struct BMI_REC, sex));
  31.     printf("bmi     (double)      : %d\n", offsetof(struct BMI_REC, bmi));
  32.  
  33.     printf("\nLIMIT型の大きさ     : %d\n", sizeof(LIMIT));
  34.     printf("limit_under  (double) : %d\n", offsetof(LIMIT, limit_under));
  35.     printf("limit_over   (double) : %d\n", offsetof(LIMIT, limit_over));
  36.     return 0;
  37. }
$ ./ex14_8_6.prg
BMI_REC構造体の大きさ : 32
number  (int)         : 0
judge   (char)        : 4
age     (short)       : 6
weight  (float)       : 8
height  (double)      : 12
sex     (char)        : 20
bmi     (double)      : 24

LIMIT型の大きさ     : 16
limit_under  (double) : 0
limit_over   (double) : 8
$
2行目
offsetof関数を使いますのでstddef.hファイルを取り込みます。
4行目
BMI_REC構造体を宣言します。
20行目
構造体をtypedefでLIMIT型として宣言します。
24行目
構造体全体の大きさをsizeof演算子で求めます。
25行目
offsetof関数でBMI_REC構造体のnumberメンバーの位置を求めます。
34行目
offsetof関数でLIMIT型のlimit_underメンバーの位置を求めます。