04

01

[C言語] 汎整数拡張

2014.04.01(20:45)

以下の2つのブロックは1か所しか違わないが、それぞれ★が実行される(gcc-4.8.1)。

{
  unsigned char c = 0;
  if ((c-1) < 0) {
    printf("LINE%d true\n",__LINE__); // ★
  } else {
    printf("LINE%d false\n",__LINE__);
  }
}
{
  unsigned int c = 0;
  if ((c-1) < 0) {
    printf("LINE%d true\n",__LINE__);
  } else {
    printf("LINE%d false\n",__LINE__); // ★
  }
}

どうしてそうなるのか考えてみてほしい。








前者はcと1と0が汎整数拡張でintに変換されてから評価されるので、
(intの0 - intの1) < (intの0)と評価され、
(intの-1) < (intの0) は真になる。

後者ではunsigned intは汎整数拡張されずそのまま評価され、
演算の一方がunsigned intなら他方もunsigned intに変換されてから評価されるので、
(unsignedの0 - unsigned intの1) < (unsignedの0)と評価され、アンダーフローがおきて
(unsignedの0xffffffff) < (unsignedの0) は偽になる。

興味を持った方は、汎整数拡張をググってみてほしい。


参考リンク
  1. 汎整数拡張 (Wikipedia)
    http://ja.wikipedia.org/wiki/%E6%B1%8E%E6%95%B4%E6%95%B0%E6%8B%A1%E5%BC%B5

10

17

Visual Studio 2010 - Visual C++ の 出力ウィンドウ で、コンパイル時にコンパイラにどういうオプションが渡されているのか、リンク時にリンカにどういうオプションが渡されているのか、表示させる方法

2012.10.17(21:11)

Visual Studio 2010 - Visual C++ の 出力ウィンドウ で、コンパイル時にコンパイラにどういうオプションが渡されているのか、リンク時にリンカにどういうオプションが渡されているのか、表示させる方法

(そんな名前の設定値を変更するとは思わなかったよ~)

コンパイラへは以下のオプションを変更
nologo1.png

リンカへは以下のオプションを変更
nologo2.png

これにより、出力ウィンドウで、コンパイルの様子とリンクの様子がオプション付きで表示されるようになります。
output3.png
/nologo-を使うななんていうワーニング出ているけど…

10

09

複数のプロセスを使用したビルド、マルチコアを使用したビルドをやりたい

2012.10.09(21:29)

Visual Studio 2010のVisual C++では(以前から)
「複数のプロセスを使用したビルド」
がサポートされており、マルチコアCPUを使ったPCではこの機能を使って、ビルド時間を大幅に短縮することができます。

使用前
before.png
全部のコアは使われていません。

使用後
after.png
全部のコアがフルに使われて、8コアの場合、コンパイル速度はおよそ7倍になります。

ここでは
(1) Visual Studio のソリューション、プロジェクトでビルドする方法
(2) nmakeとclを使って、ビルドする方法
の2つの場合で解説します。

(1) Visual Studio のソリューション、プロジェクトでビルドする方法
/MP (複数のプロセスを使用したビルド)
http://msdn.microsoft.com/ja-jp/library/vstudio/bb385193.aspx
で説明されているとおりに設定すればOK。
プロジェクトのプロパティを開き
構成プロパティ - C/C++ - 全般 - 複数プロセッサによるコンパイル を はい(/MP)
構成プロパティ - C/C++ - コード生成 - 最小ビルドを有効にする を いいえ(/Gm-)
あとは、Visual Studioがやってくれます。

(2) nmakeとclを使って、ビルドする方法
clによってコンパイルするときに、/MPと/Gm-をつければよいのですが、

たとえば、プロセスを4つ使ってコンパイルするには、
cl /MP4 a.cpp
cl /MP4 b.cpp
cl /MP4 c.cpp
cl /MP4 d.cpp
cl /MP4 e.cpp
ではなくて、

cl /MP4 a.cpp b.cpp c.cpp d.cpp e.cpp
としてコンパイルしなければなりません。clが複数のプロセスを操るためです。

nmakeに渡すMakefileで、ファイルの依存性からどのcppをコンパイルするかを導き出したあと、それを
1つ1つclに渡していたら、それをまとめてclに渡すように変更します。

それを実現するのが、
バッチモード推論規則(Batch-Mode Inference Rules)
http://msdn.microsoft.com/ja-jp/library/vstudio/f2x0zs74.aspx
です。

cppからobjへの依存を記述する部分で、
.cpp.obj:

.cpp.obj::

コロン1つをコロン2つに書き換えるのがポイントです。

一回のコンパイルに多数のcppファイルを同時に渡してだいじょうぶかと思いましたが、nmakeの中から、262個のcppファイル名を並べ、ほかのコンパイルオプションとあわせて9073バイトの長さのコマンドラインを、一回でclに渡してコンパイルして大丈夫でした。

