• ベストアンサー

returnと条件式内の代入

Cでmemcmpライクな関数が必要になり作りました。(正確には中でmemcmpを何回か呼び出して比較をする関数) そのときに出た疑問なのですが、ループ内でif文→returnとするのと、ループの条件にif文の条件を織り込んで、関数の最後でreturnするのとどちらが良いのか。 作った後で、いくつかのサイトでreturnはまとめるほうがいいとありましたので2つめの関数も考えました。しかし、条件式に=(代入)を書くのは好ましくないとする考えもあるようで、もう一つ作りました。しかし、初期化直後のretを比較するのは気分が悪いのです。悩んでいます。つまり、次のような関数でより好ましいのはどれでしょうか? (いずれも質問用にmemcmpに書き直してます) int mymemcmp(const void *a, const void *b, size_t cnt) {   const unsigned char *ap = a, *bp = b;   int i, ret;   for (i=0; i<cnt; i++)   {     ret = ap[i] - bp[i];     if (ret != 0)     {       return ret;     }   }   return 0; } int mymemcmp(const void *a, const void *b, size_t cnt) {   const unsigned char *ap = a, *bp = b;   int i, ret=0;   for (i=0; i<cnt && (ret = ap[i] - bp[i]) == 0; i++)     ;   return ret; } int mymemcmp(const void *a, const void *b, size_t cnt) {   const unsigned char *ap = a, *bp = b;   int i, ret=0;   for (i=0; ret == 0 && i<cnt; i++)   {     ret = ap[i] - bp[i];   }   return ret; }

  • S117
  • お礼率100% (4/4)

質問者が選んだベストアンサー

  • ベストアンサー
  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.4

個人的には1を推奨します。 #2のかたが、述べているように、ソースをみたときのわかりやすさを優先すべきです。その意味では1と3が候補になります。 それで、1と3のどちらが良いかというと、この状態では、どちらでも良いでしょう。しかしながら、もし、forのネストが3重とかになったときに、一つの命令でループを抜ける方法は、C言語の場合、return文以外にありません。 その意味で、今後のことを考えると、1を推奨します。 今回の関数の要件は、「値が異なることが判明した時点で、処理終了となり、かつ、値が異なることを結果として返す。」ことが求められています。 もし、for文が3重になるような、ループで、上記の要件を満足する場合、1の方法が、良いと考えます。 以下、一般論になりますが、もし、処理中に異常が判明し、それ以上処理をする必要がないなら、即リターンする形式の方が、ソースがみやすくなります。 どちらのソースが簡潔か比較して下さい ------------------------- 以下ソース1 Aの判定 if Aのエラー発生 return; Bの判定 if Bのエラー発生 return; Cの判定 if Cのエラー発生 return; 全て正常な場合の処理 ------------------------- 以下ソース2 Aの判定 if Aが正常{  Bの判定  if Bが正常{    Cの判定    if Cが正常{      全て正常な場合の処理    }  } }

S117
質問者

お礼

たしかに、異常系はその場でreturnするのがシンプルで良いですよね。 ただ、今回は異常系とも微妙に違うので悩みどころです。 回答ありがとうございました。

その他の回答 (3)

  • tanma3
  • ベストアンサー率58% (14/24)
回答No.3

