2009年7月26日

自己参照型(線形リスト)

線形リストを作成

-----------------------------------------queue.c
#include stdio.h
#include stdlib.h
#include string.h

/*-----メニュー-----*/
typedef enum{
  Term, /* 項目 */
  Insert, /* 先頭ノードに挿入 */
   Append, /* 末尾ノードに挿入 */
   Delete, /* 先頭ノードを削除 */
   Remove, /* 末尾ノードを削除 */
   Clear, /* 全ノード削除 */
   Print /* 出力 */
} Menu;

/*-----Node----*/
typedef struct __node{

  char name[20];
  char tel[16];

  struct __node *next;

} Node;


/*----線形リスト制御ブロック----*/
typedef struct {

  Node *head;
  Node *tail;

} List;


/*----一つのノードを確保----*/

Node *AllocNode(void){

  return ((Node *)calloc(1, sizeof(Node)));

}

/*----InitList----*/
void InitList(List *list){

  list->head = list->tail = AllocNode();

}

/*----InsertNode----*/
void InsertNode(List *list, const char *name, const char *tel){

  Node *ptr = list->head;

   list->head = AllocNode();
   strcpy(list->head->name, name);
   strcpy(list->head->tel, tel);
   list->head->next = ptr;

}

/*----AppendNode----*/
void AppendNode(List *list, const char *name, const char *tel){

   Node *ptr = list->tail;

   list->tail = AllocNode();
   strcpy(ptr->name, name);
   strcpy(ptr->tel, tel);
   ptr->next = list->tail;

}

/*----DeleteNode----*/
void DeleteNode(List *list){

   if(list->head !=list->tail){
      Node *ptr = list->head->next;
      free(list->head);
      list->head = ptr;
  }
}

/*----RemoveNode----*/
void RemoveNode(List *list){

   if(list->head != list->tail){
      if(list->head->next == list->tail){
         DeleteNode(list);
      }else{
         Node *curr, *prev;

         curr = list->head;
        while(curr->next != list->tail){
            prev = curr;
            curr = curr->next;
        }

        prev->next = list->tail;
        free(curr);
     }
  }
}

/*----ClearList----*/
void ClearList(List *list){

   Node *ptr = list->head;

   while(ptr != list->head){
      Node *ptr2 = ptr->next;
      free(ptr);
      ptr = ptr2;
   }

   list->head = list->tail;

}

/*----PrintList----*/

void PrintList(List *list){

  Node *ptr;

  ptr = list->head;

   while(ptr != list->tail){
      printf("%s <<%s>> \n", ptr->name, ptr->tel);
      ptr = ptr->next;
   }

}

/*----データ入力----*/
Node Read(char *message){

  Node temp;

  printf("%sするデータを入力して下さい。\n",message);

   printf("name:"); scanf("%s", temp.name);
   printf(" tel:"); scanf("%s", temp.tel);

   return(temp);
}

/*----メニュー選択----*/
Menu SelectMenu(void){

   int ch;

   do {
      puts("(1) 先頭にノードを挿入 (2)末尾にノードを挿入");
      puts("(3) 先頭のノードを削除 (4)末尾のノードを削除");
      puts("(5) 全てのノードの削除 (6)全てのノードを表示");
      puts("(0) 終了");
      printf("番号:"); scanf("%d", &ch);
   }while(ch <> Print);
   return((Menu)ch);
}

int main(void){

  Menu menu;
   List list;

  InitList(&list);

   do{
      Node x;
      switch(menu = SelectMenu()){

        case Insert: x = Read("先頭に挿入");
           InsertNode(&list, x.name, x.tel);      break;
        case Append: x = Read("末尾に挿入");
           AppendNode(&list, x.name, x.tel);     break;
        case Delete:DeleteNode(&list);           break;
        case Remove:RemoveNode(&list);        break;
        case Clear:ClearList(&list);              break;
        case Print:PrintList(&list);               break;
     }
   }while(menu != Term);

   ClearList(&list);
  return (0);
}


上記サンプルは
下記、参考書参照。
これいいよ^^




次回は循環リスト、双方向リストです。

2009年5月30日

関数、変数のスコープとmakeファイル

今回は、下記のような流れでやっていきますよ。

1.extern宣言,static宣言
2.ヘッダファイル
3.makeファイル作成

ポイントは、関数、変数のスコープmakeファイルです。

1.*.c, *.h作成
mai.c
#include

void init();//init()のプロトタイプ宣言
void func1();//
func1()のプロトタイプ宣言

void init(){}
void func1(){}
int main(void){

init();
func1();
exit(0);

}

main.c内のプロトタイプ宣言は皆さんもご存知の通り、
min()内でinit()やdone()を使用する際にコンパイラに引数の数と型と戻り値の型を
教えてあげるために宣言するものでした。

