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型を表します)
#include <stdio.h>
#include <stdarg.h>
/* 肥満状態を判定するための定数 */
#define BMI_LIMIT_UNDER 18.5
#define BMI_LIMIT_OVER 25.0
int main(void)
{
/* 結果の表示間数 */
void VarMessage(char *Format, ...);
char cont;
double weight;
double height;
double bmi;
printf("肥満度(BMI)の計算を行います。\n");
cont = 'Y';
while(cont == 'Y' || cont == 'y')
{
printf("体重(kg)と身長(m)を入力してください ==> ");
scanf("%lf%lf", &weight, &height);
/* 肥満度(BMI) = 体重(kg) / 身長(m) / 身長(m) */
bmi = weight / height / height;
if(bmi < BMI_LIMIT_UNDER || bmi > BMI_LIMIT_OVER)
{
/* BMIが異常 */
VarMessage("sfss", "BMIは", bmi, "です。", "再検査要です。");
}
else
{
VarMessage("sfs", "BMIは", bmi, "です。");
}
printf("繰り返し肥満度の計算をしますか(Y/N) ==> ");
scanf("%*c%c", &cont);
}
return 0;
}
/* 結果の表示関数 */
void VarMessage(char *p_format, ...)
{
va_list ap;
/* 可変引数リストの初期化 */
va_start(ap, p_format);
while (*p_format) {
switch (*p_format) {
case 'f':
/* double型の表示 */
printf("%.2f", va_arg(ap, double));
break;
case 's':
/* 文字列の表示 */
printf("%s", va_arg(ap, char *));
break;
default:
printf("引数の型名が不当です。\n");
}
++p_format;
}
printf("\n");
/* 可変引数リストのリセット */
va_end(ap);
return;
}
$ ./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型のメンバーの位置を表示します。
#include <stdio.h>
#include <stddef.h>
/* 肥満度データを格納する構造体 */
struct BMI_REC
{
int number; /* 番号 */
char judge; /* 肥満状態 */
short age; /* 年齢 */
float weight; /* 体重 */
double height; /* 身長 */
char sex; /* 性別 */
double bmi; /* 肥満度 */
};
/* 肥満状態を判定するための値 */
typedef struct
{
double limit_under; /* 低 */
double limit_over; /* 高 */
} LIMIT;
int main(void)
{
printf("BMI_REC構造体の大きさ : %d\n", sizeof(struct BMI_REC));
printf("number (int) : %d\n", offsetof(struct BMI_REC, number));
printf("judge (char) : %d\n", offsetof(struct BMI_REC, judge));
printf("age (short) : %d\n", offsetof(struct BMI_REC, age));
printf("weight (float) : %d\n", offsetof(struct BMI_REC, weight));
printf("height (double) : %d\n", offsetof(struct BMI_REC, height));
printf("sex (char) : %d\n", offsetof(struct BMI_REC, sex));
printf("bmi (double) : %d\n", offsetof(struct BMI_REC, bmi));
printf("\nLIMIT型の大きさ : %d\n", sizeof(LIMIT));
printf("limit_under (double) : %d\n", offsetof(LIMIT, limit_under));
printf("limit_over (double) : %d\n", offsetof(LIMIT, limit_over));
return 0;
}
$ ./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メンバーの位置を求めます。