>returnはまとめるほうがいい >条件式に=(代入)を書くのは好ましくない 確かにその通りなので以下提案です。 1番目の以下の部分をこう変えたら、上2つの条件が満足すると思います。 int mymemcmp(const void *a, const void *b, size_t cnt) {   const unsigned char *ap = a, *bp = b;   int i, ret;   for (i=0; i<cnt; i++)   {     ret = ap[i] - bp[i];     if (ret != 0)     { // 変更箇所 return ret; break;     }   } // 変更箇所   return 0;   return ret; } 後、質問点とはまったく関係ない提案なのですが、キャストがcharだと長文の場合処理速度が遅くなるんでint型も利用して速度向上をめざす。 という具合でどうでしょう。

S117
質問者

お礼

回答ありがとうございます。なぜか考えているときにbreakを忘れてたんですよね。混乱してたのがだいぶ落ち着きました。 本来の(この疑問が生まれた)関数では、さらにライブラリのmemcmpを呼び出しています。しかし、このmymemcmpを書いたときにはint型なんて忘れてました。ありがとうございました。

  • aigaion
  • ベストアンサー率47% (287/608)
回答No.2

なにをもって「良い」と判断するのかで答えは変わると思います。 私の場合は処理速度にそれほど関係しない部分であれば 後から見て何をやっているのかがわかりやすいことを持って「良い」プログラムと考えます。 ですので、3番目あたりを良いプログラムであると判断します。 1番目はループの終了条件のi<cntとret==0の関係が分かりにくい。 2番目はループ内で何をしているかがわかりにくい。 3番目であればループ内で何をしているか終了条件が何であるかが分かりやすいです。 処理速度を考えれば2番目が最速かと思われますが、そのあたりはコンパイラの最適化が どうとでもしてくれる範囲であると思いますし、実際の処理時間もミリ秒の差もでないでしょう。

S117
質問者

お礼

回答ありがとうございます。ソースの見易さについての質問でした。質問文に書き忘れていました。

回答No.1

好みは人それぞれでしょうが、僕ならたぶんこう書きます。   for (i=0; i<cnt; i++) {     ret = ap[i] - bp[i];     if (ret != 0) {       break;     }   }   return ret; forの中を複雑にしたくないのと、出力は最後にまとめたいからです。

S117
質問者

お礼

回答ありがとうございます。なるほど、単にbreakでもいいですね。

関連するQ&A

  • なんでchar型なんでしょうか??

    なぜここ(☆☆)で char型なのかが よく・・いやさっぱりわかりません。 他の型ではだめなんでしょーか? この関数自体は挿入ソートだとわ思われるんで なんとなくこの☆☆(から以下三行あたりまで) のところの動作の意味はわかるんですが なぜchar型でなくてはならないのかが。 知っている方いたら どうか教えてください。 typedef User* PUser; typedef int (*CFT)(const void*, const void*); void ssort(void* base, unsigned int n,        unsigned int sz, CFT cmp) {    for (int i = 0; i < n - 1; i++) for (int j = n - 1; i < j; j--) {          char* pj = (char*)base + j * sz; //←ここ☆☆          char* pj1 = pj - sz;  //とここ☆☆           if (cmp(pj, pj1) < 0) { for (int k = 0; k < sz; k++) { char temp = pj[k]; pj[k] = pj1[k]; pj1[k] = temp; }     }    } /* ssort関数の引数の関数ポインタで 利用される比較関数 */ int cmp1(const void* p, const void* q) { return strcmp(PUser(p)->name, PUser(q)->name); } int cmp2(const void* p, const void* q) { return PUser(p)->dept - PUser(q)->dept; }

  • c言語関数の(1)~(5)までの部分が何をやっているのかよく分からない

    c言語関数の(1)~(5)までの部分が何をやっているのかよく分からないので、どなたか解説をお願いします。 int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *p1 = (const unsigned char *)s1; const unsigned char *p2 = (const unsigned char *)s2; while (n-- > 0) { if (*p1 != *p2) return (*p1 - *p2); p1++; p2++; } return (0); } return (*p1 - *p2); > (1) ---------------------------------------------------------------------- char *strcat(char *s1, const char *s2) { char *p = s1; while (*s1) s1++; /* s1を末尾まで進める */ while (*s1++ = *s2++) ; /* '\0'が見つかるまでs2をコピー */ return (p); } while (*s1++ = *s2++) ; > (2) ---------------------------------------------------------------------- char *strstr(const char *s1, const char *s2) { const char *p1 = s1; const char *p2 = s2; while (*p1 && *p2) { if (*p1 == *p2) { p1++; p2++; } else { p1 -= p2 - s2 - 1; p2 = s2; } } return (*p2 ? NULL : (char *)(p1 - (p2 - s2))); } while (*p1 && *p2) > (3) p1 -= p2 - s2 - 1; > (4) ---------------------------------------------------------------------- char *strcpy(char *s1, const char *s2) { char *p = s1; while (*s1++ = *s2++) ; return (p); } while (*s1++ = *s2++)   > (5) ;          > (5) ----------------------------------------------------------------------

  • このプログラムの復元処理教えでください。助けてくだ

    #include <stdio.h> int main(void) { char a[51]; char b[101]; char c[51]; int i,k; int cnt; printf("文字例-->"); scanf("%s",a); i = 0; k = 0; while(a[i] !='\0') { cnt = 0; b[k] = a[i]; while(b[k] = a[i]) { cnt++; i++; } k++; b[k] = cnt + 48; k++; } b[k]='\0'; printf("b=%s\n",b); printf("c=%s\n",c); //-------------------------------------------------- getchar(); return 0; }

  • 問題文に対してこのプログラムは正しいか

    文字列を先頭から1文字ずつ表示する関数を作成せよ。 void gput(const char *s, int speed); ここで、sは表示する文字列、speedはミリ秒単位の表示速度である。たとえば、 gput("ABC", 100); と呼びだすと、まず'A'を表示し、その100ミリ秒後に'B'を表示し、さらにその100ミリ秒後に'C'を表示する。このようにして"ABC"の全文字を表示すると呼び出し元に戻ること。 /* 課題2-1 */ #include <time.h> #include <stdio.h> #include <stdlib.h> /* gput関数の宣言 */ void gput(const char *s, int speed); /* sleep関数の宣言 */ int sleep(unsigned long x); int main (void) { gput("ABC", 1000); return (0); } /* gput関数の定義 */ void gput(const char *s, int speed) { int i; int c = strlen(s); for(i=0; i<c; i++){ putchar(s[i]); fflush(stdout); sleep(speed); } } /* sleep関数の定義 */ int sleep(unsigned long x) { clock_t c, s = clock(); do{ if((c = clock()) == (clock_t)-1) return (0); }while(1000UL * (c - s) / CLOCKS_PER_SEC < x); return (1); } 演習問題に解答が無いので質問します。 呼び出し元に戻ることとは、gput関数を呼び出した後にmain関数に戻ることを言っているのでしょうか。 またsleep関数では、return 0とreturn1を使い分けていますがそれぞれどういった意味があるのですか。 関数の返り値は理解できるのですが、本にmain関数のreturn0についてなど何も触れられていないので混乱しています。

  • C言語のreturnの使い方

    return a, b; のように2つの引数で値を返せることを最近になり 知りました。ところで以下のような使い方は可能でしょうか? test( , )という2つの引数が必要な関数にnum()で return 1,2としてひとつの関数呼び出しで引数2個分に すると言うようなことです。 #include <stdio.h> int test( int i, int j){   printf("%d %d",i,j); } int num(void){   return 1,2; } int main(void){   test( num() ); }

  • 「動的確保した2次元配列のメモリ解放」を関数化したい

    質問タイトルの通りですが、 「動的確保した2次元配列のメモリ解放」をC言語で関数化したいと思っています。しかし、関数の引数には動的確保した配列の先頭アドレスのみ渡す形にしたいです。そのような場合の関数化は可能ですか? どうもうまくいかず、困っています。 以下、具体的に、サンプルソースを記述します。 わかる方、よろしくお願いします。 //====================================================// #include<stdio.h> unsigned char** AllocByteArray2d(int column, int row); void FreeByteArray2d(unsigned char** box); int main(voidls){ unsigned char array**; array = AllocByteArray2d(2, 3); FreeByteArray2d(array); return 0; } unsigned char** AllocByteArray2d(int column, int row){ unsigned char* box; box = (unsigned char**)malloc( sizeof(unsigned char*)*column ) int i; for(i=0; i<column; i++){ box[i] = (unsigned char*)calloc( row, sizeof(unsigned char)); if(box[i] == NULL) exit(EXIT_FAILURE); } return box; } //引数では配列の先頭アドレスだけ渡す形にしたい void FreeByteArray2d(unsigned char** box){ //ここをどう書いたらいいかわからない }

  • java

    このプログラムをjavaでかくとどうなりますか? #include<stdio.h> #include<stdlib.h> #include<time.h> int comp(const void *a, const void *b){  return *(int*)a - *(int*)b; } int main(void){  int i;  int sep[9];  srand((unsigned int)time(NULL));  sep[0] = 0;  sep[8] = 100;  for(i=1;i<8;i++){   sep[i] = rand() % 101;  }  qsort(sep+1, 7, sizeof(int), comp);  for(i=0;i<8;i++){   printf("a[%d] = %d;\n", i, sep[i+1] - sep[i]);  }  return 0; }

    • ベストアンサー
    • Java
  • 処理内容

    以下の3点について有識者へ伺いたいです。 (1)このプログラムが何をするものなのか (2)最後のfor文が無限ループしてしまう理由 (3)可能であれば、各行(各処理)が何をしているのかを教えていただければ嬉しいです。 #include <stdio.h> #include <string.h> #define NUM_HASH 10 char *hash_key[NUM_HASH]; unsigned char hash_val[NUM_HASH]; unsigned char calc_hash_value(const char *key) { int i; unsigned char val = 0; for (i = 0; key[i] != '\0'; i++) val += key[i]; return val; } int main(int argc, char *argv[]) { int i, cnt = 0; unsigned char val; for (--argc, ++argv; argc > 0 && cnt < sizeof(hash_val)/sizeof(hash_val[0]); --argc, ++argv, cnt++) { hash_key[cnt] = *argv; hash_val[cnt] = calc_hash_value(hash_key[cnt]); } for (val = 0; val < 256; val++) { for (i = 0; i < cnt; i++) { if (val == hash_val[i]) { printf("key = |"%s|"|n", hash_key[i]); printf("hash value = 0x%.2X|n", hash_val[i]); } } } return 0; }

  • コンパイルエラー invalid operands to binary

    自己啓発で入力文字列をBASE64デコードする関数を作っているのですが、L20~L23(a[0] = strchr(b64, p[0]) - b64;)でコンパイルエラーinvalid operands to binaryが発生して色々試行錯誤しているのですが、どうしてもエラーがとれません。 ソースをここに書くのは大変恐縮なのですが、原因がわかる方がいらっしゃいましたら、教えていただけないでしょうか? char *Base64n(unsigned char *buf, size_t length, size_t *outlen) { const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst         uvwxyz0123456789+/="; unsigned char *p; unsigned char *q; unsigned char a[4]; char *RtnBuf; int j=0; int cnt; RtnBuf = (char *)malloc(length+1); memset(RtnBuf, 0, length+1); p = (unsigned char*)buf; q = (unsigned char*)RtnBuf; cnt = 0; while(*p != 0) { a[0] = a[1] = a[2] = a[3] = 0; a[0] = strchr(b64, p[0]) - b64; a[1] = strchr(b64, p[1]) - b64; a[2] = strchr(b64, p[2]) - b64; a[3] = strchr(b64, p[3]) - b64; q[0] = ((a[0] << 2) | (a[1] >> 4)) & 0xff; cnt++; if (p[2] != '=') { q[1] = ((a[1] << 4) | (a[2] >> 2)) &0xff; cnt++; } if (p[3] != '=') { q[2] = ((a[2] << 6) | a[3]) & 0xff; cnt++; } p += 4; q += 3; } *outlen = cnt; return(RtnBuf); } コンパイルはRed Hatでgccを使ってコンパイルしています。 引数は第1引数がデコード対象の文字列、第2引数がデコード対象文字列長、第3引数がデコード後の文字列長で、戻り値がデコード後の文字列です。

  • 2次元配列とポインタの引数受け渡しについて

    2次元配列を関数に渡すときは、引数に渡す2次元配列と同じサイズを指定、もしくは2次元目のサイズのみ合わせて渡す方法がありますが、両方とも違うサイズで同じ関数を使いたいです。 最初は中身が同じで引数で受け取る2次元配列のサイズだけ、それぞれに合わせた引数を持つ関数を2つ作っていたのですが、なんだか冗長な気がしました。 そこで、2次元配列の先頭ポインタとサイズを受け取るようにすればいいのかと思い、テストとして次のプログラムを作成してみました。 #include <stdio.h> void func(unsigned char *a, int y, int x); int main(void) { unsigned char a[10][10]; func(a, 10, 10); printf("%d\n", a[7][4]); return 0; } void func(unsigned char *a, int y, int x) { int i, j; for (i = 0; i < y; i++) { for (j = 0; j < x; j++) { *(a + i*y + j) = i * j; } } } もちろんこれでも動くのですが、やはりこういう書き方はルールにはないので、コンパイルで警告が出ます。 a.c: In function ‘main’: a.c:10: warning: passing argument 1 of ‘func’ from incompatible pointer type a.c:4: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[10]’ このような書き方はやはりやめたいいのでしょうか。 また、その際はサイズ別に関数を作るしかないのでしょうか。 他にいい方法があれば教えていただけると助かります。

専門家に質問してみよう