質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

5337閲覧

C言語の関数の返り値でreturnを明記しない場合。

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2017/08/07 14:10

いつも,クオリティの高いご回答いただきお世話になります。

現在,PHPスクリプトの以下のような関数と同様の関数をC言語で実装しようとしています。

再現したいのは
http://php.net/manual/ja/function.strpos.php
上記の関数です。
機能は,第二引数を第一引数の文字列から検索し最初に見つかった位置を返す関数です。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <malloc.h> 5 6 7int php_strpos(char* search, char* needle) { 8 // 検索文字 9 int needle_length = strlen(needle); 10 // 検索対象 11 int search_length = strlen(search); 12 // needleの先頭の値と同じバイト数が見つかった位置 13 int inner_i; 14 // フラグ 15 int flag = 0; 16 // 検索文字が検索対象より短い場合のみ検証 17 if (needle_length <= search_length) { 18 for (int i = 0; i < search_length; i++) { 19 if (*(search + i) == *(needle)) { 20 inner_i = i; 21 for (int k = 0; k < needle_length; k++) { 22 if (inner_i + k < search_length) { 23 if (*(needle + k) == *(search + inner_i + k)) { 24 flag = 1; 25 } else { 26 flag = 0; 27 continue; 28 } 29 } 30 } 31 if (flag == 1) { 32 return (inner_i); 33 } 34 } 35 } 36 return (-1); 37 } else { 38 return (-1); 39 } 40} 41 42int main (int c, char* param[]) { 43 int offset; 44 offset = php_strpos("あいうえおABCDEニードルテスト", "ニードル"); 45 printf("->%d<-", offset); 46 printf("\r\n"); 47} 48 49

上記の内容で大方の動作は確認できたのですがphp_strposという関数の中身でreturnをしている箇所がありますがふとした時に

C

1int php_strpos(char* search, char* needle) { 2 // 検索文字 3 int needle_length = strlen(needle); 4 // 検索対象 5 int search_length = strlen(search); 6 // needleの先頭の値と同じバイト数が見つかった位置 7 int inner_i; 8 // フラグ 9 int flag = 0; 10 // 検索文字が検索対象より短い場合のみ検証 11 if (needle_length <= search_length) { 12 for (int i = 0; i < search_length; i++) { 13 if (*(search + i) == *(needle)) { 14 inner_i = i; 15 for (int k = 0; k < needle_length; k++) { 16 if (inner_i + k < search_length) { 17 if (*(needle + k) == *(search + inner_i + k)) { 18 flag = 1; 19 } else { 20 flag = 0; 21 continue; 22 } 23 } 24 } 25 if (flag == 1) { 26 // return (inner_i); 27 } 28 } 29 } 30 // return (-1); 31 } else { 32 // return (-1); 33 } 34}

上記のようにreturnの箇所をコメントアウトしました。
すると
main関数の中のprintfがなんらかの整数値を返すという結果がでました。
ただその数値がどこが原因で返却されているのかがわかりません。

どこが原因で関数内にreturnがなくても整数値が返却されるのでしょうか?
ご教授のほどよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubato6809

2017/08/09 06:39

php_strpos()関数の中の、3つのreturn文をコメントアウトすると、main()のprintf()が値を返すことがわかった・・・どんな経緯でわかったのか、謎めいた話です。少なくとも、このままでは理解できない話です。もしかして、こういうことですか? 「(例えば、どこでリターンするか、printfデバッグをしようとして?)return文の代りにprintf()文を書いた、そうしたら、そのprintf()が返したと思われる値がmain()関数の offset変数に代入されたようだ、即ち、printf()の返した値がphp_strpos()関数の戻り値になったように見えた・・・」というようなことですか?
退会済みユーザー

退会済みユーザー

2017/08/09 12:29

こんばんわ,経緯としては,関数の処理を記述している最終に動作チェックで都度コンパイルしていたんですけど,当初その際にループを抜けた先にたまたまreturn文がなかったようです。それにしてはなぜかint型の値が帰ってきてたので?となったわけです
rubato6809

2017/08/09 13:04

全然、合点がいきません。それはphp_strpos()関数を修正していたのではないのですか?「php_strpos()の中のprintf()が整数値を返す」だったら理解できますが、何故それが「main関数の中の」printf()が整数値を返す、になったのですか?
rubato6809

2017/08/09 13:15 編集

お使いのCコンパイラはGCCですか?32bit/64bitいずれでしょうか?それと、アセンブリ言語に触れた経験はお持ちですか?おありなら、CPUの種類もお聞かせ願えればと思います。
退会済みユーザー

退会済みユーザー

2017/08/09 13:18

>何故それが「main関数の中の」printf()が整数値を返す、になったのですか? でなかったですね,php_strposの返り値です。それをprintfしていたとき出力された値が整数値だったので何故returnがないのに値がかえってくるのだろうか?となったわけです。ちなみに全く低級言語の経験はありませんよ。
rubato6809

2017/08/09 13:41

つまり、こういうことですか? printf()関数が整数値を<返した>ことを問題にしているのではない。 printf()が整数値を<表示した>、即ち main()関数の中のprintf()が整数値を<表示>した、 その値は、return文を持たないのにphp_strpos()関数が返した値のようだ・・・
退会済みユーザー

退会済みユーザー

2017/08/09 13:46

はい,返り値を返したと表現した対象の関数を誤っていましたね...失礼しました。
rubato6809

2017/08/09 14:04 編集

何が疑問の核心なのか、ようやく理解できた感じです。まあ、ありがちな疑問であって、既に回答がついているじゃないかと、私なんかは思うのですが、、アセンブリ言語の経験が皆無だとすれば、ピンとこないのでしょうね、、、
guest

回答5

0

規格では無く実装の話をすると、返り値用に使われているレジスタにたまたま入っている値を、返り値の型のデータ(この場合はint)と見なして、扱います。

投稿2017/08/07 14:19

otn

総合スコア84531

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

たまたま動いてしまうかもしれませんが、それは「未定義の動作」(=何がどうなっても責任を負わない状態)です。

関数を終了させる}に到達し,かつ関数呼出しの値を呼出し元が使う場合,その動作は未定義とする。

void型の関数ならもちろん問題ないのですが、そうでない場合は(よほど何かを意図してやるのでなければ)書いてはいけないコードです。

投稿2017/08/07 14:49

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2017/08/08 14:07

ということは、returnなしで末端に到達しても、返り値を使わない呼び方をすればいちおう合法、ということになってしまうんだ
guest

0

「呼び出し先関数がint型のreturn値が返却した」という解釈は適切ではありません。

「呼び出し元関数が、呼び出し先関数終了時における状態からint型のreturn値を強引に作った」というようなものです。より厳密にいうならば「C言語の文法上、動作の定められていない状態となり『たまたま試したものはそのような結果になった』だけ」です。

投稿2017/08/08 14:30

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Cygwinのgccで、returnをコメントアウトしたコードをコンパイルすると警告が発生しました。

bash

1$ gcc tmp2.c -Wall 2tmp2.c: 関数 ‘php_strpos’ 内: 3tmp2.c:40:1: 警告: 制御が非 void 関数の終りに到達しました [-Wreturn-type] 4 } 5 ^ 6/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/../../../../lib/libcygwin.a(libcmain.o): 関数 `main' 内: 7/usr/src/debug/cygwin-2.8.2-1/winsup/cygwin/lib/libcmain.c:37: `WinMain' に対する定義されていない参照です 8/usr/src/debug/cygwin-2.8.2-1/winsup/cygwin/lib/libcmain.c:37:(.text.startup+0x7f): 再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `WinMain' に対して) 9collect2: エラー: ld はステータス 1 で終了しました

