龍神録プログラミングの館10章 ゲームの初期化について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
mj

龍神録プログラミングの館10章 ゲームの初期化について

#1

投稿記事 by mj » 13年前

龍神録プログラミングの館をいろいろと勉強しながら10章まで進めてきました。
10章のゲームの初期化を行っている関数について分からない部分があるためご質問致します。

コード:

void ini(){
        stage_count=1;
        memset(&ch,0,sizeof(ch_t));//自機データの初期化
        ch.x=FIELD_MAX_X/2;
        ch.y=FIELD_MAX_Y*3/4;
        memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);//敵データの初期化
}

上記関数の中の
memset(&ch,0,sizeof(ch_t));
memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);
部分の処理がいまいちよく分かりません。

自分なりにmemset内の処理を調べてみたところ&chが示す領域を0でch_tバイト分埋めるらしいので
memset(&ch,0,sizeof(ch_t));に関しては
        ↓
typedef struct{
int flag;=0 //フラグ
int cnt=0; //カウンタ
int power=0; //パワー
int point=0; //ポイント
int score=0; //スコア
int num=0; //残機数
int mutekicnt=0; //無敵状態とカウント
int shot_mode=0; //ショットモード
int money=0; //お金
int img=0; //画像
int slow=0; //スローかどうか
double x=0,y=0; //座標
}ch_t;
というようにch_tのint型の変数とdouble型の変数に0を格納して初期化する。

memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);に関しては
           ↓
typedef struct{
   int flag=0; //フラグ
int cnt=0; //カウンタ
int pattern=0; //移動パターン
int muki=0; //向き
int knd=0; //敵の種類
int hp=0; //敵のHP
int hp_max=0; //HP最大値
int item_n[6]=0; //落とすアイテム
int img=0; //画像

double x=0; //x座標
double y=0; //y座標
double vx=0; //速度x成分
double vy=0; //速度y成分
double sp=0; //スピード
double ang=0; //角度

int bltime; //弾幕開始時間
int blknd=0; //弾幕の種類
int blknd2=0; //弾の種類
int col=0; //色
int state=0; //状態
int wtime;=0 //待機時間
int wait=0; //停滞時間
}enemy_t;
同じようにintとdouble型の変数に0を格納して初期化する。これが*ENEMY_MAX分だけある。

というような処理が行われているのではと考えているのですがこれで正しいでしょうか。

あと一つmemsetの第一引数がchでは&がついてポインタ型になっておりenemyには&がついていないの
ですがこちらに関しましてどのような違いがあるのでしょうか。どなたかご教授の程お願い致します。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#2

投稿記事 by softya(ソフト屋) » 13年前

> 自分なりにmemset内の処理を調べてみたところ&chが示す領域を0でch_tバイト分埋めるらしいので

ch_tは構造体の型名なので、sizeof(ch_t)でch_tのサイズを表します。
つまり、ch_tのサイズ分だけ0で埋めているので解釈はあっています。

>memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);に関しては

こちらもその通りです。

>あと一つmemsetの第一引数がchでは&がついてポインタ型になっておりenemyには&がついていないの
>ですがこちらに関しましてどのような違いがあるのでしょうか。どなたかご教授の程お願い致します。

memsetの第一引数はポインタです。これは絶対条件です。
さて、ここで質問です。
宣言時の書き方と別にポインタとして解釈される変数の宣言がありますが、それは何でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

mj

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#3

投稿記事 by mj » 13年前

memset()の構造はおそらくvoid *memset(void *buf, int c, size_t size);となっていると思われます。
memset(&ch,0,sizeof(ch_t));を見てみると
void *buf;
buf=&ch;
という感じでこちらは普通のポインタbufに変数chのアドレスを代入する形になっているのだと分かります。

しかし、問題のmemset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);のほうを見てみると
void *buf;
buf=enemy;とは普通はできませんよね。ポインタbufにはアドレスが入るはずですから。

そこで[ポインタとして解釈される変数]について考察してみたところそれは配列のことではないかという
考えに至りました。
int a[5];という配列を例にあげてみます。(以降に書いてあることが間違っていたらスミマセン)
int a[5];のaは配列の最初の要素:a[0]を指し示すポインタの役割をしています。
配列のa[0]のアドレス:aをポインタpに代入するには
int *p=a;
とすればpにa[0]のアドレスが代入されたことになります。

このことを考慮したうえでmemset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);を見てみると
元々enemyは配列でありenemy[ENEMY_MAX];となっていました。
define.hでENEMY_MAX 30と宣言されているためenemy[30];の配列であることが分かります。
改めて
void *buf=enemy;
とすれば上記のint *p=a;と同じ形になっていることが分かりました。
つまりenemyというのはenemy[0](int型やdouble型の22コの変数が入った箱)のアドレスであり
そのアドレスをポインタbufへと代入していることになります。

このように考えると&chとenemyはどちらも同じことを行っていることがわかります。

とりあえず、enemyについてなぜ&がついていないのかという疑問に対してenemyは配列であるから
&がなくてもポインタとしての役割をしてくれるという結論を出しました。
softya(ソフト屋)さんこちらに関しましてはこれで正しいでしょうか。

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#4

投稿記事 by Dixq (管理人) » 13年前

