GEEKy Script Writer [perl and more!]
You should permit the JavaScript!!
スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
C++
C++
評価のときは
Cのまま
[C++]の続きを読む
スポンサーサイト

テーマ:どーでもいいこと - ジャンル:コンピュータ

[自分用メモ][Win32]ReadFile()の謎
ReadFile()がうまく動作してくれない。なんで?

BYTE buf;
hFile = CreateFile(
pFile, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH
| FILE_FLAG_RANDOM_ACCESS, NULL
);
if (SetFilePointer(hFile, (LONG)offset, NULL, FILE_BEGIN)
== INVALID_SET_FILE_POINTER)
return errmsg("失敗");
if (ReadFile(hFile, &buf, 1, &rdsize, NULL) == 0)
return errmsg("失敗");

これでbufにカレントファイルポインタから1byte読み込まれるはずなんだけど、読み込まれない。
本来05が読み込まれるはずが、なぜか00

ためしにbufをBYTE buf[3]と宣言して、ReadFile()にはbufを渡し、3byte読み込むようにしてみるとbuf[1],buf[2]には正しい値が読み込まれるけどbuf[0]は上と同じで00。

BYTE buf[2]にしてみるとbuf[0],buf[1]とも00でだめ。

というわけで現在深追い中。orz
[C][ネタ] while(1)より普通な半永久ループ
以下で紹介するコードは、コンパイラに大きく依存する。
このコードの動作を確認した環境は以下の通り。
OS:WindowsXP
コンパイラ:Borland C++Compiler 5.5
オプション:最適化なし


#include <stdio.h>

void foo() {
int s1[4];
char s2[4];
s2[0] = 'a';
s2[1] = 'b';
s2[2] = 'c';
s2[3] = '\0';
puts(s2);
s1[6] = foo;
}

int main() {
foo();
return 0;
}


これで、


abc
abc
abc
..

と、資源が許す限り永久にループします。

s1[4]とs2[4]でスタックにバッファを確保すると、s1かs2からスタック内のアドレスが
わかります。そして、s1,s2より上位のアドレスには、その関数がreturnするときに戻
る場所(retアドレス)が格納されています。この2つを組み合わせれば、retアドレスを
弄って、本来戻る場所とは別のところに戻せることが分かります。
そのアドレスを自分自身のアドレス(foo)にすれば、ループするだろうってことです。
所謂 Buffer Overflowというやつですね。

foo()内のスタックはこんな感じです。(適当なアドレスを基準にしています)

0012FF70 s1[0]
0012FF74 s1[1]
0012FF78 s1[2]
0012FF7C s1[3]
0012FF80 s2[3] s2[2] s2[1] s2[0]
0012FF84 EBPレジスタ
0012FF88 retアドレス

目的は0012FF88に格納されている関数return時の戻り先(retアドレス)を書き換えること
ですから、s1かs2のアドレスにいくつ足せばretアドレスを参照できか調べます。

ここではs1のほうを使います。
0x0012ff88 - 0x0012ff70 = 0x18 なので、s1[6]で0012FF88の中身、つまりretアドスが
参照できますね。

あとは s[6] = foo; とすれば、戻り先をfooにすることでループできるでしょう。

しかし、このコードは半永久的と書いた通り問題があります。
retアドレスをfoo自身のアドレスに書き換えることによって、またfooが呼び出される
のですが、正確に言うと呼び出されるというよりは、fooのコードにジャンプするん
です。呼び出しとジャンプではどこが違うのか?

関数呼び出しは x86アセンブリで callという命令を使い、
ジャンプは普通に jmp アドレス です。

callの動作はただ、ジャンプさせるのではなく、戻り先のアドレスをスタックに格納し、
その後にjmpさせます。

call命令でスタックに格納されたretアドレスを上書きしているこのコードだと、foo()が
再び呼び出されても、retアドレスがスタックに格納されていないので、前回より1つ上位
のアドレスにretアドレスが書き込まれ、繰り返し呼び出されていくうちに、スタックを使
い切ってしまいます。スタックを使い切るとアクセス違反で止まってしまうでしょう。