おまけ
こういうことがわかりました。
  • パッケージごとにコンパイルオプションが違う場合はその単位でしか/MPできない
  • パッケージの凝集度が低く、それぞれのパッケージに cppファイルが多数ある場合は、/MPによる高速化が7倍ぐらい
  • パッケージの凝集度が高く、それぞれのパッケージに cppファイルが少しずつの場合は、/MPによる高速化は4倍ぐらい
あらら。

注意
4倍、7倍というのは、私のWindows7ノートパソコン + VisualStudio 2010 Preminum + 私のソリューション、および、プロジェクトの場合です。

がっかり
組込み機器用のクロスコンパイラで、1人が1台のPCでコンパイルしているのに8コア使うと8ライセンス使っちゃうようなクロスコンパイラにはがっかり。

03

19

[C++] デシリアライザクラス その1

2012.03.19(18:31)

教材を作っています。

ある仕様に基づき、与えられたASCII文字列からバイナリーデータを作り出すクラスを作ります。拡張BNF記法~字句解析~構文解析、C言語のポインタ、C++言語のクラス、オブジェクト指向設計、どう実装するか、どうテストケースを書くのか、そのテストケースでほんとうに良いのか、に対する教材とするつもりでいます。

適度にやさしく、適度に難しく、いろいろ考えさせるような問題作りをめざします。しかしながら、まだ実装もできていないので、行き詰まる可能性もあります。


問題は以下のとおり。拡張BNF記法で記述する。
<ポインタ>::="["[<メンバ>(","<メンバ>)]"]"
<メンバ>::=(<数>|<ポインタ>)
<数>::="0x"<HEX><HEX><HEX><HEX><HEX><HEX><HEX><HEX>
<HEX>::="0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
  |"a"|"b"|"c"|"d"|"e"|"f"|"A"|"B"|"C"|"D"|"E"|"F"
とし、
<数>は、ASCIIからバイナリに変換してバッファ上に配置する。
<ポインタ>は、もし"[]"であればNULLを配置し、そのNULLを配置したアドレスを返す。
fig[]
<ポインタ>は、もし"[0x00000001]"であればuint32_tの1を配置し、その1を配置したアドレスを返す。
fig[1]
<ポインタ>は、もし"[0x00000001,[0x00000002]]"であれば、uint32_tの1があって、次のアドレスにポインタが入っていて、そのポインタの先にuint32_tの2があるようなバイナリデータ列を作り、1を配置したアドレスを返す。
fig[1[2]]
2を配置する場所は複数考えられるが、無駄なメモリが少なくなるなら、どこに配置してもかまわない。上記は一例。以下同様。
"[0x00000001,[0x00000002],[0x00000003]]"の場合、1の次のアドレスと次の次のアドレスにポインタがあり、それぞれが2と3をさしているように配置して、1を配置したアドレスを返す。
fig[1[2][3]]
"[0x00000001,[0x00000002,[0x00000003]]]"の場合
fig[1[2[3]]]
"[0x00000001,0x00000002,[0x00000003,0x00000004,[0x00000005,0x00000006],0x00000007,0x00000008],
0x00000009,0x0000000a]"の場合(画像をクリックすると全体が見えます)
fig[12[34[56]78]910]

<ポインタ>として与えられたASCIIで記述された文字列から、C言語のポインタがアクセスできるバイナリのデータを作る。あとあとの実装とテストをラクにするため、<数>は4バイト符号なし整数(uint32_t)だけ、<ポインタ>の長さは4バイトとし<数>の長さと同じ、あとは適当にC言語同様の仕様とする。

ポインタの先に、数が1つだけなら、数へのポインタ。ポインタの先に、数やポインタが複数あれば、構造体へのポインタである。構造体のメンバには数も他の構造体へのポインタもある。

その2へ続く (実装しながら、これから書きます。)

02

07

g++で、コンストラクタが2種、デストラクタが3種生成される。

2011.02.07(21:57)

WindowsXP / Cygwin の環境で

bash-4.1$ g++ -v
gcc version 4.3.4 20090804 (release) 1 (GCC)
で以下のようなソースコード

main.cpp
#include "stdio.h"
#include "Foo.h"

int main(int argc,char** argv)
{
	Foo f(10);

	Foo* foo = new Foo(1);
	foo->update();
	delete foo;
}

Foo.h
class Foo
{
private:
	int m_num;
public:
	Foo();
	Foo(int num);
	virtual ~Foo();
	virtual void update(void);
};

Foo.cpp
#include "stdio.h"
#include "Foo.h"

Foo::Foo()
{
	printf("Foo::Foo()\n");
	m_num = 0;
}
Foo::Foo(int num)
{
	printf("Foo::Foo(int)\n");
	m_num = num;
}
Foo::~Foo()
{
	printf("Foo::~Foo()\n");
}
void Foo::update(void)
{
	printf("Foo::Update()\n");
}