但し、gccに-Wallオプションをつけています。gccでコンパイルするときには、理想的には-Wall
オプションをつけてコンパイルすべきです。但し、大量のオープンソースなどをコンパイルする場合
などはうまくいかない場合もあります。
/usr/lib/gcc/以下はWindows環境特有のメッセージたと思いますが、
「警告: 制御が非 void 関数の終りに到達しました」というメッセージは非常に重要なことを
伝えています。return hogehoge;すべき関数で、returnされずにブロックの終わりまで来ましたよ、
と言っています。

ですから、Cコンパイラでコンパイルする場合は警告のレベルをできるだけ上げるようにするとよいです。
C言語の仕様は多少問題を含んでいるかもしれませんが、Cコンパイラはそれほどバカではありません。

投稿2017/08/07 21:26

anndonut

総合スコア667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

anndonut

2017/08/08 21:03

だれが低評価をつけてるかは知らないけれど、クソな書き方をしたらどうなりますか?という質問に、クソな書き方をするなと回答したまでですけどね。
guest

0

そちらをベストアンサーにしましたか。
return文が無い時にもphp_strpos()関数が値を返したのは

返り値用に使われているレジスタにたまたま入っている値を、返り値の型のデータ(この場合はint)と見なし

たからです。その値は「たまたま」入った値だから「未定義の動作」なわけです。私なら核心を突いたotnさんを推しますが、まあ良いでしょう。

因みに、返り値用のレジスタとは、例えばx86 CPUならEAXレジスタの事です。実は、return文の有無に関わらず、関数は常に何かの値を返していると言えるのですが、それはアセンブリ言語を経験すれば普通に理解できます。不可解に思える事が、アセンブリ言語レベルで解明できる事は少なくないのです。先日の、ローカル変数のアドレスを返すと変数が上書きされてしまう件もそうです。

さて、余計なお世話でしょうが、php_strpos()関数を見た率直な感想は、やけに複雑だという事です。

<string.h> をインクルードして利用できる文字列操作ライブラリ関数の一つ、strstr()はphp_strpos()関数と同等の関数です。ただ、strstr()はポインタを返すので、単純に置き換えられませんが、strstr()を使えば、とてもシンプルに実装できます。

もうひとつ。strlen()を用いて検索文字列と検索対象、それぞれ文字列長を求めるのであれば、一致するかどうかの検査を早めに打ち切ることができます。さらに、これをヒントに、お手元のコードを改善できるのではないでしょうか。

C

1int php_strpos_2(char* search, char* needle) 2{ 3 int needle_length = strlen(needle); // 検索文字 4 int search_length = strlen(search); // 検索対象 5 int upto; 6 7 // 検索文字が検索対象より短い場合のみ検証 8 if (search_length < needle_length) 9 return (-1); 10 11 // 2つの文字列が重ならなくなれば、もはや検査不要 12 upto = search_length - needle_length; 13 for (int i = 0; i <= upto; i++) { 14 if (strncmp(search + i, needle, needle_length) == 0) 15 return i; 16 } 17 18 return (-1); 19}

投稿2017/08/10 15:11

rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問