C言語メモ C++入門編
exeGCC設定
ポインタ
データの型操作
enum (列挙指定子)
struct(構造体)
union (共用体)
ビットフィールド
メモリの動的確保
ヒープ領域
動的に確保されたメモリの初期化
リスト構造
ある配列のアドレス値を得る。
char mem_du_t[]={
'\n','\r','m','e','m','o','r','y','
','d','u','m','p','\n','\r'};
int dump_count = 0;
unsigned *xxx;
xxx = & mem[dump_count] ;
変数 'xxx'に、配列 'mem'の置かれているアドレスが入ります。
宣言についての注意 変数 'xxx'は、ポインタ変数
*xxxとして、宣言する。
データの型操作
enum (列挙指定子)
enum を使用することで「定数値を特定の単語に置き換える」ことができる。これは
#define や const int とほぼ同じ機能だが、これらと違い「メンバ変数のような形で
定数値を作れる」というメリットがある。
つまり、enum を用いると、 複数の数字に一意な「名前」を 付ける事が出来ます。
defineとの違いは・・・
大量の数字に名前を順番に付けたい場合、define では 人間が1つ1つ割り当てなければ
なりません。enum では、 コンパイラが自動的に数字を割り当てるので、重複を
気にする事なく使用できます。define 文では、数字と名前の対応付けは人間が管理しなければ
なりませんが、enum 文では任せてしまえるのです。
プログラム中に、直接数字を書く事は良い事ではないので、 enum を用いて、
名前で使うように心がけましょう。
enum を使う場合は、次の3段階が必要です。
1.定義
まず、目的の enum 型を作る。enum 型の宣言は、
enum 型の名前 {名前1、名前2 ....};
という形を取ります。例えば、
enum moon{full, half, crescent};
とすると、full=0, half=1, crescent=2となります。
2.宣言
上記のように宣言した後、普通の変数と同じように宣言しなければ なりません。
enum moon val;
とすると、「enum moon」型の変数「val」が使えるようになります。
3.使用
宣言された変数は使う事が出来ます。
val = half;
とすると、変数「val」には「half」(この場合は 1 ですね) が代入されます。
直接数字を書くよりも、意味が明確なので プログラムが非常に分かりやすくなります。
プログラム例 enum.lzh(GNU VR5432) enum_ghs.lzh(GHS VR5432)
(これはdefineを使ってもよいのですがね。。)
void led()
{
int i;
int address0=0xb8002000;
enum LED {mem1=0xffffffff,mem2=0xfff7ffff,mem3=0xf7ffffff,
mem4=0xefffffff,mem5=0xdfffffff,mem6=0xfeffffff,
mem7=0xfffeffff,mem8=0xfffffeff,mem9=0xfffffffe,
mem10=0xfffffffd,mem11=0xfffffffb,
mem12=0xfffffff7,mem13=0xfffff7ff
};
/* 消去 */
*(unsigned *)address0=mem1;
while(1){
for(i=0;i<50000;i++)
{
*(unsigned *)address0=mem2;
}
}
注意点
enum を用いた場合 define同様、ディバッグ情報として変数値が得られません。
つまり、グローバル変数やローカル変数のウインドウで、変数値を見ることができません。
もちろん、インスペクトもできません。
−以上−
struct (構造体)
複数の変数をまとめて扱う事を可能とする。
また、構造体を使う事で変数の数を減らし、プログラムを読みやすくできます。
例 V850EのテグチップボードでLEDを光らせるもの struct1.lzh (GHS NB850E)
struct led{
int address0;
int mem0;
};
st()
{
int i;
static struct led a,b,c;
a.address0 = 0x3802000;
a.mem0 = 0x90a3a3a1;
while(1){
for(i=0;i<1000000;i++)
{
*(unsigned *)a.address0=a.mem0;
}
}
}
注意! 構造体の初期化は static にて行うこと!!
これでは、構造体を使うより、普通の変数を使った方がいいな。
配列にしたときの使い方。 struct2.lzh (GHS NB85E)
struct led{
int address0;
short int mem0;
};
st()
{
int i,j;
static struct led loop[]={
{0x3802000,0xf7ff},
{0x3802000,0xfff7},
{0x3802000,0xfffb},
{0x3802000,0xfffd},
{0x3802000,0xfffe},
{0x3802000,0xfeff},
{0x3802002,0xfffe},
{0x3802002,0xfeff},
{0x3802002,0xdfff},
{0x3802002,0xefff},
{0x3802002,0xf7ff},
{0x3802002,0xfff7},
};
while(1){
for(j=0;j<12;j++)
{
for(i=0;i<50000;i++)
{
*(unsigned *)loop[j].address0=loop[j].mem0;
}
}
}
}
ディバッガ(PARTNER)での表示例

ポインタを用いた場合
ポインタを用いて上記のプログラムを書き直すと
struct led{
int address0;
short int mem0;
};
static struct led loop[]={
{0x3802000,0xf7ff},
{0x3802000,0xfff7},
{0x3802000,0xfffb},
{0x3802000,0xfffd},
{0x3802000,0xfffe},
{0x3802000,0xfeff},
{0x3802002,0xfffe},
{0x3802002,0xfeff},
{0x3802002,0xdfff},
{0x3802002,0xefff},
{0x3802002,0xf7ff},
{0x3802002,0xfff7},
};
st()
{
int i,j;
struct led *ptr;
ptr = loop;
// loopに&がついていないことに注意
while(1){
for(ptr=loop;ptr<=loop+11;ptr++)
{
for(i=0;i<50000;i++)
{
*(unsigned *)ptr->address0=ptr->mem0;
}
}
}
}
UNION (共用体)
共用体とは、「複数のデータに対して同じメモリ領域を割り当てる」 構造です。
メモリ空間を思いっきりケチる人(特に関西系)が、よく用います。
たとえば、int mem0; short int mem1;を共用体の中で宣言すると、
int 32ビットと short int 16ビットを合わせた領域が確保されるのではなく、
宣言された中での大きい方のint(32ビット)の領域が確保されます。
例)この例では、共用体"led"で確保した領域の下位16ビットを書き換える技を使っています。
union.lzh (GHS NB85E)
struct add{
int address0;
};
union led{
int mem0;
short int mem1;
};
st()
{
int i;
static union led c ;
static struct add a;
a.address0 = 0x3802000;
/* 2ooo */
for(i=0;i<500000;i++)
{
c.mem0 = 0xa4a3a3a3;
*(unsigned *)a.address0=c.mem0;
}
for(i=0;i<500000;i++)
{
c.mem1 = 0xa3f9;
*(unsigned *)a.address0=c.mem0;
}
}
プログラム実行後の変数値
つまり・・・
共用体では、違う名前の変数でも後から値を変更すると、前の変数の値も変わってしまいます。
ビットフィールド
構造体の機能の一つで、ビット単位でデータを扱うことができるもの。
例)bit_field.lzh (GHS NB85E)
struct led{
int address0;
int del;
int mem0:8; /* mem0 と言う変数を8bit長にする */
};
st()
{
static struct led a,b,c,d; /* 構造体の名前を決定 */
a.address0 = 0x3802000;
b.address0 = 0x3802001;
c.address0 = 0x3802002;
d.address0 = 0x3802003;
a.del = 0xffffffff;
a.mem0 = 0x99;
b.mem0 = 0x86;
*(unsigned *)a.address0=a.del;
*(unsigned *)d.address0=a.mem0;
*(unsigned *)c.address0=b.mem0;
}
プログラム実行後の変数
どうも、パートナーでは、ビットフィールドの構造体中の変数は、表示できないみたいです。
メモリの動的確保
巨大な配列へのメモリの割り当てを、静的にしてしまうとメモリの無駄なので、
使うときに確保して解放する作業を言います。
使う関数
malloc(size_t size)
ヒープメモリから sizeにて指定された大きさを(確保)割り当てます。
size_tとは、sizeof演算子や、strlen関数の結果の型です。
exeGCCの場合は、Memory.hをみると、定義されています。
typedef unsigned size_t;
(フツーは、stdio.hで定義するのではないのかな??)
unsigned だけなので、int型となります。
つまり、size_t は、unsigned intをあらわします。
それなので、size_t sizeは、unsigned int size のことです。
_m_init(&_heep,&_heep_size); (exeGCCのみ)
exeGCCにて、malloc関数を使うときは、スタートアップルーチンで
上記の関数を実行します。
_heepと_heep_sizeは、リンクディレクティブファイルで宣言しておいてください。
ヒープ領域とは。。。
プログラムが使用できるメモリの中から実行コード領域や、静的変数やグローバル変数の領域、
スタック領域を引いた残りの領域です。
リンクディレクティブファイルにて、bss領域内で宣言して下さい。
例)
.bss . :
{
*(.bss)
*(COMMON)
_end = . ;
_heep = . ;
}
サイズは、リンクディレクティブファイルの先頭で宣言しましょう。
_heep_size = 0x2000 ;
これで、準備完了なので、サンプルプログラムを作ってみます。
void main()
{
unsigned *aa;
unsigned *dd;
aa = (int *)malloc(sizeof(int) * 10);
dd = (int *)malloc(sizeof(int) * 10);
}
プログラム実行後の変数値
ポインタ変数aaの値に、mallocにより確保された領域の先頭アドレスが表示され、
ポインタ変数ddの値に、そのつぎ(aaにて確保された領域のあと)のアドレスが表示されています。
計算してみると・・・
sizeof(int)は、intで宣言する変数の大きさだから sizeof(int)=0x4
また、 10 = 0xa
上記より、
0x4 * 0xa = 0x28 なので
確保されるアドレスは 0x80003040〜0x80003068 です。
つぎに、確保する領域を広げてみます。
void main()
{
unsigned *aa;
unsigned *dd;
aa = (int *)malloc(sizeof(int) * 100);
dd = (int *)malloc(sizeof(int) * 10);
}
プログラム実行後の変数値
前回よりも、ddのポインタ位置が後ろに確保されていることが分かります。
別に、sizeof(int)を用いなくて、直接値を書いても使えます。
void main()
{
unsigned *aa;
unsigned *dd;
aa = (int *)malloc(0x10 * 0x10);
dd = (int *)malloc(sizeof(int) * 10);
}
等々・・・
注意 mallocにて確保した領域が必要無くなったら、free()にて解放しよう!
貴重なリソースは大切に。
void main()
{
unsigned *aa;
unsigned *dd;
dd = (int *)malloc(sizeof(int) * 10);
free(dd);
aa = (int *)malloc(sizeof(int) * 10);
}
プログラム実行後の変数値
解放されているので、同じメモリ空間がアロケートされています。
Javaだったら、メモリーリークの処理は自動でやってくれるのにな・・・
動的に確保されたメモリの初期化
使う関数 calloc(size_t n,size_t,s);
ヒープメモリ内のどこかで、s*nにて指定された大きさのメモリ空間を確保し
0にて初期化する。
サンプルプログラム
void led()
{
unsigned *aa;
unsigned *dd;
aa = (int *)calloc(sizeof(int),0x10);
dd = (int *)malloc(sizeof(int) * 10);
}
実行後のメモリウインドウ
実行後の変数ウインドウ
0x4 * 0x10 = 0x40
つまり・・・
0x80003040 〜 0x80003080 までの領域が"0"にて初期化されています。
リスト構造
詳しくは、ここを参考にしましょう。
要するに、構造体がポインタにより繋がっているようなものです。
サンプルプログラム
struct list {
int num_data;
struct list *nextlist;
};
main()
{
struct list *a, *b;
char str[8];
char src[8];
int i;
for(i=0;i<7;i++)
{
str[i]=0x41+i;
a = (struct list *)malloc(sizeof(struct list));
a->num_data = atoi(str); /* atoiは文字列をint型に変換する */
a->nextlist = b;
b = a;
}
str[7]=NULL;
strcpy(src,str);
i++;
}
プログラム実行結果
ポインタ変数nextlistに、次の構造体変数のアドレスが入っています。
可変長の配列
大きさが分からない配列を作ります。
例)
char *mem_du;
unsigned int leng;
mem_du = (char *)calloc(leng,sizeof(char));
ここで、lengの値を変えると、配列の大きさも変化します。
重要な関数
atoi と itoa
atoi 文字列を整数に変換
itoa 整数を文字列に変換
[ヘッダ] stdlib.h
参考のプログラム
#include <stdio.h>
#include <stdlib.h>
main()
{
char a[20], *pa;
int i,n;
a[0] = '7';
a[1] = '8';
for(i=0;i<10;i++)
printf("%d",a[i]);
puts("\n");
pa=a;
n=atoi(pa);
printf("10進数では %8d\n",n);
printf("16進数では %8.8x\n",n);
for(i=0;i<10;i++)
a[i]=0;
n=7;
itoa(n,a,0x10);
for(i=0;i<10;i++)
printf("%d",a[i]);
}