2020/10/19 18:39
fukken
× printfが無いとなぜか動かない ○printfがあるとなぜか動く
2020/10/19 18:47
k2wanko
なんでprintfがあると動くのか気になって夜も6時間くらいしか寝れない
2020/10/19 18:58
ysync
「自分の書いたコードを信じてはいけない。」はい。
2020/10/19 19:06
rryu
オーバーランしてるけどたまたま直後にいた奴を犠牲にして動いているのあるある。
2020/10/19 19:14
aya_momo
昔は常識だったが、このような知識も失われていく。
2020/10/19 19:30
el-condor
ホンマC言語怖い。自分もメモリ管理迂闊な方なのでproductionではCは可能な限り書くまい(nativeが必要なら極力Rustかgolangで書く)と決めている
2020/10/19 19:34
hirolog634
なぜ動くのか、他の環境でも再現性があるのか、とても気になる。解説が欲しい。
2020/10/19 19:35
tarume
未定義動作を利用した闇最適化に敢然と立ち向かいバグを隠蔽することに定評のあるprintf先生
2020/10/19 19:41
cameraojisan
知だ
2020/10/19 19:44
tamtam3
ヒューマン・リソース・マシーン(steamゲーム)で、終盤のとある問題で似たトラブルを引き起こして???に。理由はプログラムの組み立てそのものの間違え… トホホ store.steampowered.com
2020/10/19 19:46
hatest
/* 虫(バグ)よけのおまじない* /
2020/10/19 20:07
tessy3
学生の頃、分析計算のプログラムでこーゆーのが頻発してた。どう書けばいいのかさっぱりわからなかった。
2020/10/19 20:20
Rinta
マジでC怖い。
2020/10/19 20:23
quick_past
ちゃんと初期化しないから。この書き方でも動くような言語でハードに近い部分の実装をされる方が怖いと思う。
2020/10/19 20:31
remcat
>/*十分な大きさ*/と白々しいコメントがあるにも関わらず領域が足りておらず
2020/10/19 20:41
poponponpon
“おわりに おわりです。 ”
2020/10/19 20:43
mohno
「printf() が呼ばれると動作している風なのは偶々の結果でありプログラムが正しい訳ではない」だけだよね。C言語は「問題が起きやすい」だけで、直接関係なさそうな部分が動作に影響するのはC言語に限らないと思うぞ。
2020/10/19 20:46
theatrical
"/*十分な大きさ*/と白々しいコメントがあるにも関わらず領域が足りておらずバッファオーバーランを起こしている" 最高
2020/10/19 20:53
napsucks
はい
2020/10/19 21:02
robokichi
全角について言及無し?/printf書いてデバッグするのはスクリプト言語までと思われます(バイナリにコンパイルするとソース通りに動かないことも…)
2020/10/19 21:14
honma200
聞いたことはあるもののついに現物を見られた。まさかのCの仕様による最適化と非決定性(?)によるもの。勉強になった!
2020/10/19 21:25
ryuichi1208
マルチスレッドでprintfが遅いからデバッグしやすくなったってことはあったけどこのケースは見たこと無いなぁw
2020/10/19 21:26
cive
予想➡️1. printf した後にスタックに積まれる値が0じゃ無いことがわかってるなんて、プロかよ... 2. printfでスタック領域を確保するなんて高度なプログラミング技術だなあ...
2020/10/19 21:33
muuuminsan
再現させられる例を狙って出せるのは凄い
2020/10/19 21:37
baronhorse
printf は reentrant じゃないよな
2020/10/19 21:43
tockri
printfで使われなくなると最適化で変数の宣言部が消えてスタック領域が切り詰められてたまたま参照できてた場所が領域外になるのか。読めばなるほどってなるけど、長いコードの中からこういうの見つけるの無理そー。
2020/10/19 21:47
akiat
自分のコードなは信じてないので「動け、動け!」って思いながらコード書いています。
2020/10/19 21:55
samu_i
この位置にイプシロンがないと収束してないんだ・・・。
2020/10/19 22:00
gui1
テストと称して、画面を開けたり閉じたりを繰り返して想定通りの結果が出たときだけスクショを撮ってるそこのきみ。みんな知ってんだぞ。隣の会社だから指摘しないけど(´・ω・`)
2020/10/19 22:01
ardarim
偶々動く、なんてことはふつうあり得ないわけで(0とは言わない)、突き詰めれば必ず原因がある。ただこの手の問題は正攻法では原因にたどり着かないのが難しい所以。原因の特定、理解には一段上の知識が必要。
2020/10/19 22:04
mztns
“Wandboxで実行”
2020/10/19 22:06
kkobayashi
例になるほどとなった
2020/10/19 22:11
Helfard
何で動くんだ?
2020/10/19 22:13
hdampty7
実際にはグローバル変数が乱立しててポインタ地獄で構造体に設定された関数ポインタに引数で関数ポインタ渡してたりしてそういう地獄の先にある絶対いらないprintfを外すとなぜか落ちるんだよな。
2020/10/19 22:13
John_Kawanishi
「printf() が呼ばれると動作している風なのは偶々の結果でありProgramが正しい訳ではない」のまで判るが何が間違ってるのかか全然判らん(焦。C言語いやProgrammingムズカすぎ
2020/10/19 22:19
Dragoonriders
なぜか動いてしまう系は別のところでメモリ蹂躙しとるんやで。ってことが可能なのがCだからメモリ確保をしつこく確認していくしか手がない。Cでしか書けない狭いメモリ空間もあるわけで、バグ出しもAIにやらせよう。
2020/10/19 22:29
gfx
"printf() が呼ばれると動作している風なのは偶々の結果でありプログラムが正しい訳ではない"
2020/10/19 22:30
ghostbass
「動作している風なのは偶々の結果でありプログラムが正しい訳ではない」「自分の書いたコードを信じてはいけない」肝に銘じる
2020/10/19 22:39
chokovi
printfが入ることでメモリ空間がうまいこと初期化されたんでない?
2020/10/19 22:39
inatax
なんでかわかんないけど動くからヨシッ!
2020/10/19 22:43
p1ass
こんなことが起こるのか
2020/10/19 22:50
momonga_dash
たのしい
2020/10/19 22:52
everybodyelse
マイコンのプログラムだと、printfでもコストがかかるので、微妙にタイミングがずれて挙動が変わるのある気がする。
2020/10/19 23:01
ydf
printfがあると動くのは、多くの場合[1]未初期化変数に何かした [2]変数のbuffer overflowのどちらか。
2020/10/19 23:04
Falky
こういうのサクッと実例出せるのすごい。単純なセグフォですら、普段雑に書くとすぐセグフォるのに、狙って出そうとすると全然出なくてなんでだよクソッてなる
2020/10/19 23:06
adwhing
何で動くの…
2020/10/19 23:21
shirontan
今日これに近いことがあり私は訳わからんまま不要に見えるコードを書き込みました。明日もちゃんと動きますように。ナムナム。もしかして謎コードじゃなく祈りで動いてるのかな?
2020/10/19 23:33
turanukimaru
解説すると printf が参照している文字列がメモリの先頭や buf[/*十分な大きさ*/256] 直後に配置されて、初期化されてない next がメモリの先頭を見に行ったときとかに文字列を壊しながら動く。printf 消すと最適化されて消える
2020/10/19 23:36
aves_ramphastos_toco
「何はなくとも変数には初期値突っ込んどけ、0でもnullでも結構だが未定義禁止」というルールが明文化されてる所もあるがな。
2020/10/19 23:44
harumomo2006
わりと命に関わるプロジェクトのソースコードでなぜか2~3回UPDATEしないと更新されない謎のDBがあった
2020/10/19 23:44
tsz
因果律を否定するバグ blog.practical-scheme.net
2020/10/19 23:55
PEEE
こういうメモリ領域上たまたま動くやつもあるけど、デバイスドライバとかハードウェアが絡むやつだとprintfの処理でタイミングが変わって動いたりすることがある。デバッグログ入れたら再現しなくなったとかね…
2020/10/19 23:56
Sinraptor
最初に思い浮かんだのが、Hello worldだった。
2020/10/19 23:57
monacal
printfが全角というところにセンスを感じた
2020/10/20 00:23
yoiIT
C言語はスタックの一部の領域をローカル変数として扱っていて、printfはスタックをけっこう喰う関数なのでこの手の挙動が発生する。みたいな話をどこかで聞いた。
2020/10/20 00:26
suquiya0
WAO
2020/10/20 01:01
dirtjapan
だから -Wall とか -Werror そのへんのオプションはつけとけと。ワーニング残したままcommitしてはダメ。/あとオーバーランアンダーランはvalgrindをちゃんとオプション変えて使おうな。
2020/10/20 01:10
uesugi_sbw
おもしろい
2020/10/20 01:13
flower11
組み込みの場合、コメントがあるだけで、他のメモリ領域を参照しにいって、動くとか動かないとかあった。いずれもどこかでメモリ確保ミスがあるせいだが…
2020/10/20 01:24
shikiarai
たまによくわからない呪文が唱えられるC言語
2020/10/20 01:51
oakbow
C難しいな
2020/10/20 01:56
nakag0711
これに警告をださないならそういうコンパイラを使うのが悪いという気もするが、それよりprintfの書き方が奇妙なのが目を惹く。C99以降だとcompound literalとして無名配列をこう書けるらしい。C++には正式にはない
2020/10/20 02:11
ene0kcal
こういうキテレツコード博覧会を(集めて)開催して欲しい。ある意味知見だし。
2020/10/20 02:28
nice801
めっちゃおもしろい
2020/10/20 02:48
room661
「ときどき遭遇する現象だが再現するのがめっちゃ難しい」問題。あっぱれ。
2020/10/20 03:11
toritori0318
こういうの懐かしい(最近C書いてない
2020/10/20 03:36
sodapop444
注釈が本文
2020/10/20 03:54
K-Ono
いやこういうの読むときって、ふつうその「偶々」がなんだったかって期待するじゃん?
2020/10/20 04:12
hdkINO33
“C言語は高級アセンブラではなく抽象化された高級言語であり、抽象化された部分の内部を覗こうというのは褒められた使い方ではない。”
2020/10/20 04:12
miquniqu
状況として、動かない状態から動かすためにprintfするんじゃなくて、動く状態からprintfはずしたら動かなくなったから外せなくなった時のコメントなんだよな。
2020/10/20 04:51
programmablekinoko
結構ある。gccだと甘いから逃すけどClangだとSEGV出して落ちるとか
2020/10/20 05:09
dgen
なるほどね。結構初歩的なところでも呪文になる可能性はあるのか。でもまあ今回の例みたいなことは変数の定義をきっちりやっておけば問題なさそう。
2020/10/20 05:14
kagehiens
すげ~の一言。ちょっと探偵っぽい原因究明。
2020/10/20 05:46
uzusayuu
Warning出るよね?(この記事にケチつける気ではなくて、最近のコンパイラならこういうバグを見つけるのは昔より楽なのでは?と。メモリサニタイザーとかもあるし)
2020/10/20 06:16
kamei_rio
"main() 中に printf() が無い場合は fizzbuzz() 中の未初期化の変数 next は偶々初期値が 0 となりループが機能しない" そうなんだ
2020/10/20 06:25
ken39arg
何で動くのか理解できなすぎて悶々としそうだったけど、注釈のおかげでスッキリできた
2020/10/20 06:47
chikoshoot
偶々(たまたま)
2020/10/20 07:03
buzztaiki
未定義な振る舞いを使う人は未定義な振る舞いを受け入れなければいけない。
2020/10/20 07:08
lorenz_sys
筆者さんの Qiita の記事をいくつか読んでみたがこの道40年以上の経験があると推察する。おそらく本業についてはあまり関わらないよう趣味の領域のみ記事にしている感じ。
2020/10/20 07:10
atsuououo
副作用すごい
2020/10/20 07:13
electrolite
Cですなあ。コンパイラを変えると動かなくなるパターン。
2020/10/20 07:26
typex2
背筋がゾッとしますな。。
2020/10/20 07:32
etaoinshrdlu
2020/10/20 07:33
joseph150
この手の話の解説を初めて見た
2020/10/20 07:35
BRITAN
“おわりに おわりです。”
2020/10/20 07:40
Lagenaria
これは有用な記事だ。
2020/10/20 07:46
circled
90年代のLinux推しの奴らは「C言語はコンパイルすればどんな環境でも動く移植性の高い言語なんだ」と目をキラキラさせていた。
2020/10/20 07:47
kotetsu306
例1は静的解析ツールやコンパイラウォーニングで見つけられるとして、例2は機械的に見つけるのは難しいなぁ
2020/10/20 07:49
keidge
Cで開発していた頃、たまに遭遇した。物理メモリに近い言語は謎の挙動多いよね。
2020/10/20 07:51
rideonshooting
デバッグモードなら何故か(何故かじゃない)動いてくれるのあるあるですね。メモリ破壊怖い
2020/10/20 07:53
ooblog
#C言語 「初期値0~コンパイラやコンパイルオプション、動作環境に依存」「bufの直後にspec~printf(spec) ~をコメントアウト~コンパイラの最適化機能により不要なオブジェクトと判断され削除~バッファオーバーラン~表面化」
2020/10/20 07:59
wapa
こういうローレベルなところまで理解しないと解決しないようなコードを産まないために、コーディング規約でのお約束や静的解析ツールがあるんだな、と。
2020/10/20 08:05
perl-o-pal
ハイゼンバグ(動いてしまうのがおかしい)//一読したとき、nextの初期化忘れ見逃してたり、不自然なdo〜whileループなのに見落としたり、自分はレビュアーとしてはもう駄目かもしれんな…
2020/10/20 08:08
x100jp
おう、解説増えてる。JSでもalert入れてデバッグしようとすると動くとか結構起きちゃう。
2020/10/20 08:09
sin4xe1
経験値
2020/10/20 08:09
koji28
printfがあると(gccコンパイラの最適化動作により)動く(´・ω・`)
2020/10/20 08:23
lalala360
バグっているときにちゃんと落ちてくれる言語はありがたいなって、CからJavaに移ったときに思った。
2020/10/20 08:30
su_zu_ki_1010
COBOL→Javaと使う言語が変わった僕、何言ってるんだろうと思ったけどこうやって現物を見るとそういうことがあるのかぁと勉強になった。
2020/10/20 08:31
slash_01
おもしろ
2020/10/20 08:33
new3
例題もさらっと書けるの偉い。良記事。
2020/10/20 08:33
mas-higa
例1 って printf でスタックの狙った位置に非 0 を書き込んでるってこと? スゴない? / あ、解説が書いてあった。
2020/10/20 08:34
nekomimist
シリアルへのログ出力でタイミングが変わって動きが変わる経験の方が多いな / いやまあ大抵race conditionなんだけど
2020/10/20 08:37
mogami74
本文がすっきり短くて注釈が長いこのスタイル、読みやすくていい。見習っていきたい。 というかSEOのために無駄に前置きの長いブログ記事は滅びろ。
2020/10/20 08:38
honeybe
昔の「prinftが無いとなぜか動かない」案件の一部はコンパイラの最適化のバグ関連で引き起こされていたと聞いたことがある(要出典
2020/10/20 08:41
daishi_n
C言語はスタックにローカル変数を配置する実装が多いから、関数呼び出しでローカル変数に使われる領域が汚染されることはままあるよね。バッファオーバーフローしない適切な初期化と代入処理は重要
2020/10/20 08:44
jbase
nextが変数名なのなんか気持ち悪い。
2020/10/20 08:46
Gim
30年前の大学の卒研を思い出す。当時はibmのメインフレームでfortranだったと思うが、何故かprintfを削ると動かなくてそのまま提出した。懐かしい。
2020/10/20 08:48
sds-page
Cのソース直接直せるならいいけどCで作ったDLL呼んでる高級言語だとクエリ文字列におまじないが必要だったりする
2020/10/20 09:01
shun_libra
C言語特有の「頼んだら、やってくれる」という、危険なほどシンプルで、故に本来専門家でなければ手に余るくらい原始的な処理系が、何故か広く使われるようになってしまったことに、そもそもの問題がある気がする。
2020/10/20 09:08
workingasadog-kt
コーディングの難しさをよく表してると思う。バグが無くならないことを理解できない素人に見せたいけど、これを理解できる人は一定水準以上なんだよな…
2020/10/20 09:10
eiki_okuma
問題はこのメモリ破壊を起こしているコードが10万行のどこかにあり、そしてデバッグプリントを仕込むだけで挙動が変わってしまう時である。ああ怖い。
2020/10/20 09:21
ockeghem
printfデバッグという言葉はprintfでバグの発生箇所を見つける意味で使われるが、こちらはprintfを挿入することでバグをつぶす(つぶしてない)ので本当のpritnfデバッグ
2020/10/20 09:24
hasegawatomoki
なるほど〜〜〜
2020/10/20 09:33
airj12
printfというよりDebugビルドだと動くのにReleaseビルドだと動かない系の奴
2020/10/20 09:42
zyzy
例題だからシンプルだけど、実際のコードはこうはいかんし、原因を見つけるの大変よなぁ。
2020/10/20 09:42
tettekete37564
有名なのはスレッド関連じゃなかったっけ?しかし一目でその書き方は・・・って胸騒ぎのする箇所がやはり問題になるもんだな。
2020/10/20 09:44
kaanjun
6時間しか寝られないのは体に悪いので、疑問を解決して1日8時間寝てほしい。
2020/10/20 09:46
enemyoffreedom
メモリ管理は魔境
2020/10/20 09:50
hatebu_ai
ナビがないと走らない車って感じかな(だいぶ違う)
2020/10/20 09:52
khtokage
こういうの好きw 脚注が本番だ。
2020/10/20 09:57
yarumato
“例1 所謂FizzBuzz問題。例2 所謂FizzBuzz問題。printf() をコメントアウトすると異常終了するプログラムとなった。gcc のコード生成の特性として バッファオーバーランを起こす。”
2020/10/20 10:02
otation
C言語の動作は恐ろC
2020/10/20 10:04
neo_Neutral
このパターンで怖いのがデバッグ用のログを消すと不具合が出るパターン
2020/10/20 10:11
deep_one
未定義変数を参照できてしまう言語実装が恐ろしい。
2020/10/20 10:15
monopole
Cあるあるだ。ある処理系でたまたま動いたコードが別の処理系で大惨事を引き起こす、、、
2020/10/20 10:18
richard_raw
“自分の書いたコードを信じてはいけない。” せやな……。
2020/10/20 10:22
ginpei
未初期化変数とバッファオーバーランの周囲でprintf()を利用して意図的に「たまたま動く」状態を用意した例。前者は関数呼び出しで未初期化領域が非0で埋まることを期待、後者は壊れても影響のないバッファを配置。
2020/10/20 10:37
z1h4784
最近の言語はこれくらいコンパイラ側で何とかしてくれるから、こういう不具合に当たることは少なくなったな
2020/10/20 10:40
satomi_hanten
組み込みだと年がら年中よくある話なんだが、結局「ハードの仕様とあってない」か「ハードの仕様が間違ってる」ためタイミングがズレるとかいう話なので別枠すかね。(ROMの初期化が間に合わないとか色々)
2020/10/20 10:59
bigchu
自分の書いたコードを信じてはいけない。
2020/10/20 11:04
knok
コンパイラのお気持ちを把握されている
2020/10/20 11:11
havanap
セキュリティ的にもよろしくない
2020/10/20 11:32
hahihahi
compound literal使ってんのもスタック汚しのためだな(文字列リテラルは静的記憶域期間になるがcompound literalなら自動記憶域期間になる)
2020/10/20 11:37
NOV1975
こういうのって、コケてくれれば調べられるんだけど、動くと気づきを得るしかなくて辛いデバッグになりがち
2020/10/20 11:55
xxix29
たまたま動いている状態にすぎないからこそのおまじない。
2020/10/20 12:04
otihateten3510
C言語って、恐いよね
2020/10/20 12:20
natu3kan
変数をしっかり定義し、メモリの使う場所や初期化もしっかりしておかないと、初期化されてない前提じゃないと動かないみたいな状況になったりとかはある。
2020/10/20 12:22
modoroso
面白い
2020/10/20 12:35
kiyo_hiko
Cこわ…手を出さんとこ
2020/10/20 12:35
yamadar
例を出して解説できるの凄い
2020/10/20 12:44
maroh
昔DOS/Vマシンで、ビルド時が日本語モードか英語モードかで結果の異なるコードがあった
2020/10/20 12:48
mitsubushi
にくいねっ! アルファベットを全角にするとそもそも動かないからだ!
2020/10/20 12:53
t-tanaka
未定義動作はコンパイラが警告出してくれるけど,バッファーオーバーランは,ほんと機械的には防げないから大変。
2020/10/20 13:25
sharaku3eyes
もしかしたら動かなくなってから誰かがprintf入れてfixした可能性…
2020/10/20 13:30
nicht-sein
マルチスレッドが絡むと「偶々動く」が頻発するので、人類にはまだマルチスレッドは早すぎる
2020/10/20 15:02
OkadaHiroshi
これらの例とは関係ないけど、昔の処理系でprintfの内容で別なライブラリをリンク(単純なprintfなら機能が限定されたフットプリントの小さい物)するので、そのライブラリの関係で動作したりしなかったりする事があった
2020/10/20 15:51
tonocchox
意外とprintfじゃなくてstdio.hのincludeとそれに伴う定数の初期化とかだったりして。よく考えたら、未初期化の変数に何が入ってるかわからないのって、数学っぽくなくて面白いな
2020/10/20 16:35
nyokkori
わぁ、卒業研究で作ったプログラム思い出して来たぞ、、、。
2020/10/20 16:42
bell_chime_ring238
これぞプログラム言語って感じがする(古老感) あまりにもこういう事例が起きすぎるんで、最近の言語やスクリプトでは気にしなくても済むような仕様になってきてるが、それがまた「?」になる人を増やすという…
2020/10/20 16:48
terazzo
狙って鼻から悪魔を出せる人すごい
2020/10/20 17:24
mashori
素晴らしき解説。
2020/10/20 18:46
shiju_kago
原因はわからないけどなぜか入れたら動く部分を『おまじない』って名付けた人秀逸すぎる
2020/10/20 19:43
konishika
へー。おもしろい
2020/10/20 23:18
raahii
“自分の書いたコードを信じてはいけない。”
2020/10/21 15:14
versatile
こういうのよくあったなぁ昔
2020/10/23 07:34
tanakh
C++書いてた時よくこういうのに悩まされたなあ。今だと-fsanitizeでいけるんかなって試してみたら、例2のほうはエラーになってくれたけど、例1のほうはaddressでもundefinedでも出なかった。これを拾ってくれないのか…。