をコンパイルするMakefile
# Makefile
all: main.exe

main.exe: main.o foo.o 
	g++ -o main main.o foo.o

main.o: main.cpp
	g++ -c main.cpp

foo.o: foo.cpp
	g++ -c foo.cpp

clean:
	rm -f main.o foo.o

を作ってビルドして
make clean
make
nm foo.o
nm main.o

できた.oファイルをnmで調べてみると、
nm foo.o

00000000 T __ZN3Foo6updateEv

00000014 T __ZN3FooC1Ei
0000003a T __ZN3FooC2Ei

00000060 T __ZN3FooC1Ev
00000088 T __ZN3FooC2Ev

000000b0 T __ZN3FooD0Ev
000000e2 T __ZN3FooD1Ev
00000114 T __ZN3FooD2Ev

00000000 R __ZTI3Foo
00000000 R __ZTS3Foo
00000000 R __ZTV3Foo

のように、コンストラクタが2種ずつ、デストラクタが3種類生成されます。

引数なしのコンストラクタは
00000060 T __ZN3FooC1Ev
00000088 T __ZN3FooC2Ev

intの引数ありのコンストラクタは
00000014 T __ZN3FooC1Ei
0000003a T __ZN3FooC2Ei

デストラクタは、
000000b0 T __ZN3FooD0Ev
000000e2 T __ZN3FooD1Ev
00000114 T __ZN3FooD2Ev

となります。

C++言語は、C言語と同等のコードサイズになる(ほんの少ししか違わない)と主張したい私にとって、コンストラクタやデストラクタが書いたコードの3倍とか2倍になることは当然許したくありません。

呼び出している側を見ると、

nm main.o
U __ZN3FooC1Ei
U __ZN3FooD1Ev

となっており、使っているのは、C1がついているコンストラクタと、D1がついているデストラクタだけです。

そもそも、どうしてコンストラクタが3種、デストラクタが2種できるのか知りたいです。さらに、

私の希望は、以下の(1)または(2)のどちらかができればいいのですが、どちらの方法もまだ見つかっていません。
(1) g++コンパイル時に使わないコードが生成されないようにする。
(2) .oファイル中でどこでも使われていないオブジェクトを削除する。

う~ん、困った。



逆アセンブルもしてみました。
bash-4.1$ objdump -d Foo.o

00000060 <__ZN3FooC1Ev>:
  60:   55                      push   %ebp
  61:   89 e5                   mov    %esp,%ebp
  63:   83 ec 08                sub    $0x8,%esp
  66:   8b 45 08                mov    0x8(%ebp),%eax
  69:   c7 00 08 00 00 00       movl   $0x8,(%eax)
  6f:   c7 04 24 1c 00 00 00    movl   $0x1c,(%esp)
  76:   e8 00 00 00 00          call   7b <__ZN3FooC1Ev+0x1b>
  7b:   8b 45 08                mov    0x8(%ebp),%eax
  7e:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)
  85:   c9                      leave
  86:   c3                      ret
  87:   90                      nop

00000088 <__ZN3FooC2Ev>:
  88:   55                      push   %ebp
  89:   89 e5                   mov    %esp,%ebp
  8b:   83 ec 08                sub    $0x8,%esp
  8e:   8b 45 08                mov    0x8(%ebp),%eax
  91:   c7 00 08 00 00 00       movl   $0x8,(%eax)
  97:   c7 04 24 1c 00 00 00    movl   $0x1c,(%esp)
  9e:   e8 00 00 00 00          call   a3 <__ZN3FooC2Ev+0x1b>
  a3:   8b 45 08                mov    0x8(%ebp),%eax
  a6:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)
  ad:   c9                      leave
  ae:   c3                      ret
  af:   90                      nop

同じコードを2回生成しているように見えます。
どうしてこんな無駄を生成するのでしょう?

プロフィール

島敏博

Shima Toshihiro 島敏博
信州アルプスハイランド在住。HaskellとElixirが好き。組み込みソフトウェアアーキテクト、C++プログラマ、山歩き、美術館巡り、和食食べ歩き、日本赤十字社救急法指導員、インデックス投資、クラシック音楽、SESSAME会員、状態マシン設計、モデル駆動開発、ソフトウェアプロダクトライン、Rubyist、実践ビジネス英語

■ ツイッター
http://twitter.com/saltheads
■ Facebook
http://www.facebook.com/saltheads
■ Qiita
http://qiita.com/saltheads

印刷する場合は、ブラウザの印刷メニューではなく、このページの上から3cmくらいの青いところにある、「印刷」を押してみてください。少しうまく印刷できます。まだ完全ではないのですが、これで勘弁してください。


カテゴリ
最新記事
月別アーカイブ
最新コメント
検索フォーム
リンク
sessame
RSSリンクの表示