これは何?
C++でアスタリスクをつけすぎると端末が落ちる という素晴らしい記事がある。
ふと、もっと簡単なコードでたくさん *
をつけられるな、という事に気づいたので試した。
*
のつけかた
こういう感じ。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
int main()
{
intptr_t ********a;
*(intptr_t*)&a = (intptr_t)&a;
printf("a=%p, *..*a=%jd\n", a, ********a);
return 0;
}
ほんのり未定義動作感があるが、実用上は悪くない。
実行すると
a=0x7ffee5828808, *..*a=140732748957704
みたいな出力が得られる。
限界に挑戦。
ruby でこんなコードを書いた。
# CC="gcc-7"
CC="clang"
def test_aster(n)
aster = "*"*n
src=<<~"SRC"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
int main()
{
intptr_t #{aster}a;
*(intptr_t*)&a = (intptr_t)&a;
printf("a=%p, *..*a=%jd\\n", a, #{aster}a);
return 0;
}
SRC
File.open( "hoge.c", "w" ){ |f| f.puts(src) }
puts %x((time #{CC} -O0 hoge.c ) && time ./a.out)
puts( "n=#{n} err = #{$?}")
$?==0
end
n=1
loop do
break unless test_aster(n)
n*=2
end
a = [*(n/2)..n]
p(a.bsearch do |n|
!test_aster(n)
end)
gcc-7 の場合。
gcc-7 (Homebrew GCC 7.5.0_4) 7.5.0 で試した。
幸い端末が死んだりはせず、最終的には
135275
が出力された。つまり、13万5274個までは OK。13万5275個で死ぬ。
ちなみに結果が出るまで数時間を要した。一回のコンパイルに 20分 以上かかることがある。
遺言はこうだった。
gcc-7: internal compiler error: Segmentation fault: 11 (program cc1)
Please submit a full bug report,
with preprocessed source if appropriate.
See <https://github.com/Homebrew/homebrew-core/issues> for instructions.
「メモリ確保に失敗したので断念します」みたいなちゃんとしたエラー終了ではなく、セグフォ。
メモリ確保に失敗していることに気づかないまま先に進んじゃったのかな。
gcc-11 の場合。
gcc-11 (Homebrew GCC 11.2.0) 11.2.0 で試した。
幸い端末が死んだりはせず、最終的には
209672
が出力された。つまり、20万9671個までは OK。20万9672個で死ぬ。
gcc-7 よりちょっと多いね。
ちなみに結果が出るまで gcc-7 の場合よりももっと長い時間を要した。
gcc-11 の遺言は
gcc-11: internal compiler error: Segmentation fault: 11 signal terminated program cc1
Please submit a full bug report,
with preprocessed source if appropriate.
See <https://github.com/Homebrew/homebrew-core/issues> for instructions.
だった。
こちらもセグフォ。南無南無。
clang
Apple clang version 12.0.5 (clang-1205.0.22.11) で試した。
こちらも端末が死んだりはせず、最終的には
2463
が出力された。つまり、 2462個までは OK。2463個で死ぬ。
gcc と比べるとだいぶ少ないけど、困る人はいないだろう。
clang の遺言は長くてこんな感じ。
clang: error: unable to execute command: Illegal instruction: 4
clang: error: clang frontend command failed due to signal (use -v to see invocation)
Apple clang version 12.0.5 (clang-1205.0.22.11)
Target: x86_64-apple-darwin20.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
clang: note: diagnostic msg:
********************
PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /var/folders/zy/xw4cyzx50_b2h0l86k76qh900000gn/T/hoge-590b89.c
clang: note: diagnostic msg: /var/folders/zy/xw4cyzx50_b2h0l86k76qh900000gn/T/hoge-590b89.sh
clang: note: diagnostic msg: Crash backtrace is located in
clang: note: diagnostic msg: /Users/nabetani/Library/Logs/DiagnosticReports/clang_<YYYY-MM-DD-HHMMSS>_<hostname>.crash
clang: note: diagnostic msg: (choose the .crash file that corresponds to your crash)
clang: note: diagnostic msg:
********************
Illegal instruction とは、穏やかでない。
Visual Studio
Visual Studio 2019 の cl も、Visual Studio 2022 preview の cl も、同じだった。
バージョンは
Microsoft(R) C/C++ Optimizing Compiler Version 19.29.30040 for x64
と
Microsoft(R) C/C++ Optimizing Compiler Version 19.30.30401 for x64
最終的に 1495 を出力して終わったので、 1494 まで OK。1495 だとコンパイル失敗。
失敗時のメッセージは
fatal error C1026: プログラムの解析でコンパイラ内でスタック オーバーフローが発生しました。プログラムが複雑すぎます。
と、死亡ではなくエラー終了。ちゃんとしている。
コンパイル結果
gcc-7, gcc-11, clang, cl は、いずれも最適化なしの場合、
movq (%rax), %rax
movq (%rax), %rax
movq (%rax), %rax
movq (%rax), %rax
movq (%rax), %rax
movq (%rax), %rax
movq (%rax), %rax
というように大量の movq
を生成する(cl の場合はアセンブラの文法が違うけど、実質的に同じ)が、 -O2
にすると一掃される。ちゃんとしているね。