会社で超ハマったのでメモしておきます。
GCC 7からswitch-case文において、暗黙のフォールスルーを警告する機能 -Wimplicit-fallthroughが実装されました。動作の仕様についてはRedHatの解説(リンク)が詳しいです。
フォールスルーの警告オプションは -Wimplicit-fallthrough=3のように指定しますが、オプションを直接指定するより-Wextraの指定により勝手にONにされている、というパターンが多いのではないでしょうか。ちなみに -WallではONになりません。
Ubuntu 14.04のccache 3.1.9とこのフォールスルー警告オプションを組み合わせると変な挙動を示します。まずはこんなプログラムを用意します。
int a(int i)
{
switch (i) {
case 0:
i = 2;
/* Fall through */
case 1:
i = 3;
break;
default:
return 0;
}
return 0;
}
このプログラムは、フォールスルーの警告オプションのレベルを4以上にすると、警告が発生します。ちなみにデフォルトの警告レベルは3ですから、レベルを指定しない場合や、-Wextraなどでは警告は発生しません。
$ gcc --version gcc (Debian 7.3.0-11) 7.3.0 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc -Wextra a.c -c -o a.o ★警告無し★ $ gcc -Wimplicit-fallthrough a.c -c -o a.o ★警告無し★ $ gcc -Wimplicit-fallthrough=3 a.c -c -o a.o ★警告無し★ $ gcc -Wimplicit-fallthrough=4 a.c -c -o a.o a.c: In function ‘a’: a.c:5:5: warning: this statement may fall through [-Wimplicit-fallthrough=] i = 2; ~~^~~ a.c:7:2: note: here case 1: ^~~~
しかしUbuntu 14.04の古いccacheを経由してGCCを起動すると、おかしなことが起きます。警告されないはずのレベルを指定しても警告が出てしまうのです……。
$ ./ccache --version ccache version 3.1.9 Copyright (C) 2002-2007 Andrew Tridgell Copyright (C) 2009-2011 Joel Rosdahl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. $ gcc -Wimplicit-fallthrough=3 a.c -c -o a.o ★警告無し★ $ ./ccache gcc -Wimplicit-fallthrough=3 a.c -c -o a.o a.c: In function ‘a’: a.c:5:5: warning: this statement may fall through [-Wimplicit-fallthrough=] i = 2; ~~^~~ a.c:7:2: note: here case 1: ^~~~ ★なぜか警告が出る★
とても不思議ですが、新しいccacheだとこの現象は発生しません。
$ ccache --version ccache version 3.4.1 Copyright (C) 2002-2007 Andrew Tridgell Copyright (C) 2009-2018 Joel Rosdahl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. $ ccache gcc -Wimplicit-fallthrough=3 a.c -c -o a.o ★警告無し★
推測の域を出ませんがccacheのバグなんだろうなあとは思います。
Ubuntu 14.04のGCCは4.8なので、無関係かと思うかもしれませんが、外部から持ってきたクロスコンパイル用のツールチェーンでも同じことが起きます。x86用だろうがARM用だろうが、GCC 7のとある警告オプションを使うとハマります。
昨日(2018年3月23日の日記参照)でGCC 7とUbuntu 14.04のccache 3.1.9を組み合わせると、暗黙のフォールスルー警告が誤動作する現象を紹介しました。
int a(int i)
{
switch (i) {
case 0:
i = 2;
/* Fall through */
case 1:
i = 3;
break;
default:
return 0;
}
return 0;
}
なぜそうなるのか、ざっと見てみました。使うのは下記のスクリプトです。引数をファイル /tmp/aaa.txtに「追記」でダンプした後、本物のGCCを起動するスクリプトです。
引数のダンプを標準出力に出さない理由は、標準出力がccacheに取られて見えないから、「追記」モードで出力する理由はccacheがgccを2回起動するからです。sleep 10を入れた理由は、引数に一時ファイルを指定されたとき消える前に見たいからです。
#!/bin/sh
echo $* >> /tmp/aaa.txt
sleep 10
gcc_ $*
使い方は /usr/bin/gccを /usr/bin/gcc_ にリネームし、このスクリプトを /usr/bin/gccとして配置するだけです。ではやってみましょうか。
-Wimplicit-fallthrough=3 -E a.c -Wimplicit-fallthrough=3 -c -o a.o a.c
最初にGCCをプリプロセッサのみ(-E)で起動し、次にGCCを元の引数(-c -o a.o a.c)で起動しています。
-Wimplicit-fallthrough=3 -c -E a.c -Wimplicit-fallthrough=3 -c -o /home/katsuhiro/.ccache/d/a/8431625291455df2df790a4c34be0d-395.o.tmp.blackbird.18397 /home/katsuhiro/.ccache/tmp/a.tmp.blackbird.18397.i
プリプロセッサのみで起動するところは同じですが、その後が違っていて、一時ファイルをコンパイルするように指定しています。このファイルが怪しいですね。
# 1 "a.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "a.c"
int a(int i)
{
switch (i) {
case 0:
i = 2;
case 1:
i = 3;
break;
default:
return 0;
}
return 0;
}
見たところプリプロセッサ出力をそのまま保存しているようです。case 1: の前にあったコメント /* Fall through */ が消えていますので、-Wimplicit-fallthrough=3でも警告が出てしまいます。
GCC 6までは、元のコードと、プリプロセッサ出力(コメントが全て消える)の間で、警告内容に差は無かったのだろうと思われますが、-Wimplicit-fallthroughは「コメントの内容」によって動作が変わるオプションのため、誤動作を引き起こしたようです。
Ubuntu 14.04にGCC 7は採用されていませんが、クロスコンパイラなどでGCC 7とご対面する可能性があります(自分がそうでした)。
対策としては、
辺りが考えられます。
アイスの写真を10個くらい追加して、77種類になりました。これでもまだ載せていないアイスがたくさんあるんだ…。
集計してみると、こんな感じ。
赤城乳業のファンであることは否定しませんが、新商品を見かけたらとりあえず買うので、特に選り好みはしていません。
それでも赤城乳業がやたら多いのは、ガリガリ君の新商品の出る感覚がかなり短いからでしょうね……。
イテレータには、
この2つがあるみたいで(他にもある?)、どらちもパーフェクトではありません。
C++ もJavaも書いたことがある身としては、別にどちらの方式でも困らないと思っていたのですが、JavaでRandom Iteratorが使いたいときは、どうしたら良いのかわかりませんでした。
リストのインデックスを内包し、リストの要素の型を取るジェネリッククラスでも作れば良いのだろうか…??
メモ: 技術系の話はFacebookから転記しておくことにした。
目次: C言語とlibc
会社で隣の席の方がハマっていて相談されたことのメモです。といっても、私も調べて初めて知りましたから、偉そうなことは言えませんけども。
C言語をコンパイルする際、gccに言語標準オプション(-std)を付けてコンパイルすると「デフォルトの拡張機能」が全てOFF になります。
デフォルトの拡張機能とは、具体的には _DEFAULT_SOURCEを定義すると有効になる機能で、マニュアル(Man page of FEATURE_TEST_MACROS の _DEFAULT_SOURCE (glibc 2.19以降) の節)によれば、
「デフォルト」定義は、POSIX.1-2008で必須となっている定義と、BSDとSystem V由来の種々の定義を公開する。glibc 2.19以前では、これらのデフォルトは以下を明示的に定義するのとほぼ等価である。
cc -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809
会社ではtimersub(BSD由来の機能)がコンパイルエラーになって初めて気づきました。普段POSIX由来か、BSD由来か、そんなことはほぼ意識せず使っていますから、最初はかなり意味不明でした。
この動きは機能は隠された機能でもバグでもなく、gccというかglibcの仕様です。先ほどのマニュアル(Man page of FEATURE_TEST_MACROS の _DEFAULT_SOURCE (glibc 2.19以降) の節)にも、
「デフォルト」が無効になるような状況は、個別のマクロが明示的に定義された場合や、コンパイラが「標準」モードのいずれか (例えばcc -std=c99) で起動された場合などである。
とあります。普段 -stdオプションなんて使わないので、こんな動きするなんて知りませんでしたね……。
< | 2018 | > | ||||
<< | < | 03 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
合計:
本日: