先の記事で可変引数について説明した。
軽くまとめると、可変引数とは、任意の型、数の引数を受け取る為の仕組みだ。
前回はこの仕組みを利用して複数の値を関数に渡し、平均を求めた。
#include <stdarg.h>
float avg(int amnt, ...){
va_list list;
va_start(list, amnt);
float sum = 0;
for(int i = 0; i < amnt; i++){
sum += va_arg(list, int);
}
va_end(list);
return sum/amnt;
}
まずはこの関数を改造していく。
基本的に平均を求めたいような値は配列に格納されているだろう。 こんな渡し方はしない。
そこでまずは配列で渡し、平均値を返す関数にする。
#include <stdio.h>
float avg(int amnt, int* array){
int sum = 0;
for(int i = 0; i < amnt; i++){
sum += array[i];
}
float ret = sum / amnt ;
return ret;
}
これで整数型の配列と配列の長さを受け取り、平均値を実数型で返す関数が出来た。
一つのプログラムの中ではこれで充分だが、今後の事を考えてモジュール化したい。
(この程度なら毎回書いてもそこまで苦では無いが)
モジュール化するにあたり、対応出来る型を増やしたい。
が、型毎に関数を分けるような事はしたくない。
そこで、表題にあるように任意の型を返す関数を作りたい。
stdarg
には、前回のように複数の引数を受け取る以外にも任意の型を受け取れる性質がある。
そこで、stdarg
を用いて先の関数を改造する。
#include <stdarg.h>
#include <stsio.h>
float avg(int amnt, ...){
va_list list;
va_start(list, anmt);
int sum;
int* array = va_arg(list, int*);
va_end(list);
for(int i = 0; i < amnt; i++){
sum += array[i];
}
float ret = sum / amnt;
return ret;
}
こんな感じだろうか。
しかし
これでは先の関数と何ら変わらない。
引数で型を指定するようにしてみる。
#include <stdarg>
double avg(char fmt, int amnt, ...){
va_list list;
va_start(list, amnt);
if(fmt == 'd'){
int* array = va_arg(list, int*);
int sum = 0;
for(int i = 0; i < amnt; i++){
sum += array[i];
}
double ret = sum / amnt;
va_end(list);
return ret;
}else if(fmt == 'f'){
float* array = va_arg(list, float*) ;
float sum = 0.0;
for(int i = 0; i < amnt; i++){
sum += array[i];
}
double ret = sum / amnt;
va_end(list);
return ret;
}else{
double* array = va_arg(list, double*);
double sum = 0.0;
for(int i = 0; i < amnt; i++){
sum += array[i];
}
double ret = sum / amnt;
va_end(list);
return ret;
}
}
これで複数の型の配列を一つの関数で処理できる。
こんな感じで、戻り値の型も可変にしてみたい。
先の例のように平均値を求めたい場合は実数型で良いが、最大値や最小値を求めたい場合はそのままの型で取得したい。
そこで、今回はvoid*
を用いて任意の型を返してみる。
printfを参考にフォーマットを指定し、対応した型で返す。
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void* max(const char* fmt, int amnt, ...){
va_list list;
va_start(list, amnt);
if(strncmp(fmt, "d", 1) == 0){
int* array = va_arg(list, int*);
int max = array[0];
for(int m = 1; m < amnt; m++){
if(max < array[m]){
max = array[m];
}
}
int* ret = &max;
return (void*)ret;
}else if(strncmp(fmt, "lf", 2) == 0){
double* array = va_arg(list, double*);
double max = array[0];
for(int i = 1; i < amnt; i++){
if(max < array[i]){
max = array[i];
}
}
double* ret = &max;
return (void*)ret;
}else if(strncmp(fmt, "lld", 3) == 0){
long long int* array = va_arg(list, long long int*);
long long int max = array[0];
for(int i = 1; i < amnt; i++){
if(max < array[i]){
max = array[i];
}
}
long long int* ret = &max;
return (void*)ret;
}else{
return NULL;
}
}
呼び出す側ではこうなる
void main(){
int a[5] = {1, 2, 9, 5, 4};
int amax = *(int*)max("d", 5, a);
double d[5] = {1.0, 2.0, 3.0, 4.0, 5.0};
double dmax = *(double*)max("lf", 5, d);
long long int l[5] = {1, 2, 3, 4, 5};
long long int lmax = *(long long int*)max("lld", 5, l);
printf("%d %lf %lld\n", amax, dmax, lmax);
}
void*
型にされているポインタを必要な型のポインタにして参照している。
さて、これで一応複数の型には対応出来た。
しかし、これでは満足出来ない。
記事にて、型を受け取るマクロを作った。
せっかくなのでこの関数もマクロにしてしまおう。
#include <stdio.h>
#define ARRAY_LEN(array) (sizeof(array) / sizeof(typeof(*array)))
#define max(array, ret) \
do{ \
int i = 1; \
*ret = array[0]; \
for(; i < ARRAY_LEN(array); i++){ \
if(*ret < array[i]){ \
*ret = array[i]; \
} \
} \
}while(0)
void main(){
int array[5] = {1, 2, 24, 4, 4};
int r = 0;
max(array, &r);
printf("%d\n", r);
}