ラベル C++ の投稿を表示しています。 すべての投稿を表示
ラベル C++ の投稿を表示しています。 すべての投稿を表示

2013年3月11日月曜日

C言語の関数中における文字列の交換

文字列を関数の仮引数として渡して、その交換を行う際には多少注意が要る。以下の例では、char *型の変数にポインタを通じてアクセスするので、呼び出す関数ではchar **型にする必要がある。関数に渡すものはアドレスなので、実引数はchar *型変数のアドレスとする必要がある(つまり&をつける)。比較のため、整数型変数を交換する場合と共にそのコードを示す。

#include <stdio.h>

void swap_int(int *a, int *b){
 int tmp = *a;
 *a = *b;
 *b = tmp;
}

/* 仮引数は*aaではなく**aaになることに注意 */
void swap_str(char **aa, char **bb){
 char *tmp = *aa;
 *aa = *bb;
 *bb = tmp;
}

int main(void){
 int x = 10;
 int y = 20;
 char *a = "hello";
 char *b = "world";

 printf("x = %d, y = %d\n", x, y); /* x = 10, y = 20 */
 swap_int(&x, &y);
 printf("x = %d, y = %d\n", x, y); /* x = 20, y = 10 */

 printf("a = %s, b = %s\n", a, b); /* a = hello, b = world */
 swap_str(&a, &b);
 printf("a = %s, b = %s\n", a, b); /* a = world, b = hello */

 return 0;
}

2009年12月22日火曜日

RとC言語で関数ポインタ:書き直し

以前書いたソースコードの焼き直しをしてみました。
今度は中央値の計算も実装してみました。

#include <stdio.h>
#include <string.h>
#include <stdlib.h> // qsortに必要

double mean(double d[],int n)
{
 int i;
 double sum = 0.0;

 for( i=0 ; i<n ; i++ ){
  sum += d[i];
 }
 return sum/n;
}

// qsort用の関数.qsortに渡せる形の関数を作成する必要がある.
// 仮引数や返り値の形式は指定されていることに注意.
int doublecmp(const void *a,const void *b){
 return( *(double *)a - *(double *)b );
}

double median(double d[],int n)
{
 int i;
 double dCpy[n];

 // コピーを作成するのは元データの順序保持のため
 // 今回の統計量を計算するだけのコードでは不要ではあるが.
 for( i=0 ; i<n ; i++ ){
  dCpy[i] = d[i];
}

 qsort(dCpy,n,sizeof(double),doublecmp);

 if( n % 2 ){// 奇数
  return dCpy[n/2];
 }else{// 偶数
  return (dCpy[n/2] + dCpy[n/2 -1])/2.0;
 }
}

double min(double d[],int n)
{
 int i;
 int idx = 0; // 最小値の添字
 for( i=0 ; i<n ; i++ ){
  if( d[idx] > d[i] ){
   idx = i;
  }
 }
 return d[idx];
}

double max(double d[],int n)
{
 int i;
 int idx = 0; // 最大値の添字
 for( i=0 ; i<n ; i++ ){
  if( d[idx] < d[i] ){
   idx = i;
  }
 }
 return d[idx];
}

/*
double (*p[])(double [],int)={mean,median,min,max,NULL};
char *cp[]={"mean","median","min","max",NULL};
*/

typedef struct funcs{
 double (*p)(double [],int);// 関数へのポインタ
 char *cp;// 入力の文字列と比較する関数を表す文字列へのポインタ
}FUNCS;

FUNCS f[]={
 {mean,"mean"},
 {median,"median"},
 {min,"min"},
 {max,"max"},
 {NULL,NULL}
};// ここで一元管理.最後のNULLは終端を示す番兵

double (*setfunc(char *str))(double d[],int n)
{
 int i;

 /*
 for( i=0 ; cp[i] != NULL ; i++ ){
  if( !strcmp(cp[i],str) ){
   return p[i];
  }
 }
 */
 for( i=0 ; f[i].cp != NULL ; i++ ){
  if( !strcmp(f[i].cp,str) ){
   return f[i].p;
  }
 }
 printf("error in setfunc\n");
 return NULL; // 入力の文字列が不適切な場合
}

int main(void)
{
 double (*f)(double d[],int n);
 double a[10]={0,1,2,3,4,5,6,7,8,19};
 f = setfunc("mean");
 printf("%lf\n",f(a,10));
 f = setfunc("min");
 printf("%lf\n",f(a,10));
 f = setfunc("max");
 printf("%lf\n",f(a,10));

 return 0;
}


