• ベストアンサー

C言語の基礎

1.main関数の引数である、int argc と char **argvはどのように使用するのでしょうか? 2.特定の文字列(UserID)を入力し,それぞれの文字が使用可能文字列(数字とアルファベット(大文字小文字)と一致したら,使用可能文字列(配列)から削除し,残りの配列の文字を使って6~8文字のパスワードを作成するにはどのように関数を設計すればいいでしょうか? パスワードを作成するのに時刻を種とした乱数を使用してある一文字を取得するという方法を考えました。 srand(unsigned time(NULL); パスワードはUserIDに使用した文字を含まないようにしたいので,(使用可能文字列)-(UserID)= パスワードに使用できる文字列になると思うのですが, 配列-配列なんてできるのでしょうか?実力不足のため,変な質問をして申し訳ございません。どうかご教授ください。

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

  • ベストアンサー
  • hitomura
  • ベストアンサー率48% (325/664)
回答No.5

この質問は参照URLの続きですよね? こうしたほうがいいと言った立場上、答えさせていただきます。 あのアドバイスをしたとき、私の頭にあったのは次のようなソースでした。 (ちなみに、このソースは必要な処理を色々抜かしてある上、空白を全角で入力しているためそのままでは動きません。) /*使用可能な文字テーブル:62だと乱数に偏りが出るため64にしてある*/ char g_acTable[64]; /*=10(数字)+26(大文字)+26(小文字)+2(穴埋め)*/ main( int argc, char** argv ) {  int i;  char szPassword[8]; /*パスワード格納スペース*/  int nLength;    /*パスワードの文字列長*/  int nCount=0;    /*処理中の文字が何文字目か?*/  int nLetter;    /*文字候補がテーブルの何文字目か?*/  /*使用可能な文字テーブルの初期化*/  for(i=0;i<10;i++){   g_acTable[i]='0'+i;  }  for(i=0;i<26;i++){   g_acTable[i+10]='A'+i;  }  for(i=0;i<26;i++){   g_acTable[i+36]='a'+i;  }  g_acTable[62]=g_acTable[63]=-1;  /*パラメータのチェックルーチンがここに入る*/  /*ユーザーIDと一致する文字を使わないようにする*/  for(i=0;argv[1][i]!=0;i++){   if(isdigit(argv[1][i])){    g_acTable[argv[1][i]-'0']=-1;   }else if(isupper(argv[1][i])){    g_acTable[argv[1][i]-'A'+10]=-1;   }else if(islower(argv[1][i])){    g_acTable[argv[1][i]-'a'+36]=-1;   }  }  nLength=atoi(argv[2]);  /*パスワード作成*/  while(nCount<nLength){   nLetter=rand()%64;   /*パスワードに使えない文字だった場合は乱数の出しなおし*/   if(g_acTable[nLetter]==-1){    continue;   }   szPassword[nCount]=nLetter;   nCount++;  }  /*完成したパスワードの表示*/  printf("Password for %s : %s\n",argv[1],szPassword); }

kiroro302
質問者

お礼

前回に引き続き、ご丁寧なご教授を賜り,ありがとうございます。パラメータのチェックルーチンは if(1==argc) return help() (←help()関数は単に使用方法を表示したもの) としました。使用可能な文字テーブル上の、UserIDに使用されている文字を使用できなくしてしまう方法はすばらしいですね。私が関数を作ると長くなってしまい効率が悪いプログラムになってしまっているようです。UserIDと一致する文字を使わないようにするやり方は参考にさせていただきます。もう少しでできそうですので,がんばってみます。どうもありがとうございました。今後もよろしくお願いいたします。

kiroro302
質問者

補足

すみません。ちょっとわからないので、おしえてください。 パスワードを作成するのに時間を種にした乱数を使用してある一文字を取得するのに while(nCount<nLength){ srand(ungigned int time(NULL)); nLetter=rand()%64; -------------------------- というところで、%64の意味がわかりません。%64は余りという意味でしょうか? rand()は0から32767までの数値を返すのですよね。RAND_MAXは定義されている定数で、2の15乗‐1に変換されるということを本で見たのですが,どういうことでしょうか? %64とすると0~63までの乱数を取得できるということになるということですが、この意味が理解できないのですが、ご教授願えますでしょうか?あまりに知識がなさ過ぎて申し訳ありません。よろしくお願いいたします。

その他の回答 (4)

  • madman
  • ベストアンサー率24% (612/2465)
回答No.4

文字列の引き算関数を作ってみました。 あと、2.で使えるような感じのソースを書いてみました。 char *subtraction(char *retval, char *userID); で引数の、前の文字列-後ろの文字列の結果が関数値で返ります。 呼出先よりこの関数を上に置くか、上の形でプロトタイプ宣言してください。 まあ、普通パスワードはcrypt関数を使うんだけど、標準ではライブラリにくっついていないんで... 後は自分で調べて勉強してください。 --- #include <stdio.h> #include <strings.h> #define BASE_MAX 62 char base[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; char *subtraction(char *retval, char *userID) { int i, cnt; char *cret; for (i=0; i<strlen(userID); i++) { cret = strchr(retval, userID[i]); cnt = (int)(cret - retval); if (cret && cnt != strlen(retval)) { strcpy(&retval[cnt], &cret[1]); } else if(cnt == strlen(retval)){ base[cnt-1] = 0; } } return retval; } main(){ char tmp[BASE_MAX]; strcpy(tmp, base); printf("%s\n",subtraction("sodivubv", tmp)); strcpy(tmp, base); printf("%s\n",subtraction("zoyivubv", tmp)); }

kiroro302
質問者

お礼

ご回答ありがとうございます。私もいろいろ考えてみました。使用できる文字を配列に入れて,UserIDの文字と比較し,等しかったらフラグを1にし、不等だったら、0にして、フラグが0だったら、usablestrという配列にいれて、パスワードに使用できる文字の配列をつくるというような、関数を考えてみました。 void makecharset(char *account, char *usablestr) { int i, j, k, n; const char c[62] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; n = 0; for( i=0; i < 62; i++ ) { k=0; for( j=0; j < strlen(account); j++ ) { if ( account[j] == c[i] ) { k=1; break; } } if(0 == k) usablestr[n++] = c[i]; } usablestr[n] = '\0'; } ご教授いただいたソースを自分なりに消化し,参考させていただきます。 ご丁寧にご回答いただき本当にありがとうございました。これからもよろしくお願いいたします。

  • sssohei
  • ベストアンサー率33% (33/98)
回答No.3

> どのように関数を設計 問題をどのように解決すればいいか、分解してみてください。 > 配列-配列なんてできるのでしょうか C++ でなら可能と言えば可能です^^; 配列の変わりにvectorを使って、、、(とりあえず、それるので、以下略でおいておきます。 Cでやる場合、擬似的なのですが、一致したら、次以降の奴で上書きするという感じで書きます。 # いらばいデータのぶん詰めてしまう、と言うことです。 char array[size] int i, pos=0; for(i=0; i<size; i++) {  if (array[i] が UserIDに含まれていなければ) {   array[pos] = array[i];   pos++;  } } for(i=pos; i<size; i++) array[i] = 0; // 残りを破棄する array_size = pos; // 新しい配列の大きさはpos

kiroro302
質問者

お礼

ありがとうございます。大変参考になります。いらないデータの上から上書きして詰めてしまい、残りの要素に0を代入して,破棄するのですね。この"残りの要素に0を代入して破棄する"というような、アイデアがなかなか浮かびません。センスないのでしょうか?やはり私のようなものは、たくさんソースを書いて,いろいろ試してみて経験を積無しかないなと思います。地道にやっていくつもりですので,これからもよろしくお願いいたします。

  • syo_2910
  • ベストアンサー率21% (3/14)
回答No.2

2.の回答です。 見当違いだったらごめんなさい。 まず使用可能文字列の配列を2次元以上に取ります 例えばこんな風に struct TBL { int flag ; char moji[9] ; }Tbl[256] ; 文字数最大8文字までの使用可能文字列を 256用意します。 UserIDと比較を行い、一致した使用可能文字列には flagをONすることによって、その文字列を 使わないようにします。 また、UserIDと比較は完全一致のみで あれば、strcmp関数等関数で比較を行えば用意にできると 思いますが。 UserID+他の文字等の場合は、一文字ずつ比較する 関数を作る必要があります。 こんなもんでどうでしょうか?

kiroro302
質問者

お礼

ご丁寧に補足していただきありがとうございます。構造体を使うということは、思案しなかったので,大変参考になります。私の力ではまだ構造体をうまく使いこなせそうにないのですが,本を読みながら考えてみたいと思います。いっしょに勉強している友達も、構造体を使用している人はいないみたいなので、挑戦してみます。どうもありがとうござます。最近だんだん勉強が面白くなってきつつあります。またつまずいてしまうかもしれませんが,これからもよろしくお願いいたします。

  • syo_2910
  • ベストアンサー率21% (3/14)
回答No.1

まずは、1.質問から回答します あまり綺麗なプログラムではありませんが main(argc, argv) int argc ; char *argv[] ; { int i ; if(argc<= 1) { printf("引数がありません\n") ; } else { for(i = 1;i < argc;i ++) printf("引数は%s\n",argv[i]) ; } } 例えば、上記のようなプログラムが fnc.exeのmain関数だったとします。 このとき、 fnc.exe(Enter) と、実行すると argc=1,argv[0]="fnc.exe"が渡されます。 もしも、fnc.exe a b(enter) とすると argc=3, argv[0]="fnc.exe" argv[1]="a" argv[2]="b"が渡されます。 このように、argc,argvを使うと main関数(EXE)へ引数を渡すことができます。 ※ デバッグしていないので動かないプログラムだったら   ごめんなさい

kiroro302
質問者

お礼

ご回答ありがとうございます。fnc.exe a b(enter)とすると argc=3, argv[0]="fnc.exe" argv[1]="a" argv[2]="b"が渡されます ということですが、argc=3の3は引数の数が3つあるということでしょうか? argv[0]には実行するファイルそのものを渡されるのですね。この配列が勝手に作られるのはC言語の仕様なのでしょうか?

関連するQ&A

  • C言語プログラミングについて

    「要素数10の配列を準備する。 配列の各要素に0.0~1.0の乱数を入れる。 各配列に入力された乱数を出力する。 配列をオリジナル関数hanteiに渡す。 数hantei内において,各要素の値が0.5以上であれば1 , 0.5未満であれば0を出力する。」 C言語でこのような問題があるのですがどのように作ればよろしいのでしょうか? ちなみに以下のように作ってみて、「argcは一度も使用されていない」「argvは一度も使用されていない」とエラー(警告)が出てしまいました。修正、もしくは正しいプログラムを教えてくれませんか? #include <stdio.h> #include <time.h> #include <stdlib.h> #define SIZE 10 int hantei(double num) { return num>=0.5; } int main(int argc,char *argv[]) { double number[SIZE]; int i; srand((unsigned)time(NULL)); for(i=0;i<SIZE;i++) number[i]=(double)rand() / RAND_MAX; //手抜き乱数 for(i=0;i<SIZE;i++) { printf("%f %d\n",number[i], hantei(number[i])); } return 0; }

  • C言語についての質問です

    C言語についての質問です キーボードから文字列を入力しそれらを結合して行くことで長い文字列を作成する "Q"を入力した場合入力終了する "F"を入力した場合既存の文字列の前部に追加文字列を加える "R"を入力した場合既存の文字列の後部に追加文字列を追加する。 追加文字列はF,Q,Rのいずれかを入力したのちにキーボードより入力する。 またグローバル関数を使用しないでプログラムを作成する。加えてmain関数とは別に次の様な関数を作成し利用する 戻り値 : 無し(void型) 引数1 : コマンド文字列(Q,F,Rなどの追加文字列の追加場所を決定するchar型配列) 引数2 : 追加文字列(char配列) 引数3 : 追加される側の文字列(char配列) どうしてもグローバル関数を使用しないで作成することができません。また後部への追加文字列の追加はstrcat関数を使い実現することができましたが前部への追加文字列の追加の方法がわかりません。アドバイスやヒントなど何でもいいので指導のほどよろしくお願いします。

  • C言語

    以下のC言語のプログラムを教えてください。 お願いします。 (1)標準入力から文字列(2 文字以上)を入力し,文字数を計上すると共に,入力された文字列の逆順に入れ替える処理を実現してください.なお,以下の要件を満たしたプログラムを作成してください. ・ 入力された文字列は,char 型の配列(要素数50)で受け取ること ・ 文字数を計上するcount 関数(引数:配列のアドレス,戻り値:文字数)を定義 し,main 関数より呼び出すこと ・ 文字列を逆順に入れ替えるreverse 関数(引数:配列のアドレス,戻り値:無し) を定義し,main 関数より呼び出すこと ・ 標準出力の処理は,main 関数で記述すること 【プロトタイプ宣言】 int count(char *str); void reverse(char *str); 【実行結果】 文字列を入力してください(2 文字以上) apple 文字数 = 5 入れ換え前 apple 入れ換え後 elppa (2)char 型の配列(要素数50)を2 つ宣言し,標準入力から2 つの文字列を入力してください.そして,格納した字列を入れ替える関数(swapstr 関数)を作成し,入れ替え前と入れ替え後の配列内の値(文字列)を配列名とともに標準出力するプログラムを作成してください. 【プロトタイプ宣言】 void swapstr(char *str1, char *str2); 【実行結果】 2 つの文字列を入力してください apple strawberry 入れ換え前 配列str1 = apple 配列str2 = strawberry 入れ換え後 配列str1 = strawberry 配列str2 = apple

  • C言語について教えてください。

    二次元配列でまとめられた「配列で実現する文字列」の文字と文字列を表示する関数を定義し、その関数の機能を確認するプログラムを作成する。 ただし、以下の例のように文字列の個数が変更されても処理できる関数を作成する。 また、文字列の文字の個数は最大9とする(一次元配列の要素数は10とする) さらに、関数printf()をできるだけ用いない。 (1)二次元配列でまとめられたすべての文字列を「一度に」表示する関数を定義する (2)二次元配列でまとめられたすべての文字列の文字を「一度に」表示する関数を定義する。このとき、前回の課題で定義した「すべての文字を表示する関数print_all_char()」を新しく定義する関数から呼び出して用いる。 (3)二次元配列に各文字列を格納してまとめるとき、初期化ではなく、配列の宣言・定義の後で、関数strcpy()を用いること(例では、初期化によりまとめている) [例] char astr[][10] = { "ABCD", "EFGHIJ", }; [実行結果の例] すべての文字列の表示 ABCD EFGHIJ すべての文字列の文字の表示 A B C D E F G H I J [ヒント] (1)二次元配列でまとめられた文字列の文字あるいは文字列を表示する関数<返却値型><関数名>(二次元配列の受け渡しに対応した仮引数の宣言、文字列の数) (2)文字列の数を求める。二次元配列のすべての要素に文字列が格納されている場合、文字列の数=二次元配列の要素数(一次元配列の数) =sizeof(astr)/sizeof(astr[0]) ただし、sizeof演算子の生成する型はsize_t型である。 この問題について教えてください。問題丸投げだとは分かっていますがどうしても分かりません。教えてください。ちなみに前回の課題の定義は下の通りです。 void print_all_char(char *chs) { while(*chs!='\0') { printf("%c\n",*chs); chs++; } } よろしくお願いします。

  • C言語のプログラム(ポインタについて)

    /* コマンドライン引数で与えられた長い文字列を長さ10文字ずつに分割してp[100][11]に順番に入れ,表示するプログラム <例>./a.out 1234567890ABCDEFG p[1] = "1234567890" p[2] = "ABCDEFG" */ #include <stdio.h> int main(int argc, char **argv) { int i, len; char *str; len=0; for(i=1; i<argc; i++){ len += strlen(argv[i]); } if((str=(char *) malloc(len+1))==NULL){ perror("malloc"); exit(1); } strncpy(str,argv[1],10); for(i=2; i<argc; i++){ strcat(str,argv[i]); } printf("p[%d] = %s\n", i-1, str); } 上に示した処理をするプログラムを作成したいのですが、 今のソースは単に10文字表示するだけで、ここからどうしたらいいのか見当がつきません・・・・・。 なのでどういった改良をすればよいのか教えていただければ助かります。 /*配列aにファイルから値を読み込み、それを表示した後*/ /*配列に格納された値を下に1つずつずらして表示するプログラム(ポインタを使用して)を作成*/ /*(なお、一番上a[0]にはa[99]の値を入れる)*/ /*<例>*/ /*a[0] = 0 */ /*a[1] = 1 */ /*  ・ */ /*  ・ */ /*a[98] = 98*/ /*a[99] = 99*/ /*a[0] = 99 */ 1つずらしたもの /*a[1] = 0 */ /*  ・ */ /*  ・ */ /*a[98] = 97*/ /*a[99] = 98*/ (読み込むファイル[file-100.dat]には0~99の数字が1つずつ改行しながら入っています) できればこちらにも答えていただければ嬉しいです。 この問のソースは書きませんが、ファイルを配列に読み込んで表示してからの処理がいまいちわからず困っています。 すばやい御回答お待ちしております。よろしくお願いします。

  • C言語でのコマンドライン引数の内部での処理のされ方

    C言語でint main(int argc, char *argv[])とメイン関数を宣言します。 2番目の引数はC言語の文法的にいうと文字列へのポインタの配列だとおもいますが、一般的な関数でこの引数に値を渡すとすると、以下のように宣言されたポインタ配列を渡すことになるとおもいます。 ・宣言 char *pa[]; ・関数への渡し func(pa); 話が元に戻りますが、main関数でもらう場合は、プログラム外部から与えられた引数は(正確に言うとアドレス)、メモリ上ではC言語で書かれたexeファイルの外から実行時にプログラムファイルのメモリ上にコピーされるのでしょうか? 自分でもうまく表現できないのですが、 ・コマンドプロンプトで引数を与えて実行         ↓ ・プログラムファイルのメモリ上に引数がロードされる ということでいいんでしょうか? 自分でもなんだかうまく表現できないので、お暇な方でよろしいので、気が向いた人、回答ください。 よろしくお願いします。

  • main の引数には const 付けた方が

    C言語での質問です。 引数を取るような main 関数は int main( int argc, char *argv[]){~} とされていますが、argvの指す文字列を変更する、というのはいくら何でもまずいので、 int main( int argc, const char *argv[]){~} あるいは int main( int argc, const char const * const * argv){~} の方がいいのではないでしょうか? 何故、constを付けない形が出回っているのでしょうか?

  • C++の関数

    関数の課題が出たんですが分からないので教えてください。 第1引数と第2引数はchar型の1元配列であり、これら2つの配列(文字列)を連続して表示する関数catstringがあるものとする。 ただし第2引数にはデフォルトの文字列"あいうえお"が設定されている。main関数からキーボード入力で2つの文字列を取得し、 catstringの第1引数のみに文字列が渡される場合と第1、2引数ともに文字列が与えられるプログラムを作成せよ。 やってみましたがエラーが出てしまいます。 #include <iostream> #include <cstdlib> using namespace std; char catstring(char,char="あいうえお"); int main() { char a,b; cin>>a; cin>>b; cout<<catstring(a)<<endl; cout<<catstring(a,b)<<endl; return EXIT_SUCCESS; } char catstring(char x,char y) { char s; s=x+y; return(s); }

  • c言語

    (c++ではなくC89準拠) c言語について質問 (1) 関数名()と関数名(void)は違う意味 (2) mainの引数の型は(void)か(int argc, char *argv[]) (3) K&Rは標準c準拠でmain()という表記があります ということは、(1)の関数名とはmain以外の関数名で、 main()とmain(void)は同じなんでしょうか。

  • C言語の質問です

    #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fpi, *fpo; unsigned char idat; /* 引数のチェック */ if (argc != 3) { fprintf(stderr, "Usage: %s [input] [output]\n", argv[0]); exit(1); } /* 入力画像のオープン */ if((fpi=fopen(argv[1], "rb")) == NULL){ fprintf(stderr, "input file open error\n"); exit(1); } /* 出力画像のオープン */ if((fpo=fopen(argv[2], "wb")) == NULL){ fprintf(stderr, "output file open error\n"); exit(1); } /* 入力画像の読込み */ while (fread(&idat, sizeof(unsigned char), 1, fpi) == 1){ /* 2倍の変換 */ if (idat * 2 > 255) { idat = 255; } else { idat = idat * 2; } /* 変換データの書出し */ if(fwrite(&idat, sizeof(unsigned char), 1, fpo) != 1){ fprintf(stderr, "data write error\n"); exit(1); } } fclose(fpi); fclose(fpo); return (0); } このプログラムをグレースケール化のプログラムに修正してください お願いします