アプリ版:「スタンプのみでお礼する」機能のリリースについて

下記は、*inの<title>から<と>で挟まれたtitle
を得るためのソースです。
全くの完全では有りませんが。質問の内容は、
while文の中で、コンパイラーが出すエラーメッセージの
内容が理解出来ません。

下記が出ます。
01.c: In function 'main':
01.c:12:17: warning: assignment makes pointer from integer without a cast [enabled by default]
while(strstr((p=(*out++ = *in++)),">") !=0);
^
while文の中でのエラーで、キャスト無しの整数からポインターを作っているので
strstrの最初の引数を飛ばしました(デフォルトで有効)無視しました。と言う意味ですが。

要はこれはポインターで無いのでstrstrの引数としては無効ですと言う事でしょうか。
それであれば、ここではポインターに変換しないといけませんが。

実はこれに関しては、前の質問でその方法と言うのは分かったのですが。どうしても、ここでは
将来の勉強と言う意味で理解をしたかった物ですから質問をしました。

このエラーメッセージが出ない様にするためにはソースのどこを変更
すれば良いのでしょうか。今度は、コードがどうのこうのではなくて
説明をお願いします。

よろしくお願いします。後学の為にどうしても理屈を知りたかった物ですから。
宜しくお願いします。

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

int main() {
char *in="<title>";

char work[256];
char *out=work;
char *p;

while(strstr((p=(*out++ = *in++)),">") !=0);
*out='\0';
printf("%s",work);

return 0;
}

A 回答 (5件)

コンパイルエラーをとるためにどうするかという回答としては、以下のようになります。


while(strstr((p=(*out++ = *in++)),">") !=0);
上記を
while(strstr((p=(char*)(int)(*out++ = *in++)),">") != 0);
に変えます。

01.c:12:17: warning: assignment makes pointer from integer without a cast [enabled by default]
このエラーは、整数値をキャストせずにポインターに割り当てているというエラーです。
整数値とは、*outで取得される値:'V'の1文字です。(1文字も整数です)

最初に
p=(*out++ = *in++) により
Pには、"<"の1文字を格納しようとします。
従ってpが文字型であれば、警告は発生しませんが、ポインター型なのでエラーが発生します。
その為、
p=(char*)(*out++ = *in++)
とすれば
01.c:12:17: warning: assignment makes pointer from integer without a cast [enabled by default]
の警告はとれますが、

warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
の警告が発生します。

これは、整数値をキャストしているがサイズが違っているという警告です。
このコンパイラでは
ポインター型は4バイト、char型は1バイトなので
1バイトを4バイトのポインターにするには無理がある。と言っています。
そのため、1バイトを(int)でキャストし強引に4バイトにし、それをポインター型にキャストします。
それが、
p=(char*)(int)(*out++ = *in++)
になります。
これで、警告がとれます。

ちなみに、警告はとれますが、これをコンパイルして実行すると暴走します。
今回は、エラーメッセージをとるのが目的ということなので、ここまでにします。
もし、正しく動作するコードはどうなるかという質問をされる場合は、
なにをなさりたいのか。期待する結果は、どうなるのか。を明確にされたほうが良いと思います。
このソースでは、何をなさりたいのかがよくわかりません。
    • good
    • 1
この回答へのお礼

有難う御座います。

この辺の説明と言うのは通り一遍に本を読んだだけでは
かなりに理解をするのは難しそうです。

警告は取れると言うのは分かりましたが、実行すると暴走と言うのは
何がどんな理由で暴走するのでしょうか。どうせなら、最後迄理解を
したいと思いますので、お知えて下さい。

今回のプログラムと言うのは、char *in="<title>";から、<と>を外して、
titleと言う文字を取り出すのがこのプログラムの目的です。

中のプログラム云々と言うよりかはこれを作るに当ってコンパイラーで
エラーが出たので。何が原因でエラーになっているのだろうかと言うのが。

私が将来に渡ってC言語を使う時に、一つの壁になるのではと思った物
ですから質問をしました。perlはこんな細かい所は全く必要としない
物ですから、面食らっている所です。