正しいですよ。
なお、
memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);
とわざわざ書かなくても
memset(enemy,0,sizeof(enemy));
でもOKです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#5

投稿記事 by softya(ソフト屋) » 13年前

そうですね。
配列はポインタとして扱うことができます。
こういうことは、C言語の入門書にも書かれていると思いますので、この機会に読みなおして見ることをオススメします。
そうすれば更に知見が深まることでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

mj

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#6

投稿記事 by mj » 13年前

softya(ソフト屋) さん、ありがとうございます。
これでmemsetのenemyで行われている処理を理解することができました。

memsetに関して色々と調べているときに構造で疑問に思ったことがあります。
memsetの基本構造はvoid *memset(void *buf, int c, size_t size);となっていますが、
ここで分からないのはvoid *memset()とvoid *bufとなっているところです。
こちらはmemset()関数とmemset()関数の引数であるbuf変数ですがvoid*型となっています。
int*型やchar*型であれば格納する箱の大きさのようなものがイメージできるのですが
voidは戻り値や引数をもたない関数(つまり空の関数)を表すものであると認識しています。
だから、void*型の箱の大きさというものがどのようなものであるかのイメージできません。

memsetの構造に関しまして下記の内容までは分かりましたがvoid*部分でどのよなことが
行われているのかが分かりません。こちらはどのような構造となっているのでしょうか。
memset():メモリの内容をすべて同じ値に設定する
void *buf:メモリ領域の先頭アドレス
int c:設定する値
size_t size:cで設定した値を代入するメモリのバイト数(値cを入れる箱の数)

アバター
Tatu
記事: 445
登録日時: 15年前
住所: 北海道

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#7

投稿記事 by Tatu » 13年前

「void ポインタ」や「memset」で検索をかけ、まとめてみるとこのようになりました。

(1)void*とは何か?
void*型は
他のポインタ型からvoid*型への変換が可能
(memsetで任意のポインタが受け取れる)
void*型から任意のポインタ型への変換が可能
(メモリを動的確保するmallocなどの返り値(void*)を他のポインタ型にキャストすることができる)
という性質を持つポインタ型です。この性質から汎用ポインタと呼ばれることがあります。

(2)void型の変数がないのに、void*型があるのはおかしくない?
void型の変数のアドレスを入れるためのポインタではなく、
memsetの引数のように何の型のアドレスかわからないけど
アドレスを入れる必要がある場合にvoid*型のポインタを使うと考えればよいと思います。

(3)void*のサイズは?
void型の変数は存在せず、またvoid型の関数は何の値も返せませんが
void*はポインタです。アドレスが入るサイズを持っています。

(4)memsetにvoid*って書かれているけどどういう意味なのか?
void*型はmemsetのようなどのようなポインタでも引数として受け取れる関数を作成するのに使えます。
void*型の引数にポインタを入れると自動的にvoid*にキャストされるため、どのようなポインタでも入れられます。
memsetの返り値はvoid*にキャストされたbufなので返り値の型はvoid*になります。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#8

投稿記事 by softya(ソフト屋) » 13年前

void*は型として大きさを持たないポインタです。何者でもないとポインタと言ったところでしょうか。
これは特殊なので、戻り値を返さないvoidとは意味が異なります。
なので、sizeof(void)はワーニングだったりサイズが-だったりしますがsizeof(void*)はポインタのサイズを返します。

大きな特徴は、型変換をせずに全ての型のポインタを代入可能なことです。※試してみて下さい。
ただし、型のあるポインタにvoid*を代入する場合は型変換が必要です。※試してみて下さい。

こういうのがちゃんと書いてあるC言語の書籍やサイトを読んで欲しいものです。


>memsetの構造に関しまして下記の内容までは分かりましたがvoid*部分でどのよなことが
>行われているのかが分かりません。こちらはどのような構造となっているのでしょうか。

参考に実装サンプルをお見せします。ただし、本物のコンパイラがこれで実装されているわけではないです。
「BohYoh.com【C言語講座】標準ライブラリ memset」
http://www.bohyoh.com/CandCPP/C/Library/memset.html

size_tに関してはintじゃないのは歴史的な経緯があります。
なぜなのかを勉強のため調べてみて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

mj

Re: 龍神録プログラミングの館10章 ゲームの初期化について

#9

投稿記事 by mj » 13年前

>Tatu さんvoid*についてのご解説どうもありがとうございました。
voidとvoid*は別物でvoid*はどのようなポインタでも代入できる便利な型といったものなのですね。
これでvoid *memset(void *buf, int c, size_t size);の構造についておおまかな状況を把握できました。

>softya(ソフト屋) さん、度々のアドバイスありがとうございます。
自分が所有している書籍は基本的な初心者向けのものであるためそれほど深く掘り下げた内容までは
記載されていませんでした。今度は関数の構造等に対して一歩踏み込んだようなことも解説されている
書籍を探してみようと思います。
size_tに関して少し調べてみましたがなにやらsize_t型と呼ばれる特殊な型で入る値は常に0以上の整数と
なるらしいですね。今度時間があるときに詳しく勉強してみます。

softya(ソフト屋) さん/Tatu さんこの度はお付き合い頂きありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る