$gcc -c main.c -Wall
これは、問題なくコンパイルされますね。

では、以下のように別ファイルに関数の実体がある場合はどうでしょうか?

mai.c
#include

void init();//init()のプロトタイプ宣言
void func1();//func1()のプロトタイプ宣言

int main(void){

init();
func1();
exit(0);

}


init.c
void init(){}
func1.c
void func1(){}
$gcc -c main.c -Wall
$gcc -c init.c -Wall
$gcc -c func1.c -Wall
$gcc main.o init.o func1.o dummy -Wall

同様に
問題なくコンパイル・リンクされました。

つまり、init()とfunc1()は外部に公開されている事になりますね。
外部に公開されているというのは、main.c以外の別ファイルから
関数を呼び出す事ができるという事です。
つまり、関数はデフォルトの定義でグローバルになるわけです。
明示的に関数をグローバルに宣言するには、次のようにしてextern接頭語を用います。


mai.c
#include

extern void init();//init()のプロトタイプ宣言
void func1();//func1()のプロトタイプ宣言

int main(void){

init();
func1();
exit(0);

}

$gcc -c main.c -Wall
$gcc main.o init.o func1.o dummy -Wall

結果は、extern接頭語を使用してもしなくても同様ですね。
また、関数の定義自体にも

func1.c
extern void func1(){


とすることもできます。
ちなみにstatic接頭語も勉強しましょう。

static接頭語を用いて関数や変数を定義すると、
スコープはファイル中に限定されます。
つまり、static定義されている関数は、
そのファイルの中でしか使えないという事になります。
そのため、static定義された関数のプロトタイプ宣言を書く場合、
プロトタイプ宣言と実際の関数の定義は同一ファイル内に
ある必要があります。

次に変数の場合です。
1)value1は、関数内でstatic宣言しています。
2)value2は、関数内で普通に宣言しています。
3)value3は、関数外でstatic宣言しています。
4)value4は、関数外で普通に宣言しています。

【func1.c】
#include
#include

static int value3 = 3;
int value4 = 4;


void func1(){

static int value1 = 1;
int value2 = 2;
printf("value1 = %d\n",value1);
printf("value2 = %d\n",value2);

value1 = value1 + 1;
value2 = value2 + 2;

}

void func2(){

printf("value3 = %d\n",value3);
value3 = value3 + 1;

}

【main.c】

#include
#include

extern void init();//init()のプロトタイプ宣言
void func1();
//func1()のプロトタイプ宣言
void func2();//func2()のプロトタイプ宣言

extern int value4;//value4のextern宣言

int main(void ){

init();
func1();
func1();
func2();
printf("value4 = %d\n",value4);
exit(0);

}



$gcc -c main.c -Wall
$gcc -c init.c -Wall
$gcc -c func1.c -Wall
$gcc main.o init.o func1.o dummy -Wall


実行結果
$ ./dummy.exe
value1 = 1
value2 = 2
value1 = 2
value2 = 2
value3 = 3
value3 = 4
value4 = 4

結果をまとめると、
1)関数内でstatic宣言された変数value1は、変数の値が保存されます。
2)関数内で普通にに宣言された変数value2は、func()が呼ばれる度に
初期化されfunc()を抜けると、値は消滅する。
3)関数外でstatic宣言された変数value3は、func.c全体がスコープになり、
変数の値が保存されます。これを広域変数といいます。
4)関数外で普通に宣言された変数value4(main.cでextern宣言されている変数value4)は、
extern宣言する事で、別ファイルにあるvalue4の実体の型をコンパイラに通知することができます。

関数と変数で微妙に違いますね~
ややこしや~~~

所で、上記のようなサンプルプログラムの規模なら問題ないのですが、
大規模のプログラムの場合、関数や変数を利用するたびにプロトタイプ宣言や
extern宣言を行う必要が出てくる事になります。面倒です。
そこで次の章です。

2.ヘッダファイル

下記のように関数のプロトタイプ宣言をヘッダファイルに書きます。

【main.c】
#include
#include

#include "../inc/init.h"
#include"../inc/func.h"

int main(void){

init();
func1();
func1();
func2();
func2();
printf("value4 = %d\n",value4);
exit(0);

}

【init.h】
extern void init();//init()のプロトタイプ宣言

【init.c】
#include "../inc/init.h"

void init(){
}

【func.h】

void func1();//func1()のプロトタイプ宣言
void func2();//func2()のプロトタイプ宣言
extern int value4;//value4のextern宣言

【func.c】
#include
#include "../inc/func.h"

static int value3 = 3;
int value4 = 4;


