Posts Tagged Programming

Javaと例外

Javaで標準クラスを使う限り例外機構の実装はTry-Catchで決まりそう。
自前クラスをわざわざ用意するのは無駄なので標準クラスを使いたいところ。
なので、あまり好きじゃないけどTry-Catch文で試す。
前回の引数をとる続編として、引数をどんどん掛け算して行く。

Try-Catch文の書き方は

try
{
/* 処理 */
}
catch( 受け取るエラーのException型 変数 )
{
/* 処理でこけた後の処理 */
}

となる。
try文で処理をテストして、エラーが出た瞬間にcatch文へ進む。
catch文で例外の型に合致する箇所でパラメータを受け取り、適宜処理をする。

ArgumntsCalc.java

class ArgumentsCalc
{
public static void main( String args[] )
{
int argc = args.length;
double tmp, ans = 1.0;
for( int i = 0; i < argc; ++i )
{
try
{
ans *= Double.parseDouble( args[i] );
}
catch( NumberFormatException e )
{
System.out.println( e );
}
}
System.out.println( "result is " + ans + "." );
}
}

こんな感じ。
エラーが起きたらとりあえずエラー内容を出力する。
実行結果は下記の通り。

McLaren% javac ArgumentsCalc.java
McLaren% java ArgumentsCalc 2.0 5 x
java.lang.NumberFormatException: For input string: "x"
result is 10.0.

独習Java 第4版

著者/訳者:ジョゼフ・オニール

出版社:翔泳社( 2008-05-29 )

定価:

Amazon価格:¥ 3,456

大型本 ( 528 ページ )

ISBN-10 : 4798117153

ISBN-13 : 9784798117157


Post to Twitter

, , ,

No Comments

Javaと引数

Javaで引数をとってみる。

引数を複数とって、その数と引数自体を表示し、その長さも表示したい。
実行方法は

McLaren% java ArgumentsTest x y z abc

とする。

で、こんな感じのコードを書いてみた。

ArgmentsTest.java

class ArgumentsTest
{
public static void main( String args[] )
{
int maxLen = args.length;
System.out.println( "total arguments count is " + maxLen + "." );
for( int i = 0; i < maxLen; ++i )
{
System.out.println( "\tcurrent arguments is \"" + args[i] + "\"." );
System.out.println( "\t\tlength is " + args[i].length() + "." );
}
}
}

Javaでは引数がmainで取得したString型の変数に集約される。
と言うのも、引数の数を取得したければ .lengthを付け足せばそのメンバ変数に引数の数が入っている。
各々の引数は配列毎に格納されるので、その中で長さを取得したければ .length()メソッドを呼び出せば文字列長を取得することが出来る。

実行結果は下記の通り。

McLaren% javac ArgumentsTest.java
McLaren% java ArgumentsTest x y z abc
total arguments count is 4.
current arguments is "x".
length is 1.
current arguments is "y".
length is 1.
current arguments is "z".
length is 1.
current arguments is "abc".
length is 3.

独習Java 第4版

著者/訳者:ジョゼフ・オニール

出版社:翔泳社( 2008-05-29 )

定価:

Amazon価格:¥ 3,456

大型本 ( 528 ページ )

ISBN-10 : 4798117153

ISBN-13 : 9784798117157


Post to Twitter

, ,

No Comments

Javaと標準出力と変数など

Javaを弄ろうと思って本読んでる。
で、とりあえずさっきHello Worldも出してみたわけ。

PHPべったりで、Cを少し勉強した俺からして、「Javaってどんな感じかなぁ。」というのが知りたかった。
まず、文字出力メソッドがおおらか。
例えば、下記のようなコードは普通に通る。

PrintTest.java

class PrintTest
{
public static void main( String args[] )
{
int num = 10000;
System.out.println( "num is " + num );
}
}
McLaren% javac PrintTest.java 
McLaren% java PrintTest      
num is 10000

なんか、JavaScriptと同じと言うか、この部分はJavaをそのまま流用したような実装になってるんだなぁ、と。

配列に関してもおおらか。
気持ち悪い書き方だけど、下記コードと実行結果。
ArrayTest.java

class ArrayTest
{
public static void main( String args[] )
{
int nums[];
nums = new int[10];
for ( int i = 0; i < 10; ++i )
{
System.out.println( "current num is " + nums[i] );
}
System.out.println( "end" );
}
}
McLaren% javac ArrayTest.java
McLaren% java ArrayTest      
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
current num is 0
end

new、mallocと言うよりはcallocと言う雰囲気。
まぁ、メモリ確保した後すぐに中身入れ替えちゃうような時は無駄な気がするけど、事故るよりは良いってことなんでしょうな。

あと、面白いのは

arrayVarName.length

とすると、そのまま配列の個数が取得できる。

class ArrayTestWithInit
{
public static void main( String args[] )
{
int nums[];
nums = new int[10];
int max = nums.length;
System.out.println( "nums length is " + max );
for ( int i = 0; i < max; ++i )
{
nums[i] = i;
}
for ( int i = 0; i < max; ++i )
{
System.out.println( "current num is " + nums[i] );
}
System.out.println( "end" );
}
}
McLaren% javac ArrayTestWithInit.java
McLaren% java ArrayTestWithInit      
nums length is 10
current num is 0
current num is 1
current num is 2
current num is 3
current num is 4
current num is 5
current num is 6
current num is 7
current num is 8
current num is 9
end

こう言うことも出来る。

独習Java 第4版

著者/訳者:ジョゼフ・オニール

出版社:翔泳社( 2008-05-29 )

定価:

Amazon価格:¥ 3,456

大型本 ( 528 ページ )

ISBN-10 : 4798117153

ISBN-13 : 9784798117157


Post to Twitter

,

No Comments

いろんな言語でこんにちは[ Java篇 ]

JavaでのHello World。
Mac OSXで動作確認済み。
あ、コンソールのみね。

class HelloWorld
{
public static void main( String args[] )
{
System.out.println( "Hello World!" );
}
}
McLaren% javac HelloWorld.java
McLaren% java HelloWorld 
Hello World!

独習Java 第4版

著者/訳者:ジョゼフ・オニール

出版社:翔泳社( 2008-05-29 )

定価:

Amazon価格:¥ 3,456

大型本 ( 528 ページ )

ISBN-10 : 4798117153

ISBN-13 : 9784798117157


Post to Twitter

, ,

No Comments

Zend Frameworkと戯れる -じゃんけんゲームを作ってみる-

とりあえず、タイトル通りのことをやってみた。
Hello, World!じゃつまらないので、名前入れてもらってじゃんけんの手を選んでもらってCPUと対決しましょう、という感じ。
まず、ディレクトリ構成を考えることに。
サブドメイン(xxx.yuxx.netのxxxの部分)を切ってやっても良いんだけど、便利な設定してないから、UserDir(xxx.yuxx.net/~hogeの~hogeの部分)でやることに。
しかもドキュメントルート(このアプリの頭はやっぱつかいたくなかったからディレクトリを一段したにした。
URL的にはhttp://test.yuxx.net/~yuxx。

以下、treeコマンドより。(超便利w)

zend_test_prj
|-- public_html
|   `-- zend_test
|       |-- .htaccess
|       `-- index.php
`-- web_app
`-- zend_test
|-- controllers
|   `-- IndexController.php
|-- models
`-- views
`-- scripts
`-- index
|-- index.phtml
`-- pon.phtml

public_html以下が公開ディレクトリで、web_appがプログラムの場所。
まぁ、web_appはプログラムは権限さえあればどこ置いたって良いんだけどね。
名前も本来はapplicationsが推奨だし。。

で、まず公開領域にある.htaccessを置きましょう、と。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

一行目でURLを変換出来るようにmod_rewriteをOnに。
4行目で「js、ico、gif、jpg、png、css以外はindex.phpへ送る」と言う設定。
Flashとかをこのディレクトリ内に置いて使うんだったら適宜設定を。
2行目と3行目は別に必要ないんだけど、そのディレクトリ内にURLで叩かれたディレクトリ、あるいはファイルがあった場合はそっち優先で見に行くよ~と言うおまじない。

お次は同じ場所に設置するindex.php。

<?php
ini_set('display_errors', 'On');
require_once 'Zend/Controller/Front.php';
Zend_Controller_Front::run('../../web_app/zend_test/controllers');

2行目は一応エラー表示させたいので置いといた。
この程度のプロジェクトだからそうそうエラーは出ないだろうけど、それでも俺みたいな脳たりんには重要だったりする。
もちろん、リリース時には外すけど:-)
で、重要なのは3行目と4行目。
3行目は基本的にこういうもの、と言うことで。
4行目はこのファイル「index.php」の場所から見ての相対パスで、IndexController.phpがどこにあるか、と言うこと。
今回の場合、ドキュメントルートがそもそも1階層深かったり、アプリケーションルートもそれに合わせて深くしたりしているから、チト長い書き方になってしまったかね。。。

で、用意したテンプレはこんな感じ。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html, charset=UTF-8">
<title>じゃんけん</title>
</head>
<body>
<center>
<h1>じゃんけんゲーム</h1>
<?php if (!empty($this->errMsgs)) { ?>
<?php   foreach ($this->errMsgs as $currMsg) { ?>
<span style="color:#ff0000"><?php echo $currMsg ?></span><br />
<?php   } ?>
<?php } ?>
<form action="<?php echo $this->baseUrl(); ?>/index/pon" method="post">
お名前<?php echo $this->formText('pName', $this->escape($this->pName)) ?><br />
<?php echo $this->formRadio('pMethodKey', $this->pMethodKey, null, $this->methodNames) ?>
<br /><?php echo $this->formSubmit('', 'ポン!') ?>
</center>
</body>
</html>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html, charset=UTF-8">
<title>ポン!</title>
</head>
<body>
<center>
<h1>CPU &gt; <?php echo $this->cpuMethod ?></h1>
<?php echo $this->escape($this->pName)?>さんは<?php echo $this->pMethodName?>を出したのでの<?php echo $this->result?>です。<br />
<a href="<?php echo $this->baseUrl(); ?>">戻る</a>
</center>
</body>
</html>

では、web_app/zend_test以下のcontrollers/IndexController.phpを見ていこうか。
ちなみに、このコントローラーはデフォルトだと一番最初にアクセスされるコントローラクラスと言うことになる。

<?php
// load base components.
require_once 'Zend/Controller/Action.php';
class IndexController extends Zend_Controller_Action
{
private $_methodNames = array(
0 => 'グー',
1 => 'パー',
2 => 'チョキ'
);
private $_resultNames = array(
0 => 'あいこ',
1 => '負け',
2 => '勝ち'
);
// Index action
public function indexAction()
{
$this->view->assign('methodNames', $this->_methodNames);
}
public function ponAction()
{
$req = $this->getRequest();
$errMsgs = array();
$pName = $req->getPost('pName');
if ($pName == null || $pName == '') {
$errMsgs[] = '名前を入力して下さい。';
}
$pMethodKey = $req->getPost('pMethodKey');
if ($pMethodKey == null || $pMethodKey == '') {
$errMsgs[] = 'あなたの手を選択して下さい。';
$pMethodKey = null;
} else {
$errFlg = true;
foreach ($this->_methodNames as  $key => $methodName) {
if ($pMethodKey == $key) {
$errFlg = false;
break;
}
}
if ($errFlg == true) {
$errMsgs[] = 'もう一度選択して下さい。';
}
}
// index、ponの両方で必要なデータのアサイン
$this->view->assign('pName', $pName);
if (!empty($errMsgs)) {
// indexで必要なデータのアサイン
$this->view->assign('errMsgs', $errMsgs);
$this->view->assign('pMethodKey', $pMethodKey);
$this->view->assign('methodNames', $this->_methodNames);
// indexをレンダー
//            $this->render('index/index'); // なぜかこれだと動作しない。
$this->getHelper('viewRenderer')->setNoController()->setScriptAction('index/index');
// 処理修了
return;
}
// CPUのじゃんけんの手をランダムで出す
srand(time());
$cpuMethodKey = rand() % 3;
// じゃんけんの結果を計算
$result = ($cpuMethodKey - $pMethodKey + 3) % 3;
// ponで必要なデータのアサイン
$this->view->assign('pMethodName', $methodName);
$this->view->assign('cpuMethod', $this->_methodNames[$cpuMethodKey]);
$this->view->assign('result', $this->_resultNames[$result]);
}
}

今回はMVC(モデル・ビュー・コントローラー)デザインの内、DBを使ってないからModelは用意せず、コントローラー内でバリデート(エラーチェック)を行った。
Postされた値は$this->getRequest()で取ってくることが出来る。
また、ビューでデータを使うためには他のフレームワーク同様アサイン(追加)しないといけない。
アサイン方法は$this->view->assign(‘ビューで使うための変数名’, データ)という感じ。
ビューファイルで使うときは先ほどのビューに書いてあるとおり、$this->ビューで使うための変数名 と言う風に取ってこれる。
とまぁ、こんな感じ。
じゃんけんのルーチンは自分で検証してみてくだされ。

出来上がったクソアプリは下記の通り。
http://test.yuxx.net/~yuxx/zend_test

一応まとまったソースは下記。
zend_test_prj.tar.bz2
zend_test_prj.zip
お好きな方をどうぞ。

お粗末。

Zend Framework 徹底マスター

著者/訳者:藤野 真吾

出版社:ソーテック社( 2009-04-11 )

定価:

Amazon価格:¥ 28,387

単行本 ( 592 ページ )

ISBN-10 : 488166669X

ISBN-13 : 9784881666692


Post to Twitter

, ,

No Comments

Zend Frameworkのインストール

ちと所用でZend Frameworkを使わなくてはいけないっぽいので当サーバへインスコすることに。
FreeBSDなのでportsからサクッと入れてしまいまひょ。

% portinstall www/zend-framework

or

% cd /usr/ports/www/zend-framework
% make install clean

とか。
コンパイル中に2回ほどオプション選択させられるけど、自分の使っているDBとかと相談して決めてみてはいかがだろうか。

で、最終的に

Now you need to adjust PHP's include_path to contain
`/usr/local/share/ZendFramework/library'
For example, insert:
include_path = ".:/usr/local/share/ZendFramework/library"
into `/usr/local/etc/php.ini'.
Zend Framework includes the Zend_Tool class and wrapper script
for automating many common framework-related tasks. To use the
zf wrapper script, set the following environment variable:
Bourne shell:
export ZEND_TOOL_INCLUDE_PATH_PREPEND=\
/usr/local/share/ZendFramework/library
C-shell:
setenv ZEND_TOOL_INCLUDE_PATH_PREPEND \
/usr/local/share/ZendFramework/library
Documentation for the Zend_Tool class is found at:
http://framework.zend.com/manual/en/zend.tool.framework.html
For more general information about the Zend Framework, please
visit: http://framework.zend.com/

こんなメッセージが出る。
Zend_Tool classの事はよ~知らんから放置。
php.iniにあるinclude_pathを指示通り書き直す。

% vim /usr/local/etc/php.ini
(略)
include_path = ".:/usr/local/share/ZendFramework/library"

そんでもって、apache再起動。

% apachectl restart

せっかく140MB強までダイエットに成功したhttpdが一気に190MB手前にまでふくれあがってしまった。。。
サーバ管理技術を身につけたい。。
サーバを強化および低消費電力化したい。。。

で、お決まりのテストコード。

<?php
require 'Zend/Version.php';
echo Zend_Version::VERSION;

Zend Frameworkコード規約によりPHPのみのファイルでは終了タグ?>は書かない。
結果は下記ページの通り。
http://yuxx.net/~yuxx/zend_ver.php

とりあえずインストール完了なり:-)

Zend Framework 徹底マスター

著者/訳者:藤野 真吾

出版社:ソーテック社( 2009-04-11 )

定価:

Amazon価格:¥ 28,387

単行本 ( 592 ページ )

ISBN-10 : 488166669X

ISBN-13 : 9784881666692


Post to Twitter

, , , ,

No Comments

Quake IIIをVisual Studio 2008でビルド

id software(イド・ソフトウェア)、この会社は一人称視点のシューティングゲームをFirst Person Shooter通称FPSと言うジャンルとして広めた。
Wolfenstein 3Dを皮切りにDoomが欧米で大ヒット。
3D時代前夜にQuakeをリアルタイム3DCGでリリース。
少数精鋭主義を貫いたせいか、この前Bethesda Softworksかその親会社のZeniMaxに買収。
「洋ゲー=FPS」の様な状況を作り、今に至る。

で、id softwareは本当に素晴らしいソフト会社で、最新以外のQuakeシリーズのソースコードがGPLライセンスの元リリースされている。
例えばQuake 3はこれ。
ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip
他にもDoom / Doom 2やHexen / Heretic、当然Quake、Quake IIもリリースされているので、興味があったらググってみてもいいかもしれない。

そしてそして、お待たせしました。
本題。
このソース、落として解凍してVCにぶち込んでビルドすれば動くのか?と言う話。
答えはNO!
なので記事に・・・。
で、「luozhiyu – Compile Quake III arena」を参考にやってみましょう、と。
あ、先に書いておくけどQuake 3 Arenaを持ってないと動かないからね!
未確認ながら、Demo版があるから、それでも出来るかも?
Amazonでもほぼ売り切れのようなものだから、Steamで買うという手もある。

と言う前置きはさておき、はじめましょうか。
まず普通に解凍する。
解凍したディレクトリに「code」と言うディレクトリがあるはず。
とりあえずそこを開く。
で、「quake3.sln」を開く。


バックアップは作らなくても良いと思う。
だって、そもそもZipファイルにフルソースがあるし・・・。


これもとりあえず無視して「OK」。

これも消しちゃってOKのはず。

これで変換終了。

履歴を出すとこんな感じ。

ここからコンパイル。
でもいろいろ設定しなきゃいけない。


まず、ソリューションのプロパティでスタートアッププロジェクトを「quake3」にする。

で、ウィンドウ上部中央にあるソリューションの構成が「Debug Alpha」とかの場合は「Debug」に変更する。


「quake3」のプロジェクトのプロパティを開いて、「デバッグ」、「コマンド引数」を

+set fs_cdpath &quot;C:\Program Files\Quake III Arena\&quot; +set r_mode &quot;4&quot;

とかにする。
ここのオプションはQuake3のインストールディレクトリにある「baseq3」ディレクトリにある「q3config.cfg」の設定内容と同様と思われる。
ただ一点違うのはプログラムやらアセットやらへのパス。
それを「fs_cdpath “インストールパス”」としないとゲームが起動しない。
あと、もう一つのオプションは解像度のオプションで、今使っているマシンはこのゲームが発売されて軽く10年は過ぎたくらいの時に組んだから、当時としては考えられないような解像度(1920 x 1200)なので、どうしてもエラーが出た。。
製品版から起動してもエラーが出たので、起動時の解像度を抑えるようにしてみた。
自分の環境ではフルスクリーンで落ちるので、「q3config.cfg」の

seta r_fullscreen &quot;1&quot;

seta r_fullscreen &quot;0&quot;

として抑えた。

ちなみに、ビルドして実行してみるとこんなエラーが出る。

これが出なくてオープニングムービーが始まってCDキーを入力する画面が出たらもう問題ないでしょう。

id softwareらしい無骨なタイトル画面w






こんな感じ。

お粗末。

Post to Twitter

, , , , , , , , ,

No Comments

メモリのアラインメントを確認する

そもそもの話。
なぜ「ゲームプログラマになる前に覚えておきたい技術(以下「セガ本」)」を読んでる途中で「C言語ポインタ完全制覇(以下「ポインタ本」)」を読むことになったか?
それは、セガ本にデータ型のサイズで割りきれるアドレスでデータを読み込まないと、x86系(いわゆるIntel、AMD系)以外のCPUではクラッシュし、x86でもデータを2回読むことになってパフォーマンスが落ちる、と書いてあったから。
で、パッと見て理解できないでいたら、時を同じくしてポインタ本が届き、目次にアラインメントの記述が。
と言うわけで、ポインタ本を読み進めてみた次第。

そこで思ったのが、この本、本当に為になる。
俺みたいなCの見習いの肩もみの家の便所掃除みたいな人間にはいろいろ発見が多すぎて驚く。
例えば

#include &lt;stdio.h&gt;
int main()
{
int i;
char s[] = &quot;abcdefghij&quot;;
for ( i = 0; i &lt; sizeof( s ) - 1; ++i ) {
printf( &quot;%d:%c\n&quot;, i, i[s] );
}
}

なんてコード。
実行するとこう。

McLaren% gcc -o pointer_test pointer_test.c
McLaren% ./pointer_test
0:a
1:b
2:c
3:d
4:e
5:f
6:g
7:h
8:i
9:j

9行目のi[s]はs[i]の間違いと思いがちだけど、”これでも”問題ない。
出来るか出来ないかの話であって、常識的にこの書き方をして良いかは別の話(そしてもちろんダメ)。
この表記はコンパイル時に

printf( &quot;%d:%c\n&quot;, i, *( i + s ) );

と変換しているだけ。
確かにポインタ+数値でそのポインタの型の分だけ先へ進む機能がある。
とまぁ、なかなか興味深い。

さてさて本題。
ようやくアラインメントが書いてあるページまでたどり着いた。
どうやら現代のコンパイラはかなりその辺りも配慮しているらしく、次に置かれるデータがそのデータ型で割り切れない場合、”詰め物”をしてくれるんだとか。
実際コードにして試してみまふ。

// alignment_check.c
#include &lt;stdio.h&gt;
typedef struct {
char ch;
double dn;
short sn;
int nu;
} MyStruct;
int main()
{
MyStruct hoge;
unsigned long gap1, gap2, gap3;
printf( &quot;sizeof char\t%lu\n&quot;, sizeof( char ) );
printf( &quot;sizeof double\t%lu\n&quot;, sizeof( double ) );
printf( &quot;sizeof short\t%lu\n&quot;, sizeof( short ) );
printf( &quot;sizeof int\t%lu\n\n&quot;, sizeof( int ) );
printf( &quot;sizeof MyStruct\t%lu\n\n&quot;, sizeof( MyStruct ) );
printf( &quot;\t&amp;hoge's hexadecimal memory address.\n&quot; );
printf( &quot;\tname\t\ttype\t\taddress\t\tsize\n&quot; );
printf( &quot;\t&amp;hoge\t\tMyStruct\t%p\t%4lu\n&quot;, &amp;hoge, sizeof( hoge ) );
printf( &quot;\t&amp;hoge.ch\tchar\t\t%p\t%4lu\n&quot;, &amp;hoge.ch, sizeof( hoge.ch ) );
gap1 = (unsigned long)&amp;hoge.dn -
( (unsigned long)&amp;hoge.ch + sizeof( hoge.ch ) );
printf( &quot;gap1:%lu\n&quot;, gap1 );
printf( &quot;\t&amp;hoge.dn\tdouble\t\t%p\t%4lu\n&quot;, &amp;hoge.dn, sizeof( hoge.dn ) );
gap2 = (unsigned long)&amp;hoge.sn -
( (unsigned long)&amp;hoge.dn + sizeof( hoge.dn ) );
printf( &quot;gap2:%lu\n&quot;, gap2 );
printf( &quot;\t&amp;hoge.sn\tshort\t\t%p\t%4lu\n&quot;, &amp;hoge.sn, sizeof( hoge.sn ) );
gap3 = (unsigned long)&amp;hoge.nu -
( (unsigned long)&amp;hoge.sn + sizeof( hoge.sn ) );
printf( &quot;gap3:%lu\n&quot;, gap3 );
printf( &quot;\t&amp;hoge.nu\tint\t\t%p\t%4lu\n\n&quot;, &amp;hoge.nu, sizeof( hoge.nu ) );
printf( &quot;char + gap1 + double + gap2 + short + gap3 + int = %lu\n\n&quot;,
sizeof( char ) + gap1 +
sizeof( double ) + gap2 +
sizeof( short ) + gap3 +
sizeof( int ) );
printf( &quot;&amp;hoge's decimal memory address (with alighnment gap size).\n&quot; );
printf( &quot;\tname\t\ttype\t\taddress\t\tsize\n&quot; );
printf( &quot;\t&amp;hoge\t\tMyStruct\t%ll\t%4lu\n&quot;, &amp;hoge, sizeof( hoge ) );
printf( &quot;\t&amp;hoge.ch\tchar\t\t%lu\t%4lu\n&quot;, &amp;hoge.ch, sizeof( hoge.ch ) );
printf( &quot;gap1:%lu\n&quot;, gap1 );
printf( &quot;\t&amp;hoge.dn\tdouble\t\t%lu\t%4lu\n&quot;, &amp;hoge.dn, sizeof( hoge.dn ) );
printf( &quot;gap2:%lu\n&quot;, gap2 );
printf( &quot;\t&amp;hoge.sn\tshort\t\t%lu\t%4lu\n&quot;, &amp;hoge.sn, sizeof( hoge.sn ) );
printf( &quot;gap3:%lu\n&quot;, gap3 );
printf( &quot;\t&amp;hoge.nu\tint\t\t%lu\t%4lu\n&quot;, &amp;hoge.nu, sizeof( hoge.nu ) );
return 0;
}

まぁ、こんだけ親切に情報盛ったから、アドレス表記だけで良いとは思ったけど、一応アドレスを10進数にしたやつも記載。
(つっても、64ビット環境においてunsigned long程度でアドレス表示して間に合わない気がするけど、ギャップを目視することが目的だから、あえてこのまま。
あれ?ラップトップのMacでgcc動かしたときって64ビット扱い?
・・・ま、今回は気にしないことに。
どうせテストだし。。。
まともに64ビットアドレス書いてたら、京単位らしいから・・・ね。)

で、結果。

McLaren% gcc -o alignment_check alignment_check.c
alignment_check.c: In function ‘main’:
alignment_check.c:48: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘struct MyStruct *’
alignment_check.c:49: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘char *’
alignment_check.c:52: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘double *’
alignment_check.c:55: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘short int *’
alignment_check.c:58: warning: format ‘%lu’ expects type ‘long unsigned int’, but argument 2 has type ‘int *’
McLaren% ./alignment_check
sizeof char     1
sizeof double   8
sizeof short    2
sizeof int      4
sizeof MyStruct 24
&amp;hoge's hexadecimal memory address.
name            type            address         size
&amp;hoge           MyStruct        0x7fff5fbfef10    24
&amp;hoge.ch        char            0x7fff5fbfef10     1
gap1:7
&amp;hoge.dn        double          0x7fff5fbfef18     8
gap2:0
&amp;hoge.sn        short           0x7fff5fbfef20     2
gap3:2
&amp;hoge.nu        int             0x7fff5fbfef24     4
char + gap1 + double + gap2 + short + gap3 + int = 24
&amp;hoge's decimal memory address (with alighnment gap size).
name            type            address         size
&amp;hoge           MyStruct        140734799802128   24
&amp;hoge.ch        char            140734799802128    1
gap1:7
&amp;hoge.dn        double          140734799802136    8
gap2:0
&amp;hoge.sn        short           140734799802144    2
gap3:2
&amp;hoge.nu        int             140734799802148    4

こんな塩梅。
ワーニングは全部アドレスを無理矢理10進数で出したため。
「ポインタ本」と同じように構造体で確認した。
こうすれば普通の処理系では間に別の変数を入れたりする余地はあるまい:-)
で、確かにアドレスを次のメンバ変数のサイズで割り切れない場合、ギャップが出ている。
なるほど、こうすればどのCPUでもスマートに動くというわけですかい。
勉強になった。

アマゾンのサーバでエラーが起こっているかもしれません。
一度ページを再読み込みしてみてください。


ゲームプログラマになる前に覚えておきたい技術

著者/訳者:平山 尚

出版社:秀和システム( 2008-11-14 )

定価:

Amazon価格:¥ 4,860

単行本 ( 872 ページ )

ISBN-10 : 4798021180

ISBN-13 : 9784798021188


Post to Twitter

, , , ,

No Comments

スタックオーバーフローをテスト

最近、「C言語ポインタ完全制覇 (標準プログラマーズライブラリ)」という本を読んでる。
タイトルの通り、C言語の、それもポインタだけに的を絞った本。
これがなかなかどうして面白くて、文字も大きいのですらすら進んでしまう。
ただ、簡単かと言われれば全く違って、興味深くて考えさせられるような話題ばかり。

ようやく100ページまで進んだ。
そしたらセキュリティーホールの温床になると言うスタックオーバーフローをテストしようという楽しいコードが書いてあった。
で、試したくなるじゃないですか。
若干情報多めに書いて写経してみたのが下記。

// stack_overflow_test.c
#include &lt;stdio.h&gt;
void hello()
{
static int i = 0;
fprintf( stderr, &quot;stderr:\tHello xD\n&quot; );
printf( &quot;\tNo.%d Hello:P\n&quot;, i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( &quot;buf\t%p\n*buf\t%p\n&amp;i\t%p\n&amp;hello\t%p\n\n&quot;, buf, *buf, &amp;i, &amp;hello );
for ( i = 0; i &lt; 16; ++i ) {
buf[i] = hello;
printf( &quot;buf[%d]\t%p\n&quot;, i, &amp;buf[i] );
}
printf( &quot;\n\tEnd of func()\n\n&quot; );
}
int main()
{
int buf[1000];
printf( &quot;Start\n\n&quot; );
func();
printf( &quot;End\n\n&quot; );
return 0;
}

で、実行するとこう。

% gcc -o stack_overflow_test stack_overflow_test.c
% ./stack_overflow_test
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&amp;i      0x10000108c
&amp;hello  0x100000d1c
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
zsh: segmentation fault  ./stack_overflow_test

あれ~?
hello()が実行されてない・・・??
と言うわけで、落ちる前に一度hello()を呼んでみた。

#include &lt;stdio.h&gt;
void hello()
{
static int i = 0;
fprintf( stderr, &quot;stderr:\tHello xD\n&quot; );
printf( &quot;\tNo.%d Hello:P\n&quot;, i++ );
}
void func()
{
void        *buf[10];
static int  i;
printf( &quot;buf\t%p\n*buf\t%p\n&amp;i\t%p\n&amp;hello\t%p\n\n&quot;, buf, *buf, &amp;i, &amp;hello );
hello();
for ( i = 0; i &lt; 16; ++i ) {
buf[i] = hello;
printf( &quot;buf[%d]\t%p\n&quot;, i, &amp;buf[i] );
}
printf( &quot;\n\tEnd of func()\n\n&quot; );
}
int main()
{
int buf[1000];
printf( &quot;Start\n\n&quot; );
func();
printf( &quot;End\n\n&quot; );
return 0;
}

するとこうなる。

% gcc -o stack_overflow_test stack_overflow_test.c
Start
buf     0x7fff5fbfdf20
*buf    0x100000eee
&amp;i      0x10000108c
&amp;hello  0x100000d10
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfdf20
buf[1]  0x7fff5fbfdf28
buf[2]  0x7fff5fbfdf30
buf[3]  0x7fff5fbfdf38
buf[4]  0x7fff5fbfdf40
buf[5]  0x7fff5fbfdf48
buf[6]  0x7fff5fbfdf50
buf[7]  0x7fff5fbfdf58
buf[8]  0x7fff5fbfdf60
buf[9]  0x7fff5fbfdf68
buf[10] 0x7fff5fbfdf70
buf[11] 0x7fff5fbfdf78
buf[12] 0x7fff5fbfdf80
buf[13] 0x7fff5fbfdf88
buf[14] 0x7fff5fbfdf90
buf[15] 0x7fff5fbfdf98
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P
stderr: Hello xD
No.3 Hello:P
stderr: Hello xD
No.4 Hello:P
stderr: Hello xD
No.5 Hello:P
zsh: segmentation fault  ./stack_overflow_test

これでしっかり動いた。
5回勝手(不正)に実行されちょる。

本の説明によるとメモリの状態は下記のような感じとなるらしい。
–ここ移行に別の変数が追加されていく–
buf[0] 0x7fff5fbfdf20
buf[1] 0x7fff5fbfdf28
buf[2] 0x7fff5fbfdf30
buf[3] 0x7fff5fbfdf38
buf[4] 0x7fff5fbfdf40
buf[5] 0x7fff5fbfdf48
buf[6] 0x7fff5fbfdf50
buf[7] 0x7fff5fbfdf58
buf[8] 0x7fff5fbfdf60
buf[9] 0x7fff5fbfdf68
==他の自動変数==
==関数終了時に戻るアドレス情報など==
==その他の領域==

考察:buf[10]~buf[15]までの6個分余分に配列があると仮定して上がいてます、と(・・アレ?5回しか実行されてない??)。
自動変数を食い破り、呼び出し元の関数情報を破壊して行きます、と。
あ、わかった。。。
funcに自動変数iがあるから1回少ないんだ。
funcをこう変えた。

void func()
{
void        *buf[10];
static int  i;
int a, b, c, d, e; // 追加
printf( &quot;buf\t%p\n*buf\t%p\n&amp;i\t%p\n&amp;hello\t%p\n\n&quot;, buf, *buf, &amp;i, &amp;hello );
hello();
for ( i = 0; i &lt; 15; ++i ) {
buf[i] = hello;
printf( &quot;buf[%d]\t%p\n&quot;, i, &amp;buf[i] );
}
printf( &quot;\n\tEnd of func()\n\n&quot; );
}

結果はこう。
(ちなみに、ちょっとプログラムいじっちゃったからアドレスと表記が若干違う。。)

Start
&amp;func   0x100000d3f
buf     0x7fff5fbfeea0
*buf    0xffffffffffffffff
&amp;i      0x10000108c
&amp;hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeea0
buf[1]  0x7fff5fbfeea8
buf[2]  0x7fff5fbfeeb0
buf[3]  0x7fff5fbfeeb8
buf[4]  0x7fff5fbfeec0
buf[5]  0x7fff5fbfeec8
buf[6]  0x7fff5fbfeed0
buf[7]  0x7fff5fbfeed8
buf[8]  0x7fff5fbfeee0
buf[9]  0x7fff5fbfeee8
buf[10] 0x7fff5fbfeef0
buf[11] 0x7fff5fbfeef8
buf[12] 0x7fff5fbfef00
buf[13] 0x7fff5fbfef08
buf[14] 0x7fff5fbfef10
End of func()
End
zsh: segmentation fault  ./stack_overflow_test

ほら、実行されない。
ただ不思議なことに、

int a;

だけにした場合と、

int a, b, c, d;

までにした場合、

Start
&amp;func   0x100000d3f
buf     0x7fff5fbfeeb0
*buf    0x7365745f776f6c66
&amp;i      0x10000108c
&amp;hello  0x100000cf8
stderr: Hello xD
No.0 Hello:P
buf[0]  0x7fff5fbfeeb0
buf[1]  0x7fff5fbfeeb8
buf[2]  0x7fff5fbfeec0
buf[3]  0x7fff5fbfeec8
buf[4]  0x7fff5fbfeed0
buf[5]  0x7fff5fbfeed8
buf[6]  0x7fff5fbfeee0
buf[7]  0x7fff5fbfeee8
buf[8]  0x7fff5fbfeef0
buf[9]  0x7fff5fbfeef8
buf[10] 0x7fff5fbfef00
buf[11] 0x7fff5fbfef08
buf[12] 0x7fff5fbfef10
buf[13] 0x7fff5fbfef18
buf[14] 0x7fff5fbfef20
End of func()
stderr: Hello xD
No.1 Hello:P
stderr: Hello xD
No.2 Hello:P

となる。
エラーは起きない。
けど変。

結論:戻り先アドレスまで上がいたら、そこにある情報を実行しちゃいました、的な感じの認識。

まぁ、何にせよ、バッファが溢れないように注意しないといつか痛い目見ますよ、と。
精進します。

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

著者/訳者:前橋 和弥

出版社:技術評論社( 2001-01-01 )

定価:

Amazon価格:¥ 2,462

単行本 ( 323 ページ )

ISBN-10 : 4774111422

ISBN-13 : 9784774111421


Post to Twitter

, , , ,

No Comments

c++でのファイル読み込みテスト

ゲームプログラマになる前に覚えておきたい技術」を読んでいて、C++のファイル読み込みをやっていた。
Cの時でさえ、そんなにファイルいじったりしていなかったので、ちょっと自分でも書いてみる。
まぁ、写経的なコードになっちゃうんだけれども。

main.cpp

#include &lt;iostream&gt;
using namespace std;
bool readFile( char** buff, int* size, const char* filename );
int main()
{
char *fBuff = 0;
int fSize = 0;
if ( readFile( &amp;fBuff, &amp;fSize, &quot;test_dat.txt&quot; ) ) {
cout.write( fBuff, fSize );
}
delete[] fBuff;
return 0;
}

fileRead.cpp

#include &lt;fstream&gt;
using namespace std;
bool readFile( char** buff, int* size, const char* filename )
{
// 初期化
*buff = 0;
*size = 0;
// ファイルストリームをインスタンス化しつつストリームオープン
ifstream ifs( filename, ios::binary );
// 失敗
if ( !ifs ) { return false; }
ifs.seekg( 0, ifstream::end ); // ファイルストリームを最後まで移動
// 現在の読み込み位置(詰まるところサイズ)を取得
// tellg()はpos_type型を返すので、実体はintでも一応キャスト
*size = static_cast&lt; int &gt;( ifs.tellg() );
ifs.seekg( 0, ifstream::beg );	// ファイルストリームを先頭へ
// メモリ確保
*buff = new char [ *size ];
// ファイルの内容をメモリに読み込む
ifs.read( *buff, *size );
return true;
}

test_dat.txt

Hello.
And say goodbye.

で、結果。

% g++ -o test main.cpp fileRead.cpp
% ./test
Hello.
And say goodbye.

ちなみに、

delete[] *fBuff;

とても、

delete[] &amp;fBuff;

としても怒られた。
ダブルポインタは大本の変数名だけ指定すれば良いんですかい?
メモリリークとかしないのか?
この前紹介した「OpenGLで作るiPhone SDKゲームプログラミング」では

ParticleSystem::~ParticleSystem()
{
int i;
for (i = 0; i &lt; this-&gt;amount; ++i) {
delete this-&gt;particle[i];
}
delete this-&gt;particle;
}

なんてコードがあったから、面倒くさいけど個別に解放しないとリークしちゃうのかと思った。
そうか、その配列内でさらにnewしているから個別に解放しているのか。
そうじゃない場合はそのまま解放して良い、と。
ポインタをもっとよく勉強せねば。。。

そんだけ。

Post to Twitter

, , ,

No Comments