関数ポインタについてわからないところがあります

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
大学生

関数ポインタについてわからないところがあります

#1

投稿記事 by 大学生 » 13年前

先日線形リストの課題について質問させていただいた者です。
今回は前回のプログラムの機能をさらに拡張します。

市のデータ(cities.txt)を対象として、以下の各機能を持ったプログラム
1. 情報の登録と一覧表示
2. 情報の削除
3. cities.txt からの情報の読み込み
4. citiesDB.txtへのリスト内容の書き出し
5. 人口でソート

に対して、以下の点を拡張します。
- 人口、面積、人口密度、設立年月日について、それぞれ昇順および降順でソート
- 関数ポインタを使用すること

それで、自分なりにサンプルプログラムなどを使って作ってみましたが、わからない点が数点あります。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define STRLEN   32
#define BUF_LEN 128
#define CITYDB_R_FILE "cities.txt" // 読み込み用ファイル
#define CITYDB_W_FILE "citiesDB.txt" // 書き出し用ファイル

struct cities{
  char pref[STRLEN];
  char city[STRLEN];
  int popl;
  float area;
  float dens;
  char founded[STRLEN];
  struct cities *next;
};

struct cities *root=NULL; // rootへのポインタは大域変数として定義

void *mymalloc(size_t sz){
  void *p = (void *)malloc(sz);
  if(p == NULL){
    fprintf(stderr,"ERR: Can't malloc memory %d bytes.",(int)sz);
    exit(1);
  }
  memset(p,0,sz);
  return p;
}

void showCity(struct cities *c, FILE *fp){
  fprintf(fp,"%s\t%s\t%d\t%.2f\t%.2f\t%s\n",
          c->pref, c->city, c->popl, c->area, c->dens, c->founded);
}

struct cities *genNewCityCell(char *pref, char *city, int popl, float area, float dens, char *founded ){
  struct cities *c;
  c = mymalloc(sizeof(struct cities));

  strcpy(c->pref,pref);
  strcpy(c->city,city);
  c->popl = popl;
  c->area = area;
  c->dens = dens;
  strcpy(c->founded,founded);

  return c;
}

void showList(FILE *fp){
  struct cities *cur= root;
  while(cur != NULL){
    showCity(cur,fp);
    cur = cur->next;
  }
}

void saveList(){

  FILE *fp;
  if((fp=fopen(CITYDB_W_FILE,"w"))==NULL){
    printf("cannot open!\n");
    exit(1);
  }
  showList(fp);

  fclose(fp);

  printf("File saved to %s\n",CITYDB_W_FILE);
}

struct cities *line2City(char *buf){
  struct cities *c=NULL;
  int popl;
  char pref[STRLEN], city[STRLEN], founded[STRLEN];
  float area, dens;

  sscanf(buf, "%s %s %d %f %f %s", pref, city, &popl, &area, &dens, founded);

  c = genNewCityCell(pref,city,popl,area,dens,founded);
  return c;
}

void addCity(struct cities *c){
  struct cities **cur = &root;

  while(*cur!=NULL){
    cur = &(*cur)->next;
  }
  *cur = c;
  c->next = NULL;

}

void inputStr(char *buf,int len){
  fgets(buf,len,stdin);
  buf[strlen(buf)-1]='\0';
}

void addNewCity(){
  char buf[BUF_LEN] = "";
  struct cities *c;

  while(strlen(buf)<=0){
    printf("Input one line:\n");
    inputStr(buf, BUF_LEN);
  }
  if(strncmp(buf, "Prefecture", 10) == 0) return;
  
  c = line2City(buf);
  addCity(c);
}

void deleteCityFromList(char *delcity){
  struct cities **cur = &root;

  while(*cur && strcmp(delcity,(*cur)->city)){
    cur = &(*cur)->next;
  }
  if(*cur){
    struct cities *p = *cur;
    *cur = p->next;
    free(p);
  }
  

}

void deleteCity(){
  char buf[STRLEN];

  showList(stdout);

  printf("Select Delete City Name:");
  inputStr(buf, STRLEN);
  deleteCityFromList(buf);
}

