• ベストアンサー

関数に値の代入 [C言語]

C言語初心者です。関数とポインタについて勉強していたのですが、ふと関数の型を知りたくなってVC++で型を調べてみたんです。そしたら、void型で引数のない関数の型は void (__cdecl*)() となっていました。voidと__cdeclはわかります。 そしてこれ型に*が入ってるじゃないですか。ということは関数はポインタということになると思います。なので私はもしかしたら値の代入ができるのではないか、と思ったのです。早速、 f1=f2;(f1とf2は型と内容の同じ関数) や、 (*f1)=(*f2); としてポインタの中身や参照先の関数の実態の値を処理中に書き換えてみようとしました。ですが、多分そうなるとは思ったのですが、コンパイルエラーが出ました。 《エラーの内容》 error C2106:'=':左のオペランドが、左辺値になっていません。 warning C4550:式は引数リストのない関数として評価します。 関数を書き換えようとすること自体馬鹿げていることは重々承知です。でも、微かにいけそうな気がするんですよ。代入させたくないなら関数の型は一律constにすると思うし(実際関数をconstをつけて宣言してもOKだった、(プロトタイプ有り無しでも))、関数への代入は問答無用で駄目なのならばそういうエラーメッセージを出すと思うんです。 関数の書き換えは100%無理でしょうか?それとも関数を書き換える方法があるでしょうか?回答よろしくお願いします。

noname#113783
noname#113783

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

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.2

関数の型をどうやって調べたのか知りませんが... 例えば、 void foo(void); という関数の型は、void (void)型になります。そして、関数名を大多数の演算子のオペランドにする場合には、(配列名がそうであるのと同じように)関数へのポインタ型に暗黙的に型変換されます。この型がvoid (*)(void)です。 関数へのポインタ型のオブジェクト(変数)の宣言は、 void (*pf)(void); のように行います。このpfに対してであれば、 pf = &foo; あるいは pf = foo; のように代入することができます。 > 関数の書き換えは100%無理でしょうか?それとも関数を書き換える方法があるでしょうか? 普通は行いませんが、関数の書き換えは不可能ではありません。ただし、それはポインタの更新とはわけが違います。

noname#113783
質問者

お礼

関数の型はわざと型を調べたい変数にその型に変換できない型の変数を代入して、それをコンパイルして出てきたエラーメッセージから調べました。 >>関数の書き換えは不可能ではありません 関数の実装を書き換えることができました!char型のポインタに関数のアドレスを代入して、そのポインタ経由で値を代入するソースを書いてコンパイルしたら、ワーニングメッセージしか出ませんでした。ただ、適当な値を書き込んだので実行したら案の定プログラムが停止しました(汗)。もし、うまい位置に機械語の命令の値を代入したら「プログラムの処理中に関数を書き換える」みたいなトリッキーなプログラムも書けたりするのかな・・・? >>関数へのポインタ型に暗黙的に型変換されます。 配列と似ているんですね!違うところもありますが。 回答ありがとうございました!

その他の回答 (3)

  • MrBan
  • ベストアンサー率53% (331/615)
回答No.4

> うまい位置に機械語の命令の コードの実行が許されるような「うまい位置」にもご注意を。 #DEP、VirtualProtect

noname#113783
質問者

お礼

関数を書き換えるにはwindowsに色々と許可をとらないといけないんですか! 確かにWindowsが簡単に処理内容書き換えさせてくれるほどセキュリティゆるいってこともないかぁ。でもこうして見るとウイルスプログラムなんて簡単に作れちゃうんですね(作る気ないですけど!)。 回答ありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.3

> もし、うまい位置に機械語の命令の値を代入したら「プログラムの処理中に関数を書き換える」みたいなトリッキーなプログラムも書けたりするのかな・・・? キャッシュの整合性を合わせることもお忘れなく。

noname#113783
質問者

お礼