これを作るに当ってのプログラム作成の問題は解決をしていますので。
最後に今回の説明の回答で暴走すると言う所が気になっています。

最後にこの暴走と言う所の質問の回答をお待ちしています。

宜しく回答願います。

お礼日時:2017/02/26 21:25

C言語で文字列を扱うときは


「C言語に文字列なんて存在しない」
というつもりでやるのがコツです。
あるのは「char型整数」と「char型整数の配列」と「char型整数の配列を連続して処理する関数」です。
あなたが「文字列」だと思っているものは、「char型整数の配列」の別の表記方法でしかありません。
※ あえて「char型整数」と書いています。「文字」ですら無いのです。

Perlでは、 1文字だけでも「文字列」でした。
しかし、C言語では、状況により
・char型整数
・char型整数の配列で、[0]=何かの文字に対応した整数 [1]=0
があります。
それに合せて、適切な処理を選びます。

Perlの「文字列操作」の技術は何一つ使えません。
他の言語のことを全て忘れて、C言語のことだけを考えるくらいでないと、やりたいことはできません。



8bitCPUが主流だった昔は、このように一行でまとめて書いて貴重なメモリやディスクを節約するのが良い方法でした。
しかし、今では混乱の元で、少なくとも初心者にはお勧めできません。

while(strstr((p=(*out++ = *in++)),">") !=0);
は書き直せば次のものと(ほぼ)等価です

while(1) {
 p = *in ; /* char型整数へのポインタ in が示す場所にあるchar型整数を取り出す */
 *out = p ; /* char型整数へのポインタ out が示す場所にchar型整数を書き込む */
 in ++ ; /* ポインタの位置をずらす */
 out ++ ;
 if ( strstr(p ,">") != NULL ) { /* どこから探したいの? */
  /* ループ継続 */
 } else {
  break ; /*ループ終了 */
 }
}


/* どこから探したいの? */ と書いたように、「どこから探したいのか」がまったく不明です。
変数p に入っているのは値(整数)だけで、それがどこから来たものかはまったく記憶されていません。
変数p だけでは「文字列として認識されるchar整数配列、つまり、'\0'で終わるchar整数配列」にはなりません。
&p で、pのアドレス(ポインタ)になりますが、&pの次が 0 である保証はありません。
いつまでたっても0が見つからずに終わらないかもしれないし、まったく関係ないところにある > を見つけてしまうかもしれません。