void readListFILE(FILE *fp){
  char buf[BUF_LEN];
  struct cities *c;
  while( fgets(buf,BUF_LEN,fp) != NULL){
    if(strncmp(buf, "Prefecture", 10) == 0) continue;
    c = line2City(buf);
    if(c != NULL) addCity(c);
  }
}


void readList(){
  FILE *fp;

  if((fp=fopen(CITYDB_R_FILE,"r"))==NULL){
    printf("cannot open!\n");
    exit(1);
      }
  readListFILE(fp);

  fclose(fp);

  printf("File loaded\n");
}

int poplAscend(struct cities *p,struct cities *q){
  return p->popl - q->popl;
}

int poplDescend(struct cities *p,struct cities *q){
  return q->popl - p->popl;
}

int areaAscend(struct cities *p,struct cities *q){
  return p->area - q->area;
}

int areaDescend(struct cities *p,struct cities *q){
  return q->area - p->area;
}

int densAscend(struct cities *p,struct cities *q){
  return p->dens - q->dens;
}

int densDescend(struct cities *p,struct cities *q){
  return q->dens - p->dens;
}

int foundedAscend(struct cities *p,struct cities *q){
  return p->popl - q->popl;
}

int foundedDescend(struct cities *p,struct cities *q){
  return q->popl - p->popl;
}

void insertCityWithComp(struct cities *sc,                                                         int(*comp)(struct cities*,struct cities*)){
  struct cities **cur= &root;

  while(*cur != NULL){
    if(comp(*cur,sc)>0)
      break;
    cur = &((*cur)->next);
  }
  sc->next = *cur;
  *cur = sc;

}

void sortCityWithComp(int(*comp)(struct cities*,struct cities*)){
  struct cities *cur,*fr;
  cur = root; 
  root = NULL;
  while(cur != NULL){
    insertCityWithComp(genNewCityCell(cur->pref, cur->city, cur->popl, cur->area, cur->dens, cur->founded),comp);
    fr = cur;
    cur = cur->next;
    free(fr);
  }
  
  showList(stdout);
}

void sortList(){
  char buf[BUF_LEN];
  printf("sort by?(xy)\n");
  printf("x=[p:population,a:area,d:dens,f:founded]\n");
  printf("y=[a:ascending,d:descending]\n");
  fgets(buf,BUF_LEN,stdin);

  if(strncmp(buf,"pa",2)==0){
    sortCityWithComp(poplAscend);
  }
  else if(strncmp(buf,"pd",2)==0){
    sortCityWithComp(poplDescend);
  }
  else if(strncmp(buf,"aa",2)==0){
    sortCityWithComp(areaAscend);
  }
  else if(strncmp(buf,"ad",2)==0){ //少しずれが生じる 例.55.30>55.50
    sortCityWithComp(areaDescend);
  }
  else if(strncmp(buf,"da",2)==0){
    sortCityWithComp(densAscend);
  }
  else if(strncmp(buf,"dd",2)==0){//上と同様
    sortCityWithComp(densDescend);
  }
  else if(strncmp(buf,"fa",2)==0){//文字列なのに昇順?
    sortCityWithComp(foundedAscend);
  }
  else if(strncmp(buf,"fd",2)==0){
    sortCityWithComp(foundedDescend);
  }
  else{
    printf("Not practice!\n");
  }

}