この問題を回避する方法は凄く簡単です。というかむしろ、これは間違ったやり方ですね。
なのでその"正しい"方法でのコードを書いてみたかったけど長くなるので興味を持った方
は考えてみてください。すぐ分かります。しかし、その方法でも環境に依存してしまうかも
しれません。

"ネタにはなるが、実務では使えない。それがローレベルテクニック(略してローテク)というかhack"
【一行コード】CとPerlで標準入力からの英字の大文字と小文字を入れ替え
またもやmixiネタ

入力 getchar
出力 putchar
大文字と小文字を逆転するプログラムを書く。


書いた人大学生らしいけど、こんなのもわからんのかと怒る前に今回はCとPerlで書いてみよう
それも中軸の処理は一行で

まずはPerl

map{if($_<91 && $_>64){$_+=32;}elsif($_<123 && $_>96){$_-=32;}printf("%c",$_);}unpack('C*',<>);

んー、まぁこんなもんでしょ
んじゃ次はC

#include <stdio.h>
int main() {
char c;
while(c=getchar()) putchar(c > '@' && c < '[' ? c+32 : c > '`' && c < '{' ? c-32 : c);
return 0;
}

ちょっと無理やりかな?

追記
Perlでもっと短いのでけた

map{printf("%c",(0..64,97..122,91..96,65..90,123..255)[$_]);}unpack('C*',<>);

うーんトリッキー

追記2
更に短く

map{print chr((0..64,97..122,91..96,65..90,123..255)[$_]);}unpack('C*',<>);

もうここまでくると何の処理だかわからんね

追記3
泣きたくなるほどに短いやつ

while(<>){tr/a-zA-Z/A-Za-z/;print;}

そうか、これでよかったんだ

テーマ:プログラミング - ジャンル:コンピュータ

マッチしたやつを置き換える
mixiのC言語とC++言語コミュを覗いてたら僕にもできそうなのが質問として投げ出されていた

はじめまして。
どうしてもわからないC言語の課題があるので質問させていただきます。
―課題―
文字列と照合したいパターン文字列をそれぞれchar型配列sとpに読み込み、関数ptnmchを利用して、文字列sにおいてパターンpと一致する部分を全て*で置き換えて出力するプログラムを作成しなさい。
つまり、shinzoshin zoshinzoと入力して、照合したい文字列がshinzoの場合、
    ******shin zo******
となりたいのです。
よろしくお願い致します。


バグつぶし含め20分ぐらいでかいてみた


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

int ptnmch(char*,char*);
void str_replace(char*,int,int,char);

int main(int argc,char **argv){
int i = 0;
char hoge[] = ".c";
while(i < argc){
if(ptnmch(argv[i],hoge)){
printf("OK -> %s\n",argv[i]);
}else{
printf("NG -> %s\n",argv[i]);
}
i++;
}
return 0;
}

int ptnmch(char *p,char *s){
int matchflag = 0;
int flag = 0;
int i = strlen(s);
int m = 0;
int l = 0;
while(p[m] != '\0'){
if(p[m] == s[0]){
l = 0;
flag = 1;
while(++l < i){
if(p[m+l] != s[l]){
flag = 0;
break;
}
}
if(flag){
matchflag = 1;
str_replace(p,m,m+i,'*');
}
}
m++;
}
return matchflag;
}

void str_replace(char *str,int s_n,int e_n,char r){
while(s_n < e_n){
str[s_n++] = r;
}
return;
}

実行結果(.cにマッチするようにしてある)

# ./a.out hogehoge.c c.ge .choge.c
NG -> ./a.out
OK -> hogehoge**
NG -> c.ge
OK -> **hoge**

・・・いけてるよね?

そしてmixiではソース公開しないという、ね

テーマ:プログラミング - ジャンル:コンピュータ

copyright © 2005 GEEKy Script Writer [perl and more!] all rights reserved.
Powered by FC2ブログ.
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。