gccによるコンパイル後の実行結果は、以下の通り。

mean   = 5.500000
median = 4.500000
min    = 0.000000
max    = 19.000000


構造体を用いて管理することによって、関数を表す文字列と関数がまとまり、
対応が分かりやすくなって、バグが出にくくなったのではないかと思う。

ライブラリのqsortを利用しているが、本来は全体をソートする必要はなく、
およそ途中までのソートができていれば良いので、そのあたりは工夫できるかも。

2009年10月8日木曜日

RとC言語で関数ポインタ

Rで関数ポインタの動作というものを確認したことがあります.
具体的な問題設定は,複数の入力文字列に対応させて,
データの入ったテーブルの各列で統計量を計算するというものでした.
(例えば1列目は最小値を、2列目は平均値...のように列ごとに設定する)
まずはRで書いた,文字列で統計量を計算する関数を返す
ソースコードを載せます.

setfunc <- function(type){
 if( type == "min" ){
  return(min)
 }else if( type == "max" ){
  return(max)
 }else if( type == "median" ){
  return(median)
 }else if( type == "mean" ){
  return(mean)
 }
}


d <- c(1:10,rep(10,5))
d
f <- setfunc("mean")
f(d)
f <- setfunc("max")
f(d)
f <- setfunc("median")
f(d)
f <- setfunc("n")
f(d)
この実行結果は下記のようになる。
> d <- c(1:10,rep(10,5))
> d
[1]  1  2  3  4  5  6  7  8  9 10 10 10 10 10 10
> f <- setfunc("mean")
> f(d)
[1] 7
> f <- setfunc("max")
> f(d)
[1] 10
> f <- setfunc("median")
> f(d)
[1] 8
> f <- setfunc("n")
> f(d)
エラー:  関数 "f" を見つけることができませんでした 
これをCで実装したらどうなるかとhttp://ujihisa.blogspot.com/ を書いているujihisaさんとskypeで2時間余り議論して,ようやく動いたのがこれ. medianについては未実装で,実行結果もありませんがご容赦を.
#include <stdio.h>
#include <string.h>

double mean(double d[],int n)
{
 int i;
 double sum = 0.0;
 double *dp;

 for( i=0 ; i<n ; i++ ){
  sum += d[i];
 }
 return sum/n;
}

double median(double d[],int n)
{
 return -1;
}

double min(double d[],int n)
{
 int i;
 int idx = 0; // 最小値の添字
 for( i=0 ; i<n ; i++ ){
  if( d[idx] > d[i] ){
   idx = i;
  }
 }
 return d[idx];
}

double max(double d[],int n)
{
 int i;
 int idx = 0; // 最大値の添字
 for( i=0 ; i<n ; i++ ){
  if( d[idx] < d[i] ){
   idx = i;
  }
 }
 return d[idx];
}


double (*p[])(double [],int)={mean,median,min,max,NULL};
char *cp[]={"mean","median","min","max",NULL};


double (*setfunc(char *str))(double d[],int n)
{
 int i;

 for( i=0 ; cp[i] != NULL ; i++ ){
  if( !strcmp(cp[i],str) ){
   return p[i];
  }
 }
 printf("error in setfunc\n");
 return NULL; // 文字列に一致したものがなかった
}

int main(void)
{
 double (*f)(double d[],int n);
 double a[10]={0,1,2,3,4,5,6,7,8,9.0};
 f = setfunc("mean");
 printf("%lf\n",f(a,10));
 f = setfunc("min");
 printf("%lf\n",f(a,10));
 f = setfunc("max");
 printf("%lf\n",f(a,10));

 return 0;
}

すぐに思いつく改良点は,
・*pと*cpをバラバラではなく,構造体でまとめて持たせる
・長さについては,入力エラーチェックが必要.
といったところでしょうか.また,改良して議論したいと思います.

今日の結論としては,Rと比較すると,C言語の場合はプログラマがポインタをいちいち作らないといけないので,非常に面倒で,かつ間違えやすいということです.
言語の特徴を知って,実装に最適なものを選べということですね.

追記(2011/04)
上記の改良、および中央値の実装を行いました。
http://entertainment-lab.blogspot.com/2009/12/rc.html

フォロワー

ページビューの合計