int main(int argc, char *argv[]){
  char buf[BUF_LEN];

  while(1){
    printf("Menu(a:add, d:delete, l:list, s:sort, w:write file, r:read file q:quit):\n");
    
    fgets(buf,BUF_LEN,stdin);
    switch(buf[0]){
    case 'a':
      addNewCity();
      break;
    case 'd':
      deleteCity();
      break;
    case 'l':
      showList(stdout);
      break;
    case 's':
      sortList();
      break;
    case 'w':
      saveList();
      break;
    case 'r':
      readList();
      break;
    case 'q':
      exit(0);
      break;
    }
  }
}
・densやareaでソートした時にずれが生じる
たとえば、areaで大きい順にソートしたら
Aichi Iwakura 48042 10.49 4580.00 1971-12-01
Osaka Takaishi 60077 11.35 5293.00 1966-11-01
Tokyo Koganei 115116 11.33 10160.00 1958-10-01
Tokyo Kokubunji 118801 11.48 10349.00 1964-11-03
Tokyo Fussa 60413 10.24 5900.00 1970-07-01
Tokyo Kiyose 73518 10.19 7215.00 1970-10-01
Saitama Wako 78575 11.04 7117.00 1970-10-31
Tokyo Hamura 56984 9.91 5750.00 1991-11-01
Tokyo Musashino 138516 10.73 12909.00 1947-11-03
のような結果になります。
このずれはどうすれば治りますか?

・foundedは文字列として構造体に格納されていますが、これはどうやって比較するのでしょうか?

よろしくお願いします。

box
記事: 2002
登録日時: 15年前

Re: 関数ポインタについてわからないところがあります

#2

投稿記事 by box » 13年前

大学生 さんが書きました: たとえば、areaで大きい順にソートしたら
Tokyo Kiyose 73518 10.19 7215.00 1970-10-01
Saitama Wako 78575 11.04 7117.00 1970-10-31
のような結果になります。
このずれはどうすれば治りますか?
おかしいのは本当にそこだけですか?面積の降順については、何だか全体的におかしいように見えて仕方がありません。
大学生 さんが書きました: ・foundedは文字列として構造体に格納されていますが、これはどうやって比較するのでしょうか?

コード:

void deleteCityFromList(char *delcity){
  struct cities **cur = &root;
 
  while(*cur && strcmp(delcity,(*cur)->city)){
ここに書いてあるstrcmp()は、どういう意図で書かれたのですか?
まさに、「文字列どうし全体を比較する」ためではないのですか?

ところで、標題の「関数ポインター」に関する質問は、どこにあるのでしょうか。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

大学生

Re: 関数ポインタについてわからないところがあります

#3

投稿記事 by 大学生 » 13年前

回答ありがとうございます

>おかしいのは本当にそこだけですか?面積の降順については、何だか全体的におかしいように見えて仕方がありません

もちろんここだけではありません
全体的にずれています
何故でしょうか?

>ここに書いてあるstrcmp()は、どういう意図で書かれたのですか?
まさに、「文字列どうし全体を比較する」ためではないのですか?

foundedは1985-01-15のように文字列になっています
これをどうやって年代順に並べるのかを質問しました

かずま

Re: 関数ポインタについてわからないところがあります

#4

投稿記事 by かずま » 13年前

sortCityWithComp の引数である関数ポインタ comp は、
「構造体 struct cities へのポインタを 2つ受け取って、
それらのメンバの大小を比較した結果を int で返す関数」へのポインタですよね。

メンバの popl は int で、しかも正の値ですから、差を取れば、比較結果が
負、ゼロ、正の値になります。

ところが、area や dens は float です。11.04 と 10.19 の差は 0.85 です。
areaAscend などはこれを int に変換して返そうとするので、
0.85 は 0 になります。したがって、intCityWithComp の中の
comp(*cur, sc) > 0 では *cur と sc は等しいと判断され、正しくソートされません。

次のように書けばよいでしょう。

コード:

int areaAscend(struct cities *p, struct cities *q)
{
    if (p->area < q->area) return -1;
    if (p->area > q->area) return 1;
    return 0;
}
次に、founded の比較です。
founded は char配列で、その中には年月日の文字列が入っています。
文字列の比較は strcmp で行います。

コード:

int foundedAscend(struct cities *p, struct cities *q)
{
    return strcmp(p->founded, q->founded);
}
年月日は数値ではなく文字列ですが、数字の比較結果は数値の比較結果と同じに
なるので年代順になります。

大学生

Re: 関数ポインタについてわからないところがあります

#5

投稿記事 by 大学生 » 13年前

>かずまさん
とても納得のいく解答ありがとうございました。
floatだから結果がずれてたんですね~^ ^;
strcmpの本質も理解できていませんでした・・・
ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る