void func1(){

static int value1 = 1;
int value2 = 2;
printf("value1 = %d\n",value1);
printf("value2 = %d\n",value2);

value1 = value1 + 1;
value2 = value2 + 2;

}

void func2(){

printf("value3 = %d\n",value3);
value3 = value3 + 1;
}

ここで、ヘッダファイルや.cファイルには何を書いたら言いかというと、

ヘッダファイルには
  • 外部に公開する定義、宣言
.cファイルには
  • そのファイル専用の宣言や定義
  • 関数、変数の実体
となります。

今回は、コンパイル・リンクを手動で行ってきました。
面倒です。
決まった手順でコンパイル・リンクするので、
makeファイルを作りましょう。
ってことで、次の章です。

3.makeファイル作成

拡張子はなしで、ファイル名をMakefileとします。
内容は下記の通りとなります。
Makefileは*.cと同じディレクトリにおきます。

OBJS =main.o init.o func.o
TARGET += dummy.exe
CC =gcc
CFLAGS +=-O
CFLAGS +=-wall
#setenv CFLAGS -I/../inc/


.SUFFIXES: .c .o

all: $(TARGET)

$(TARGET):$(OBJS)
         $(CC) $(OBJS) -o $(TARGET) $(CFLAGS)

.c.o: $*.c
   $(CC) -c $*.c $(CFLAGS)

clean:
   rm -f $(OBJS) $(TARGET)

$make とすると、実行ファイルが出来上がります。
$make clean とすると、*.oと*.exeが削除されます。
楽チンですね^^

まず、各種変数の設定を行います。
-
---------------------------------------
OBJS =main.o init.o func.o
TARGET += dummy.exe
CC =gcc
CFLAGS +=-O
CFLAGS +=-wall

---------------------------------------
下記は、*.cから*.oを作成するという宣言らしいです。

.SUFFIXES: .c .o
---------------------------------------
下記は、$make allを実行すると、TARGETが生成されます。
TARGETはdummy.exeです。

all: $(TARGET)

---------------------------------------
下記は、TARGETの生成手順の定義です。

$(TARGET):$(OBJS)
        $(CC) $(OBJS) -o $(TARGET) $(CFLAGS)

---------------------------------------
下記は、*.cから*.oの生成手順の定義です。

.c.o: $*.c
   $(CC) -c $*.c $(CFLAGS)

---------------------------------------
下記は、$make cleanを実行すると、OBJSとTARGETを削除します。
OBJSは*.c、*.oです。
TARGETはdummy.exeです。
clean:
   rm -f $(OBJS) $(TARGET)


あと、「+=」と「?=」の説明です。

「+=」は、前回の変数の値に追加するという意味。
「?=」は、値が設定されていないときのみ、値を設定するという意味。

つまり、デフォルト定義が存在するという事です。
今のところcygwin上では、どこに設定ファイルがあるか不明です。
知ってる方、教えてくださいm(_ _)m

あと、includeのパス設定をmakeファイルに追加は
また、次回にします。

とりあえず、今回は以上です。





















2009年5月17日

パソコンが起動しなくなった。

WindowXPとLinux Fedora Core6のマルチブートにしているNote-PCが起動しなくなった。
理由:WindowsXPからFedoraのパーティションを削除した為。
下記のような画面が出てきて、焦った。。。。

--------------------------------[画面]----------------------------------------
GNU GRUB version 0.97 (635K lower / 1037000K upper memory)
[ Minimal BASH-like line editing is supported. for the first word, TAB
lists possible command completions. Anywhere lese TAB lists the possible
completions of a device/ filename.]

grub> root (hd0,0)
Filesystem type unknown, partition type 0x7

grub>chainloader +1

grub>boot
-----------------------------------------------------------------------------
すると、windowsXPの起動画面が出てきた。


が。。。。。Note-PC製造メーカの画面に遷移し、初期化。

また、GNUの画面で上記コマンドを打つ。
すると、またWindowsXPの起動画面が。。。また、製造メーカの画面に。。。。

--------------------------------[画面]----------------------------------------
grub> root (hd0,1)

Filesystem type unknown, partition type 0x7

grub>chainloader +1

grub>boot
-----------------------------------------------------------------------------
ってすると、WindowsXPが起動した。。。。
WindowsのBootLoaderはパーティション
hd(0,0)ではなく、hd(0,1)だった。

次の問題は、windowsXPからUSBが認識しなくなった。
この問題は、しばらくすると治った??????

USBが認識したので、外付けCD/DVD ROMから起動し再度Linux FedoraCora6をインストール。

ここで少しお勉強。
ブートプロセスについて

ブートプロセスは下記のようになっている。

1.Power ON --> 2.BIOS --> 3.Boot Loader
--> 4.Windows OR Linux --> 5.init

1.不揮発メモリに書き込まれているBIOS
(Basic Input/Output System)が実行される。