strstrを無理に使うなら
char w[2] ;
w[0] = p ;
w[1] = 0 ;
if ( strstr(w ,">") != NULL ) {
となるでしょう。

ただ、この場合、p が '>'を表す数値になっているかどうかだけ確認すればいいので、 strstrを使わずに p != '>' で判定すれればいいのです。
Perlでの「文字列で判定」というのが頭に残っているせいで、strstrやstrcmpを使おうとしたのが間違いです。




文字列の操作は、Cに詳しい人ほど「Cを使いたくない」と思うでしょう。
C以外の言語を知っている人ならなおさら「他の言語でやりたい」と思うでしょう。
C言語の面倒な部分、すなわち、メモリ管理、配列、ポインタといったこと抜きにはできないからです。
参考書の入門サンプル程度ならともかく、本格的な処理をしようとすれば、それは熟練者の領域なのです。

今の時代「C言語が速い」なんて都市伝説です。
特に、今回やろうとしていることは、「文字列操作」を速く処理がしたい、ということですよね。
相手は、昔から文字列操作や正規表現に定評のあるPerlの優秀な制作チームが作った「文字列操作プログラム」です。
勝つ自信、ありますか?

プログラムが遅い、と感じたら、まずは、現状分析です。
どこがボトルネックになっているかの調査が先です。

経験上、時間がかかるのは、ファイルの読み書き等です。
これは、メモリだけの操作に比べて、文字通り桁違いに時間がかかります。
しかも、ハードウェアに起因するので、CだろうがPerlだろうが大差ありません。
苦労したわりに、大して改善しなかった、という結果になると思われます。
    • good
    • 2
この回答へのお礼

有難う御座います。

余り、実はperlも深い所は知りませんが。私がこれを考えたきっかけ
と言うのは、試しにperlのlwpを使って14,000個のファイルをスパイダリング
した時に、数日を要した物ですから。

その時の処理にかなりのhtmlのタグの編集をしています。その処理はかなりの
ポインターが関わって来るので。これこそはC言語の番だと思った物ですから。

若しかしたら、C言語の方が早いのかなあと思った次第です。
後は、向学心ですが。それと理屈抜きに短い事は良いと思った次第です。
だらだらと長い行は単純に嫌だと言う癖みたいな物です。

お礼日時:2017/02/26 21:36

ツッコミ処はいろいろありますが、明らかな文法誤りは...



p=(*out++ = *in++)

の部分ですね。左辺は char* 型 (ポインター) なのに対して、右辺は char 型そのものを指しています。

更に...

strstr(...) !=0

これも左辺は char* 型 (ポインター) で右辺は const int 型が推定される 0 ですね。

あと、論理的に間違っている点として、初期化されていない wotk[] 配列内を strstr() 関数内で参照しています。そもそも strstr() 関数は文字列内をスキャンするので while ループさせる必要はないと思われます。

また、先頭の "<" が検索されていません。

ひとつひとつ丁寧に段階を踏んで、以下のようにするべきでしょう。



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

int main()
{
char *in = "<title>";

char work[256];
char *h;
char *t;


if(h = strstr(strcpy(work, in), "<"))
{
if(t = strstr(++h, ">"))
{
*t = '¥0';
}
}

printf("%s", h);


return(0);
}
    • good
    • 0
この回答へのお礼

有難う御座います。
確かに、色々とツッコミは有ると思います。

p=(*out++ = *in++) の部分はキャストしないと
コンパイラーでエラーになります。

strstr(...) !=0 の部分は、NULLと比較をしないと
いけないですね。

もそも strstr() 関数は文字列内をスキャンするので
while ループさせる必要はないと思われます。確かに
そうです。

然しどうしても、この*out++ = *in++の可能性に掛けて
見たかったのでこの様にしました。

また、先頭の "<" が検索されていません。この部分も
端折っています。

今回は、コンパイラーの出すエラーメッセージの意味を
私がどうしても知りたくて。プログラムを作る云々の前
にどうしても理屈を知りたかった物ですから質問を
しました。確かに、問題だらけですが。

今の私にこの手のコンパイラーが出すエラーメッセージ
と言うのは理解が出来ません。なので、どうしても将来
の事を考えて知りたかった物ですから。質問を出しました。

お礼日時:2017/02/26 21:53

pの型を見落していたので訂正します。



while(1) {
char c ;
 c = *in ; /* char型整数へのポインタ in が示す場所にあるchar型整数を取り出す */
 *out = c ; /* char型整数へのポインタ out が示す場所にchar型整数を書き込む */
 in ++ ; /* ポインタの位置をずらす */
 out ++ ;
p = c ; /* 大きさも違うし、整数→ポインタへと暗黙に変換しようとしているのが間違い */
 if ( strstr(p ,">") != NULL ) { /* pがどこを指しているか、わかってますか? */
  /* ループ継続 */
 } else {
  break ; /*ループ終了 */
 }
}

警告が出ている場所は
p = c ; /* 大きさも違うし、整数→ポインタへと暗黙に変換しようとしているのが間違い */
です。
ここで、 char型整数cをキャスト無しにcharへのポインタ pに代入しようとしています。
ここで、キャストで無理矢理型を合わせることで、警告を消すことはできます。
ですが、それは真の解決にはなりません。

ポインタp に入っているのはcの値(整数)です。
一番最初は "<title>" というchar型整数の最初の値 '<' になるので、 p='<' です。ASCIIコードなら p=60 です。
キャストでできるのは、
・「char型整数60」 を「ポインタと同じビット長の整数60」へ変換すること
・「整数60」を、「アドレス60番を指すポインタ」へ変換すること
だけです。
では、アドレスの60番がどうなっているか、というと、わかりません。
おそらくは、OSが管理している領域で勝手には触れないでしょう。そのため、プログラムが異常終了したり暴走して止らなくなるかもしれません。
ですが、「必ず暴走する」と断言することもできません。たまたまうまく動作するように見える場合も考えられます。

warning(警告)は、エラーではないので、コンパイル自体は正常に終了するし、無視できる警告もあります。
ですが、何か問題の元になりそうな箇所を警告するものです。

----
最近のコンパイラの最適化はすばらしいものがあります。
ですから、下手にソースコードで「高速化」を目指すより、見やすいソースコードにして、コンパイラの最適化にまかせた方が、効率のいいオブジェクトになることもあります。
実際、手許で -Sオプションでアセンブリにしたところ、1行で書いたのと、複数行に分けたのとで、ほぼ同じアセンブリになりました。

----
LWPを使っているのなら、時間のネックは、LWPによる通信でしょう。
これもCにしたから速くなる、というものではありません。
    • good
    • 0
この回答へのお礼

有難う御座います。確か、LWPの件ですがその時に使った
ファイルサイズは1GBでした。私は、C言語は知らなかった
ので、比較をしたかった物ですから。

何とも言えません。或いは単に私のperlのコーディングにも
問題が有るのかも知れませんが。

何か、スピードで比較が出来るのが有れば良いですが。私が
知って居る範囲では、昔の平面ペントミノで昔は数日を要し
ていたのがつい最近のC言語でやったら1分とかかっていない
と言うのが有りましたから。

perlのサンプルは知りませんが。今度探してやって見ようと
思います。

何でもそうですが。いくらでも作り方に依っては遅くは作る
事は出来ますから。何とも言えません。

お礼日時:2017/02/26 22:35

No1です。


>これを作るに当ってのプログラム作成の問題は解決をしていますので。
>最後に今回の説明の回答で暴走すると言う所が気になっています。
>最後にこの暴走と言う所の質問の回答をお待ちしています。

これを以下のような部分を追加しました。
追加したしたのは、変数の内容を印字するだけなので、本来の動作には影響しません。
--------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
char *in="<title>";

char work[256];
char *out=work;
char *p;

printf("ascii code of(<)=%d\n",'<');
printf("in_size=%d out_sieze=%d\n",sizeof(in),sizeof(out));
printf("in=%x out=%x\n",in,out);

while(strstr((p=(char*)(int)(*out++ = *in++)),">") != 0);
*out='\0';
printf("%s",work);

return 0;
}
----------------------------------
これを実行すると、添付の図のようになります。
OSにより強制終了させられます。
このように強制終了させられる原因はいろいろありますが、今回は
ROM領域をアクセスしているのが原因と考えられます。
まず、pのあたいですが、"<"のアスキーコードの値:60が設定されます。
strstr(p,">")の意味は、60番地から">"の文字列が存在する箇所を探しなさい。
ということになります。60番地はROMの領域になるはずで、OSはこのような領域を一介のアプリプログラムが
アクセスすることを禁止しています。その為、強制終了させられます。(これが暴走の意味です)
通常、pは、アプリケーションプログラムが確保した領域のアドレスが設定されるはずで、
inのアドレスが設定されるなら、添付の図のように403064(16進数)のような値になるはずです。
「しつこい様ですが、再度ポインタについて質」の回答画像5
    • good
    • 0
この回答へのお礼

有難う御座います。

何か昔のマシン語を見ている感じですね。
昔は、コンピュータの保守と言うのは、マシン語を触って修理をしていました。
その内にファームウェアのマイクロプログラム方式になりましたが。

大変ですよね。昔を思い出します。そうなんですか。難しいですね。
昔は大変だったんですよ。一つのマイクロ命令の語長が500ビット
でしたから。ロジックもそれなりにね。

而も、説明書は英語とフランス語だったり。私は結果的に英語も
フランス語も駄目だったけど。出来る人は両方出来たりした人も
いたなあ。

これで質問を閉じます。有難う御座います。

お礼日時:2017/02/26 22:48

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!