VC PlusPlus:Link Error LINK2019 未解決のシンボル…で参照されました。 対処方法 新しいページはコチラ
提供: yonewiki
(→対処方法) |
(→対処方法) |
||
83行: | 83行: | ||
=== ''' 対処方法 ''' === | === ''' 対処方法 ''' === | ||
− | '''1.安全ではない古い関数が使われるパターン''' | + | |
+ | ==== '''1.安全ではない古い関数が使われるパターン''' ==== | ||
scanf, printf, strlen, strcmpといったC言語の基本とも言える関数を使ったときに発生します。昨今ではオーバーフローしないような、より安全な関数を使うのが定説になっていますが、勉強のため、ためしにgccとか古いVisual Studioで主に開発しているプログラマからプログラムをもらってきたときとかに発生します。古い関数自体は新しいVisual Studioでもコンパイルできるので、関数は新たにobjファイル化することが出来て問題なく使えます。これがlibファイル化された中で呼び出して、使われている場合です。この場合、他のlibファイルの中で古い関数が提供されている必要があり、リンカ設定をしておかないと駄目です。Visual Studioで最初から設定されているリンカ設定されているlibファイルからは、古い関数が消えています。つまり、参照先が無い。リンクエラーだよ。ってなります。なんで、こんな嫌がらせするのか?と思ってしまいますが、Micorsoftは昔ながらの関数群を標準ヘッダファイルでインライン定義(ヘッダファイルの中に関数の定義をして、ついでにプログラムの内容までヘッダファイルに記述する方法です)して内部で持たせるように変更したかったようです。libファイルによる外部ファイル参照を必要としない方法で扱いたかったんすかね。ナカナカ思い切った互換性を保たない変更です。 | scanf, printf, strlen, strcmpといったC言語の基本とも言える関数を使ったときに発生します。昨今ではオーバーフローしないような、より安全な関数を使うのが定説になっていますが、勉強のため、ためしにgccとか古いVisual Studioで主に開発しているプログラマからプログラムをもらってきたときとかに発生します。古い関数自体は新しいVisual Studioでもコンパイルできるので、関数は新たにobjファイル化することが出来て問題なく使えます。これがlibファイル化された中で呼び出して、使われている場合です。この場合、他のlibファイルの中で古い関数が提供されている必要があり、リンカ設定をしておかないと駄目です。Visual Studioで最初から設定されているリンカ設定されているlibファイルからは、古い関数が消えています。つまり、参照先が無い。リンクエラーだよ。ってなります。なんで、こんな嫌がらせするのか?と思ってしまいますが、Micorsoftは昔ながらの関数群を標準ヘッダファイルでインライン定義(ヘッダファイルの中に関数の定義をして、ついでにプログラムの内容までヘッダファイルに記述する方法です)して内部で持たせるように変更したかったようです。libファイルによる外部ファイル参照を必要としない方法で扱いたかったんすかね。ナカナカ思い切った互換性を保たない変更です。 | ||
106行: | 107行: | ||
続きはまた気が向いたら書くので記事は増えていく予定。 | 続きはまた気が向いたら書くので記事は増えていく予定。 | ||
+ | |||
+ | |||
+ | ==== '''2.C言語とC++言語の混在による呼び出し関数の互い違い''' ==== | ||
+ | Visual Studio C++を使っているつもりでも、C言語との互換性は高く、どちらを使っているか意識しなくなりがちですが、C言語で書かれたライブラリをC++の呼び出し規約で呼び出すと関数名が違うというエラーが発生します。呼び出しているライブラリがC言語で書かれた方式なのか?C++言語で書かれた方式なのか?を見極めるには、Dumpbinコマンドで、ライブラリの中の変数名を確認することで解決します。 | ||
+ | |||
+ | <syntaxhighlight2 lang="text"> | ||
+ | dumpbin /LINKERMEMBER (確認したいライブラリの名前).lib | ||
+ | </syntaxhighlight2.> | ||
+ | |||
+ | |||
+ | ソースファイルがある場合は、ライブラリを作成するプロジェクトの中のプログラムファイルの拡張子が.cであったり、.cppであったりしています。C++でもCの規約に従ったライブラリを作成することができるので、拡張子だけでは判断しきれないかもしれません。 | ||
+ | |||
+ | |||
+ | で、どう違っているのか?なのですが、C++の場合はライブラリに書き出す関数の名称に名前マングリングという動作が追加されていて、zcallocのような関数はC言語方式だと_zcalloc、C++言語方式だと、?zcalloc@@YAPAXPAXII@Z、というような名前になります。なんじゃソレ? | ||
+ | |||
+ | |||
+ | かなり、わけわからん@@とかYXPAとかどっから来たん?ってなると思います。そもそもマングリングってなんかヤラシイ名前で恥ずかしい(Embarrassing)わ。とか言っている日本人のなんと多いことか。チコちゃんはしっています。マングリングとは! | ||
+ | |||
+ | |||
+ | 修飾~~~っ! | ||
+ | |||
+ | |||
+ | 英語のmanglingは日本語で修飾という意味です。マングリ返しとは関係ないし、ペロペロしたりもしません。C++では同一の関数名を名前空間を変更することによって定義できます。namespase ですね。だったり、オーバーロードという多重定義によって、引数が違うだけの関数名との切り分けも考慮した命名規則があります。命名規則は別の場所で解説するとして、元の名前がきちんとわかるようにわかりにくく修飾されています。元の名前は以下のようなundnameコマンドで確認できます。 | ||
+ | |||
+ | <syntaxhighlight2 lang="text"> | ||
+ | >undname ?zcalloc@@YAPAXPAXII@Z | ||
+ | Microsoft (R) C++ Name Undecorator | ||
+ | Copyright (C) Microsoft Corporation. All rights reserved. | ||
+ | |||
+ | Undecoration of :- "?zcalloc@@YAPAXPAXII@Z" | ||
+ | is :- "void * __cdecl zcalloc(void *,unsigned int,unsigned int)" | ||
+ | <syntaxhighlight2> | ||
+ | |||
+ | よく、dllを吐き出すプログラムでは、必ずextern "C"(囲われた部分はC言語の扱いで、という方法です。)で定義しましょうとか、言っている教則本やSiteがあったりしますが、名前マングリングの良さを分かっていないか、良さを教えるのを省いているということです。コンパイラによって、修飾規則が違うため、わけのからない技術で相互乗り入れも出来ず、dllの活用の幅が狭くなります。ようするに悪だとしているのだと思います。名前空間や、多重定義を利用するという点でメリットはあります。つまり作ったプログラムが同じコンパイラを使うもの同士だけが使えるdllだってあっていいわけです。なんなら、C言語の呼び出し規約で使いたいなら勝手に名前マングリングの結果を使って呼び出せばええじゃろ。って考え方もあります。これまでに紹介した方法で関数名は掌握できます。とは言ったモノの、Extern "C"を付けないDllは少ないかもしれない。 | ||
+ | |||
+ | |||
+ | ちなみに | ||
+ | |||
+ | |||
+ | <syntaxhighlight2 lang="cpp"> | ||
+ | #ifdef __cplusplus | ||
+ | extern "C" { | ||
+ | #endif | ||
+ | |||
+ | void zcalloc(void *,unsigned int,unsigned int); | ||
+ | |||
+ | #ifdef __cplusplus | ||
+ | } | ||
+ | #endif | ||
+ | <syntaxhighlight2> | ||
[[VC PlusPlus]]に戻る | [[VC PlusPlus]]に戻る |