2.BIOSはHD内の先頭セクタ(MBR)にインストールされた
Boot Loader(GRUB)をメモリロードする。

3-4.Boot Loaderは基本パーティション先頭にある
Boot Sector(WindowsXP or Linux)が実行される。

5.必要なシステムファイルが順次読み込まれログイン画面が出力


2009年5月16日

組込みLinux(H8マイコン×uCLinux)

早速、本を購入しました。


上記の本では、組込み用マイコンを通してコンピュータの本当の姿に触れるとともに、
uClinuxという現代的なOSをマイコンの上で動作させる事でより高度な応答も可能にしようというのが狙いみたいです。

よって下記のような構成となっているみたいですよ。
1.マイコンのごく基本的なプログラミングを体験
2.uCLinuxの活用

また、必要なものを下記に列挙します。

・プログラミングスキル(C言語)
・Linux経験
・Linuxが動作しているPC(ディストリビューションは何でもよい)
・半田ごてなどの電子工作のための工具類

1.今回は、CPUを体験する(windowsかつLinux)
windowの場合
 コマンドプロンプトよりメモリダンプ、書き込み、実行が行えます。

では、5 + 5の演算を行ってみましょう。
青色:コマンド
赤色:演算結果
>debug
-d 100
34C7:0100 B8 02 00 BB 03 00 01 D8-A3 20 01 CD 20 00 00 00 ......... .. ...
34C7:0110 03 00 00 BB 02 00 A3 20-01 CD 20 00 34 00 B6 34 ....... .. .4..4
34C7:0120 05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-a100
34C7:0100 mov ax,5
34C7:0103 mov bx,5
34C7:0106 add ax,bx
34C7:0108 mov [110],ax
34C7:010B int 20
34C7:010D ^C
-g
プログラムは正常に終了しました.
-d 100
34C7:0100 B8 05 00 BB 05 00 01 D8-A3 10 01 CD 20 00 00 00 ............ ...
34C7:0110
0A 00 00 BB 02 00 A3 20-01 CD 20 00 34 00 B6 34 ....... .. .4..4
34C7:0120 05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
34C7:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................


0x5 + 0x5 = 0x0A

作成途中

開発環境

先日、Note-PCをAmazonで購入しました。


スペック
cpu : Atom 1.66GHz
memory: 0.99GB RAM
HD : 160GB
OS :Winxp Home Edition v 2002 Service Pack3

使った感想は、思った以上にリアクションが速いです。
このNote-PCは組み込み開発マシンにしようかなと思ってます。
とりあえず、HDのパーティションを区切り、
Fedora Core 6をインストールしました(3日ほど掛かってしまった。。。)。

Note-PCにはCDドライブが搭載されていません。
ですので、USBメモリにbootdisk.imgを入れ、USBデバイスから
インストーラは起動したのですが、その後のインストーラからの
OS本体の場所と指定方法がわからず、断念。
結局、CD/DVDドライブ購入(; ~ ~) 情けないです。




あと、無線LANルータと。。。



環境は整ったので、とりあえず終了。

2009年4月26日

分割コンパイル for cygwin

今回はcygwinを使って分割コンパイルとmakefileの勉強です。
書籍は、これ。



1.
cygwin ダウンロード/インストールします。
  • http://cygwin.com/からSetup.exeをダウンロードしインストールします。
  • 必要なパッケージはざっくり言うとgcc compilerとmakeです。
a.Devel@default
⇒gcc-core: C compiler
⇒gcc-core: C++ compiler
b. Devel@default
⇒make: The GNU version of the 'make' utillity

参考URL:
cygwin version 1.3.22-1のインストール
Cygwinをインストールする


2.cygwinを起動し、gcc compilerとmakeがインストールされたか確認
※$XXXと表示されているのは、cygwin上でのコマンド入力になります。

a.$gcc --help ⇒ヘルプが表示されればOK
b.$make ⇒「make: *** No targets specified and no makefile found. Stop.」が表示されればOK

3.complie source 
1)"C:\cygwin\home\USER"配下にフォルダを作成
2)hello.cを作成

=====================hello.c
#include
void main(void)
{
printf("hello, world!\n");
return;
}
=====================

3)hello.cと同じディレクトリにMakefileを作成
※ファイル名を「Makefile」とする。
===================Makefile
CC = gcc
CFLAGS = -Wall

hello:hello.o
===================
     
 4)cygwinを起動し、hello.cのあるディレクトリに移動
 5)$meke    ←コンパイル
 6)正常にコンパイル終了すると、hello.cのあるディレクトリにhello.exe(実行ファイル)が生成される。
 7)$./hello.exe  ←実行
    
正しく標準出力に表示されたら、OKです。