ページ 11

無題

Posted: 2009年4月15日(水) 14:26
by ひで
暗号のプログラムを作っています。
かなり昔に使われていた暗号でいまはまったく使われていない暗号ですが
自分で作ってみたくてはじめした。
しかしいざ作ってみるとエラーだらけで・・・ww
コンパイルは成功なのですが実行で問題が発生して終了してしまいます。;;
どこがおかしいのでしょうか?
int main(void)
{
        char a[1000];
        scanf("%s",a);
        printf("%s",apri_angou(a);
        return 0;
}

int apri_angou(char *c)
{
	char pc_form[/url]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','aa','ab'};
	int  u_f=0;
	int  p_f=0;
	int  p_a=0;
	char pc_answer[1000];

	while(u_f != 1000)
	{
		while(c[u_f] != pc_form[p_f])
		{
			p_f++;
		}
		p_f = p_f +2;
		pc_answer[p_a] = pc_form[p_f];
		p_a++;
		p_f = 0;
		u_f++;
	}

	return *pc_answer;

}
includeはわざと書いてません。

Re:無題

Posted: 2009年4月15日(水) 14:45
by 御津凪
(上のコードで本当にコンパイルは通ったのでしょうか?)
コードを見たところ、以下のような問題があります。

・構文的な理由でコンパイルが通らない(printf に閉じ括弧が無い)
・pc_form[/url] 配列の最後の二つが不正(コンパイルは通るが、先頭の文字だけ使われる)
・apri_angou に渡される文字列 c に対し、不定状態の場所(入力した文字列以降の余った領域)も暗号化している
・a~z以外の文字の検索でバッファオーバーフローする(pc_form[/url] 配列の範囲内で文字が一致しないため)
・printf で暗号化した文字列を出力しようしているが、apri_angou の戻り値は int で文字列ではない
・(文字列を返す場合)pc_answer はローカル変数なのでそのままでは返せない& NULL 終端文字が無い

Re:無題

Posted: 2009年4月15日(水) 18:35
by TSP
include を書かない理由がわかりません。

たかだか2~3行を書く手間を省く意味が本当にあるのでしょうか?

Re:無題

Posted: 2009年4月16日(木) 06:35
by ひで
printfの')'はここでの入力ミスでソースにはしっかり入力されています。
その他はこれでコンパイルできました。・・・・
それなのに実行できません。
どこをどうすれば実行されるのでしょうか?

Re:無題

Posted: 2009年4月16日(木) 08:51
by non
御津凪さんが、全部書いていただいているのに、「ひで」さんは何がわからないの?

Re:無題

Posted: 2009年4月16日(木) 09:28
by やそ
そうですねえ。
まずは御津凪 さんが指摘された項目をじっくり考えて修正して見てください。

Re:無題

Posted: 2009年4月16日(木) 16:16
by ひで
とりあえず>>2さんの項目を見て自分のわかる範囲で修正してみましたが、これ以上わかりませんでした・・・
ソースファイルを載せたので申し訳ないのですが一回見てもらって間違ってるところを教えて頂けないでしょうか?・・・・

Re:無題

Posted: 2009年4月16日(木) 16:17
by ひで
大変めんどうかとは思いますが、なにとぞよろしくお願いします。

Re:無題

Posted: 2009年4月16日(木) 17:25
by non
御津凪さんの補足をします。
>・apri_angou に渡される文字列 c に対し、不定状態の場所(入力した文字列以降の余った領域)も暗号化している
while(c[u_f] != '\0')のように文字列の最後には'\0'があるので活用します。

>・a~z以外の文字の検索でバッファオーバーフローする(pc_form[/url] 配列の範囲内で文字が一致しないため)
万が一入力してはいけない文字がないかチェックしておかないと
while(c[u_f] != pc_form[p_f])で抜けなくなっちゃいます。
絶対にa-z以外は入力しないのなら、とりあえずはこのままで。

>・printf で暗号化した文字列を出力しようしているが、apri_angou の戻り値は int で文字列ではない
char * apri_angou(char *c)にしなくてはいけません。
ついでに
return pc_answer;ですね。


>・(文字列を返す場合)pc_answer はローカル変数なのでそのままでは返せない& NULL 終端文字が無い
方法はいろいろありますが、一番簡単な方法として、
char pc_answer[1000];を関数の外に出してみたらどうでしょうか?
また、NULL終端文字の件は
pc_answer[p_a]='\0';をreturnする前に行います。

Re:無題

Posted: 2009年4月16日(木) 17:36
by toyo
いろいろやり方はありますが
int main(void)
{
	char user_form[1000];
	char pc_answer[1000];
//
	user_form,apri_angou(user_form, pc_answer);
//
	printf("入力された文字「%s」を暗号すると「%s」になります。\n",user_form, pc_answer);
//
}

void apri_angou(char *c, char *pc_answer)
{
//
}
のようにmain( )側で暗号用の配列を用意しておく方法もあります。

Re:無題

Posted: 2009年4月16日(木) 18:07
by 御津凪
non さん補足ありがとうございます。

添付されたコードでは、こちらで指摘している箇所ほぼ全てが原因でプログラムが落ちていますので、
non さんの補足を元にとりあえずは直せるだけ直してください。

ちなみに、
添付されたコードを元にちゃんと動く実行可能ファイルを作ってみました。

ついでにコードでは未実装になっている、
さらに複雑化した暗号を勝手ながらに実装してみました。
(その他はいじってません)

ソースの方も同梱していますが、
そのままでは正解コードになってしまうので、暗号化してあります。
文字コードを128ずらしているだけなので、
20数行程度のコードでこの暗号化したファイルを暗号解除するプログラムが出来るはずです。

あと、こちらのコンパイル環境は MinGW (gcc version 3.4.5) です。

※ちなみにひでさんの添付コードは VC++ では正しくコンパイルできないはずなので
回答者の方に注意しておきます。
関数宣言がないのと、最後の方にある関数がおかしいためです。

(そういえばひでさんはコンパイル環境を提示していませんね。)

Re:無題

Posted: 2009年4月16日(木) 18:21
by ひで
えっと・・・・
とりあえず助言いただいたことについては大方理解することができました。
ありがとうございます。

ただ・・・nonさんがおっしゃった
char pc_answer[1000];を関数の外に出してみたらどうでしょうか?
が理解できないのですが・・・・・
関数の外に出すとはどういうことでしょうか?

なんか無知の私がC言語に手を出してみなさんに迷惑掛けてしまい大変申し訳ないです・・;;

Re:無題

Posted: 2009年4月16日(木) 18:23
by ひで
御津凪さん
なんかすみません・・・
私のコンパイル環境は御津凪VC++です。

Re:無題

Posted: 2009年4月16日(木) 18:41
by 御津凪
たとえば、
char* get_pc_answer1( void ){
    char pc_answer[1000];
    return pc_answer;
}
このコードの場合、 get_pc_answer1 関数のに pc_answer があります。
この関数の中にある変数 pc_answer は「ローカル変数」と呼ばれ、
関数から抜けると消滅します。
つまり、上の関数ではローカル変数 pc_answer を返していますが、
pc_answer が消滅するため、不正な値が返されてしまいます。
char pc_answer[1000];

char* get_pc_answer2( void ){
    return pc_answer;
}
このコードの場合、 get_pc_answer2 関数のに pc_answer があります。
この関数の外にある変数 pc_answer は「グローバル変数」と呼ばれ、
プログラムが実行されている間、ずっと存在します。
つまり、上の関数ではグローバル変数 pc_answer を返していますが、
pc_answer が消滅することはないため、正しい値が返されます。
ただし、どの関数からも変更を加えられることが出来るため、注意が必要です。

ちなみに、
char* get_pc_answer3( void ){
    static char pc_answer[1000];
    return pc_answer;
}
このコードのように static (静的を意味する単語)をつけることにより、
グローバル変数と似たような性質を持つローカル変数にすることが出来ます。
これは、ローカル変数のように関数の中でのみ操作でき、
グローバル変数のように消滅することのない特徴を持っています。
そのため、正しく pc_answer を返すことが出来ます。

コンパイル環境が VC++ なら、警告が出るような気もしますが、
自分で定義した関数は、できるだけコードの最初の方(この場合は main 関数の前)に
関数宣言しておいたほうが良いでしょう。

# コンパイル環境に誤字があるのはスルーします。

Re:無題

Posted: 2009年4月16日(木) 18:41
by non
>char pc_answer[1000];を関数の外に出してみたらどうでしょうか?

グローバル変数にしてみたら?という意味です。

Re:無題

Posted: 2009年4月16日(木) 18:43
by non
おっと、御津凪さんから詳しい説明が・・・・(汗・・)

Re:無題

Posted: 2009年4月16日(木) 19:04
by ひで
なるほど@@
詳しい説明ありがとうございます。
一度試してみます。

Re:無題

Posted: 2009年4月16日(木) 19:14
by ひで
で・・・できました@@
なんかすっごい迷惑掛けたみたいで大変申し訳ありませんでした;;

みなさんはこのぐらいのプログラムは簡単に作れるんでしょうね・・・・
未記入になってる"更に複雑な暗号"も今回教えていただいたものを参考に作っていこうと思います。

私もみなさんのようなプログラマーになれるようにもっと勉強に励みます。
これからも質問させていただくことがあるかもしれません(というより絶対)が、そのときはよろしくお願いします。


ホントに助かりました。

Re:無題

Posted: 2009年4月16日(木) 19:17
by ひで
解決です^^

Re:無題

Posted: 2009年4月16日(木) 19:31
by 御津凪
解決したということなので、
とりあえず上の方で添付している、
ソースを暗号化したプログラムコードを記載しておきますね。
#include <stdio.h>

int main( int argc, char* argv[/url] ){
    int result = -1;
    FILE* in;
    FILE* out;
    int ch;

    if(argc < 3) return -1;

    in  = fopen(argv[1], "rb");
    out = fopen(argv[2], "wb");

    if(!in || !out) goto end;

    while((ch = fgetc(in)) != EOF){
        fputc((unsigned char)ch + 128, out);
    }

    result = 0;
end:
    if(in) fclose(in);
    if(out) fclose(out);
    return result;
}
実はこのプログラムは暗号化したファイルを渡すことで、復号化することも出来ます。

この暗号処理は1バイトデータに 128 を足しただけのものだから、
128 + 128 = 256 でオーバーフローして元に戻るというトリックです。