データがキャッシュにも保存されていることがあるんですね。 初心者には難しい・・・1度コンピューターの仕組みの本を読んでみようかな。 回答ありがとうございました!

  • prophetok
  • ベストアンサー率44% (13/29)
回答No.1

関数ポインタで検索すればいろいろと参考になる情報あり。 ソートする際、コンパレータ(比較するための関数)の関数ポインタ、コールバック用関数のポインタを引数として利用するのは一般的です。 出てきたコンパイルエラーは char a[10]; char b[10]; char *p; a=b; //これはエラー p=a; //これはOK b=p; //これはエラー と同じで定数に対して値を代入しようといているのと一緒ではないですか。

noname#113783
質問者

お礼

関数ポインタならアドレスを変えられますね! >>定数に対して値を代入しようといているのと一緒ではないですか。 エラーの内容等を見た時に定数に値を代入した時と違うな、と感じました。 回答ありがとうございました!

関連するQ&A

  • C言語のvoid型ポインタを使いたいのですが…

    C言語のvoid型ポインタを使いたいのですが… 関数の引数として、void型ポインタを使おうと思ったのですが、内部でどのように処理すればいいのかわかりません キャストすれば問題なく使えるとのことですが、どの型でキャストするのかをどのように判断するのかがわかりません 具体的には、画像処理で画像の構造体をいくつか作ったのですが、それぞれの構造体ごとに関数を書くと関数が多くなるので、void型ポインタでまとめてつくろうとしています どのように型の判断を行えばいいのかを教えてください

  • C言語での関数の引数の受け渡しについて

    C言語での関数の引数の受け渡しについて教えてもらいたいのです。 char *p=Goo;  というポインタpがmain関数で定義され、このポインタpをある関数 void func(・・・) に渡すことは出来ますか? つまりポインタを実引数として扱うことはできるのかという事ですが・・・ int p=10; とかだったら、 void func(int test) の関数には、main関数で func(p) で仮引数testにわたせると思うんですが・・・ もし出来るようでしたら、関数の渡し方と定義の記述を教えてください。 どうか宜しくお願いします。

  • こんなコンパイルエラーがでます。

    OS:WinNT4.0 環境:VC++6.0 MFC コンパイル時に次のエラーが出て来ました。 error C2664: 'qsort' : 4 番目の引数を 'int (const void *,const void *)' から 'int (__cdecl *)(const void *,const void *)' に変換できません。 (新しい機能 ; ヘルプを参照)スコープ内でこの名前を持つ関数でターゲット型に一致するものはありません。 qsort関数の引数関数を'int xxxxxxx(const void *,const void *)に宣言したところこのエラーが出てきました。 何がなんだかさっぱりわかりません。 ぜひお願いします。

  • C言語(引数)

    はじめまして。 C言語を習い始めたものです。 関数を定義するとき、よく耳にする、仮引数や実引数があると思います。 仮引数は関数の定義内で値をうけとる変数のことであり 実引数は関数を呼び出す際に渡す値を実引数というらしいのですが どこからどこまでを仮引数と呼ぶのかわかりません。 例えば、 fの関数の定義内で ↓があるとします。 (関数にする意味はないのですが、確認のためあしからず・・・) double f(double x) {     x=5;     return(x); } この場合、仮引数とよばれるものは double f(double x)の xが仮引数であって x=5;のxは仮引数と呼ばないのでしょうか?? もしそうならば void f(double x) { printf("%f",x); } のprintf("%f",x);内のxは仮引数とよぶのでしょうか? 質問の内容が意味不明かもしれませんが よろしくお願いします。

  • 【C++】関数ポインタの代入

    C++の関数ポインタについて質問です。 下記のように関数ポインタを宣言し、3通りの代入を行ってみました。 (3)のように関数名の頭に&を付けた場合と(2)のように&を付けなかった場合で 全く動きが同じになってしまうのですが、何故なのでしょうか? ------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; void Func1(){ cout<<"Func1が呼ばれました。"<<endl; return; } int main() { //(1) void (*fp1_1)(); fp1_1 = Func1; fp1_1(); //(2) void (*fp1_2)()=Func1; fp1_2(); //(3) void (*fp1_3)()=&Func1; fp1_3(); getchar(); return 0; }

  • 関数の引数をvoid*でキャストする

    最近見かけたCのプログラムで、関数の引数の型は void* なのですが、その関数を使うときに 引数をvoid*でキャストしていました。 例えば、 func ( (void*) p ); こういうことです。 私の知っている知識では、 void* と 任意の型のポインタは キャストなしに相互に代入可能です。 関数の引数でも、キャストは要らないものだと思っていました。 そうすると、引数を void* でキャストするのは無意味だと思うのですが、・・・ 違うのでしょうか。処理系によるとか。 逆に、関数の引数の型がchar*などで、渡すものが void* のときはどうなのでしょうか。 下のプログラムは、関数byte_orderの引数の型はvoid*ですが、int型へのポインタ( &a )を設定しています。私の環境では、コンパイルエラーも警告もないし、動作も正常です。 #include <stdio.h> #include <string.h> void byte_order(void *vp) { char char_array[4]; strncpy(char_array, vp, 4); printf("出力します:%x %x %x %x\n", char_array[0], char_array[1], char_array[2], char_array[3]); } int main(void) { int a = 0x12345678; byte_order(&a); return 0; } このプログラムは単なる一例であって、質問はバイトオーダに関するものではありません。 また、C言語の質問であって、C++ではありません。

  • bsearch関数の呼び出しで

    C言語の深いところまで理解しようとしてます。今まで使わないだろうと思っていた関数へのポインタ なのですが、 2分探索のところで bsearch関数というのが出てきました。 この関数は第5引数に比較関数を引数にするのですが p = bsearch(&ky, x, nx, sizeof(int), (int (*)(const void *,const void *))int_cmp ); という形でサンプルソースには載っています。 この (int (*)(const void *,const void *))int_cmp の部分なのですが、まず戻り値をキャストするなら int (*)ではなく(int *)ではないでしょうか。 それとint_cmpは比較関数なのですが、引数が左側に来る、というところが納得できません。 ちなみにソースファイルをcppのままだとコンパイルできませんでした.cに拡張子を直したらコンパイルできました。 説明が足りないところがあったら指摘してください。お願いします。

  • 関数に0を代入??

    研究に使うため,C言語のプログラムを読んでいます. 下記のようなプログラムがあるのですが,関数のプロトタイプに0を代入しています,この部分が意味が分からないので,どなたか分かる人がいたら教えてください. class CLASS{ public: virtual void setVar (std::string& rcValue) = 0; }; void ConfigLineChar::setVar( std::string& pvValue ) { *m_pcPar = (Char)atoi( pvValue.c_str() ); } 不必要な部分は削除して書いています.

  • C言語の高階関数についてです。

    C言語の高階関数についてです。 double f ( (*g)(double) ){...} とすると「関数を引数にする関数」が作れますが、 「関数を引数にする関数」を引数にする関数って作れますか??? その場合は、引数のところどうやって書けばいいでしょうか??

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

    新版 明解C言語 入門編 この本でC言語を勉強しています。 授業とかではなく自主的にやっています。 入力した文字を出力するプログラムを添字演算子なしで作る問題(演習11-1)を解いてみました。 void put_string(const char *s) { while(*s) putchar(*s++); } int main (void) { char s1[128]; printf("type something: "); scanf("%s", s1); put_string(s1); return(0); } これについて二つの疑問があります。 (1)constと指定されているのにインクリメントされていたら値が変わってエラーが出ないのはなぜか (2)main関数でないほうの関数で*を全て外すと 「パラメータ 's' は const signed char 型として定義されているので signed char * は渡せない(関数 main )」といったエラーが出るのですがなぜか

専門家に質問してみよう