9.ファイル分割とヘッダファイル(2)

 

※ 修正版をこちらのページにアップしています。将来的にはこのページは削除される可能性があります。

 


1.ファイルを分割する

前回は、関数を分割するだけで終了してしまいましたが、さらにファイルも分けてみることにします。
ファイルを分ける事によって、変更箇所がないファイルについては、コンパイルする手間が省けます。
どうしてかというと、普通のコンパイラは、コンパイルしたものを OBJ ファイルとしてファイルごとに保存しており、変更がないファイルに関しては、コンパイル作業を飛びぬかして、その後のリンクから行うことで時間の短縮が行えるからです。
※ 大規模なプロジェクトじゃない限り、コンパイルがそんなに遅くなることはありません。
よく、ファイル分割を行う理由のひとつにコンパイル時間が短くなることがあげられますが、今のコンパイラーはそんなに遅くはないと思うので、昔はそうだったと読み替えてください。

ファイルのきり方は、なんでもいいのですが、今回は、以下のように大別してみました。

@ウインドウ・コントロールの作成、ウインドウプロシージャの処理を行うファイル。
A メッセージ処理を行うファイル
B ディレクトリ、ファイル関連の処理を行うファイル

それぞれ、WinMain.cpp、Message.cpp、Filelist.cpp として、前回ほど分割した関数を配置しました。
以下がその概略です。

WinMain.cpp

Message.cpp

Filelist.cpp


/* ウインドウプロシージャ */
WndProc
{
  WM_COMMAND
    IDM_EXIT
      SendMessage(WM_CLOSE)
  WM_CREATE
    OnCreate(hWnd)
  WM_DESTROY
    PostQuitMessage(0)
}

/* エントリポイント */
WinMain
{
  // ウィンドウクラス登録
  RegisterClassEx(&WndClass)
  // ウィンドウ作成
  CreateWindow
  // ウィンドウの表示
  ShowWindow
  // メッセージループ
  while (GetMessage(&Msg))
}

/* コントロール作成 */
WinCtlInit
{
  // コンボボックス作成
  WC_COMBOBOX
    CreateWindowEx
  // リストビュウ作成
  WC_LISTVIEW
    CreateWindowEx
}
       

/* WM_CREATE時処理 */
OnCreate
{
  // コントロール作成
  WinCtlInit(ID_CBOCLIENT)
  WinCtlInit(ID_LVWCLIENT)
  // コンボボックス初期設定
  GetComboBoxItems(hCtlCBOClient)
  // リストビュウ初期設定
  GetListViewItems(hCtlLVWClient)
}
      

/* コンボボックス設定 */
GetComboBoxItems
{
  // カレントディレクトリ取得
  GetCurrentDirectory
  // ドライブ名取得
  GetLogicalDriveStrings
}

/* リストビュウ設定 */
GetListViewItems
{
  FindFirstFile
  FindNextFile
  FindClose

  // ソート
  ListView_SortItemsEx(LVWCmpProc)
}

/* ソート関数 */
LVWCmpProc
{
  lstrcmpi
}
      

そして、コンパイルすると以下のエラーが出ます。

sdk_109-01.png

これはどういうことでしょうか?

前回までに、コンパイラがコンパイルしようとしたときに関数や変数が既知の状態になっていないといけないことをお伝えしました。
これは、同一ファイルだけでなく、複数ファイルでも一緒です。

Message ファイルの中では、WinMain ファイルの WinCtlInit() 関数を呼び出していますが、コンパイラが Message ファイルの WinCtlInit() というコードに達したときに、そんな関数は知らんといっているのです。
Filelist ファイルの GetComboBoxItems() 関数と、GetListViewItems() 関数の呼び出しについても同様です。
WinMain ファイルでも、Message ファイルの OnCreate() 関数を呼び出していますが、同じ理由でコンパイルできません。

同一ファイルであれば、ファイルの上のほうに書けば、よかったですが、異なるファイルの場合、どうしたらよいのでしょうか?
ファイル分割は、ここで頓挫してしまうのでしょうか?
※ プログラミングをやっていると、こうしてみようかなという思いや、ああやってみようという計画が頓挫することはよくあります。
めげないでくださいとしかいいようがありませんが、こんなところもいやになってしまう原因のひとつかも知れません。


2.ヘッダファイル

実は、ファイルの上の方に書くのと同じ効果を発揮する魔法の技があります。(大袈裟か?)
これを、ヘッダファイルと呼びます。
※ リソースの時にちょっと出てきましたね。

早速、ヘッダファイルなるものを用意しましょう。
ファイル名は 「….h」とすれば、何でもいいのですが、「プロジェクト名.h」や「common.h」という場合が多いようです。
今回は、「common.h」にしました。


/* ========================================================================== */
/*          FILE        Common.h                                              */
/*          CREATE      2007/03/11                                            */
/*          MODIFY      ----/--/--                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
/* -------------------------------------------------------------------------- */
/*                      グローバル関数宣言部                                  */
/* -------------------------------------------------------------------------- */
/* ==================== WinMain.cpp ========================================= */
 HWND WinCtlInit(LPCTSTR, HWND, LPVOID, UINT);

/* ==================== Message.cpp ========================================= */
void OnCreate(HWND);

/* ==================== FileList.cpp ======================================== */
void GetComboBoxItems(HWND);
void GetListViewItems(HWND);

ややっ、これは何でしょうか。関数の頭の引数の部分だけしか書いてありません。
これをプロトタイプ宣言といいます。
実は、コンパイラは、コンパイル時点で出てきた関数や変数が既知の状態になっていないといけないのですが、その関数の中身までは既知になっている必要はないのです。
コンパイラは、プロトタイプ宣言があると、その関数の本体はどこかにあるはずということでコンパイルをしてくれるのです。
ですが、引数に関しては、チェックを行っているので、プロトタイプ宣言を行うことで、コンパイルができるようになるのです。

それでも、コンパイルできないでっすって?
ヘッダファイルをインクルードしてください。

ところで、このヘッダファイルには、すべての関数のプロトタイプが書いてあるわけではありません。
そうです。LVWCmpProc() 関数については、Filelist.cpp ファイルの中でしか使いませんね。
このようにファイル内に限定される関数は、そのファイルだけで使えるようにすればいいのです。
※ すべて、ヘッダファイルに入れるという方法もありだとは思いますが、はじめから使わないとわかっている場合は、間違いを起こさないためにも使用するファイル内に限定したほうがいいでしょう。

この場合は、ファイルの上のほうで、プロトタイプ宣言します。
※ プロトタイプ宣言は、引数の型をコンパイルに教える働きがありますので、引数の型は書かないといけませんが、引数名は書いても書かなくても問題ありません。

ふー、何とかWinMain.cpp、Message.cpp、Filelist.cpp の分割までこぎつけました。


/* ========================================================================== */
/*          FILE        WinMain.cpp                                           */
/*          CREATE      2006/10/01                                            */
/*          MODIFY      2007/03/11                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>

#include "Common.h"
#include "Resource.h"


/* -------------------------------------------------------------------------- */
/*                      グローバル(EXTERN)関数宣言部                          */
/* -------------------------------------------------------------------------- */
extern HWND WinCtlInit(LPCTSTR, HWND, LPVOID, UINT);

/* -------------------------------------------------------------------------- */
/*                      ローカル(STATIC)関数宣言部                            */
/* -------------------------------------------------------------------------- */
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


/* -------------------------------------------------------------------------- */
/*          CALLBACK    WndProc                                               */
/*          PARAM       HWND        hWnd        : ウィンドウハンドル          */
/*                      UINT        uMsg        : メッセージ                  */
/*                      WPARAM      wParam      : メッセージの追加情報        */
/*                      LPARAM      lParam      : メッセージの追加情報        */
/*          RETURN      LRESULT                                               */
/*                      メッセージ処理の結果                                  */
/*          REMARK                                                            */
/* -------------------------------------------------------------------------- */
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  /* 関数本体部 */
  switch (uMsg)
  {
    case WM_COMMAND:
      switch (LOWORD(wParam))
      {
        case IDM_EXIT:
          SendMessage(hWnd, WM_CLOSE, 0, 0L);
          break;
        default:
          return (DefWindowProc(hWnd, uMsg, wParam, lParam));
      }
      break;
    case WM_CREATE:
      OnCreate(hWnd);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  }

  return 0L;
}

/* -------------------------------------------------------------------------- */
/*          WINAPI      WinMain                                               */
/*          PARAM       HINSTANCE   hInstance   : 現在のインスタンス          */
/*                      HINSTANCE   hPrevInst   : 直前のインスタンス          */
/*                      LPSTR       lpCmdLine   : コマンドライン引数          */
/*                      int         nCmdShow    : ウインドウの表示形式        */
/*          RETURN      int                     :                             */
/*                      メッセージの wParam パラメータ                        */
/*          REMARK      Win32 アプリケーションの初期エントリポイント          */
/*                      Windows システムが呼び出す。                          */
/* -------------------------------------------------------------------------- */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
  /* 変数宣言部 */
  HWND              hWnd;
  WNDCLASSEX        WndClass;
  MSG               Msg;


  /* 関数本体部 */
  // ウインドウクラス作成
  WndClass.cbSize        = sizeof(WndClass);
  WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  WndClass.lpfnWndProc   = WndProc;
  WndClass.cbClsExtra    = 0;
  WndClass.cbWndExtra    = 0;
  WndClass.hInstance     = hInstance;
  WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  WndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  WndClass.lpszMenuName  = MAKEINTRESOURCE(IDM_MENUMAIN);
  WndClass.lpszClassName = "FTPManager";

  // ウィンドウクラス登録
  if (! RegisterClassEx(&WndClass))
  {
    return false;
  }

  // ウィンドウ作成
  hWnd = CreateWindow(WndClass.lpszClassName, // 登録されているクラス名
                      "FTPManager",           // ウィンドウ名
                      WS_OVERLAPPEDWINDOW,    // ウィンドウスタイル
                      CW_USEDEFAULT,          // ウィンドウの横方向の位置
                      CW_USEDEFAULT,          // ウィンドウの縦方向の位置
                      CW_USEDEFAULT,          // ウィンドウの幅
                      CW_USEDEFAULT,          // ウィンドウの高さ
                      NULL,                   // 親ウィンドウまたはオーナーウィンドウのハンドル
                      NULL,                   // メニューハンドルまたは子ウィンドウ ID
                      hInstance,              // アプリケーションインスタンスのハンドル
                      NULL);                  // ウィンドウ作成データ

  if (! hWnd)
  {
    return false;
  }

  // ウィンドウの表示
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);

  // メッセージループ
  while (GetMessage(&Msg, NULL, 0, 0))
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }

  return (int) Msg.wParam;
}

/* -------------------------------------------------------------------------- */
/*          FUNCTION    WinCtlInit                                            */
/*          PARAM       LPCSTR      szClassName   : ウィンドウクラス名        */
/*                      HWND        hParentWnd    : 親ウィンドウのハンドル    */
/*                      LPVOID      lpParam       : アイテム情報              */
/*                      UINT        uSize         : アイテムのサイズ          */
/*          RETURN      HWND        : 作成されたウィンドウのハンドル          */
/*          REMARK      コントロールウインドウの作成                          */
/* -------------------------------------------------------------------------- */
  HWND WinCtlInit(LPCTSTR szClassName, HWND hParentWnd, LPVOID lpParam, UINT uSize)
{
  /* 変数宣言部 */
  HWND              hControl;
  HINSTANCE         hInstance;
  LPLVCOLUMN        lpLVColumn;
  DWORD             dwStyle;


  /* 関数本体部 */
  // インスタンス取得
  hInstance = (HINSTANCE) GetWindowLong(hParentWnd, GWL_HINSTANCE);

  // コンボボックス作成
  if (lstrcmp(szClassName, "COMBOBOX") == 0)
  {
    dwStyle  = WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWN | CBS_AUTOHSCROLL;
    hControl =
                              CreateWindowEx(WS_EX_CLIENTEDGE,
                              szClassName,
                              NULL,
                              dwStyle,
                              10,
                              5,
                              380,
                              100,
                              hParentWnd,
                              0,
                              hInstance,
                              NULL);

    if (! hControl)
    {
      return NULL;
    }

    SendMessage(hControl, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(true, 0));
    ComboBox_LimitText(hControl, _MAX_PATH);
  }
  // リストビュー作成
  else if (lstrcmp(szClassName, WC_LISTVIEW) == 0)
  {
    dwStyle  = WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL;
    hControl =
                              CreateWindowEx(WS_EX_CLIENTEDGE,
                              szClassName,
                              NULL,
                              dwStyle,
                              10,
                              30,
                              380,
                              320,
                              hParentWnd,
                              0,
                              hInstance,
                              NULL);

    if (! hControl)
    {
      return NULL;
    }

    if (lpParam)
    {
      lpLVColumn = (LPLVCOLUMN) lpParam;

      for (UINT i = 0; i < (uSize / sizeof(LV_COLUMN)); i++)
      {
        ListView_InsertColumn(hControl, i, &lpLVColumn[i]);
      }
    }
  }

  return hControl;
}



/* ========================================================================== */
/*          FILE        Message.cpp                                           */
/*          CREATE      2007/03/11                                            */
/*          MODIFY      ----/--/--                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>

#include "Common.h"
#include "Resource.h"


/* -------------------------------------------------------------------------- */
/*                      グローバル(EXTERN)関数宣言部                          */
/* -------------------------------------------------------------------------- */
extern void OnCreate(HWND);

/* -------------------------------------------------------------------------- */
/*                      コントロール(STATIC)宣言部                            */
/* -------------------------------------------------------------------------- */
static HWND         hCtlCBOClient;
static HWND         hCtlLVWClient;


/* -------------------------------------------------------------------------- */
/*          PROCEDURE   OnCreate                                              */
/*          PARAM       HWND        hWnd          : ウインドウハンドル        */
/*          REMARK                                                            */
/* -------------------------------------------------------------------------- */
void OnCreate(HWND hWnd)
{
  /* 変数宣言部 */
  INITCOMMONCONTROLSEX  ICC;
  LV_COLUMN         LVColumn[] =
                    {
                      {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT,  150, "名前",     0, 0},
                      {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_RIGHT,  75, "サイズ",   0, 1},
                      {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT,  110, "更新日時", 0, 2},
                    };


  /* 関数本体部 */
  ICC.dwSize = sizeof(INITCOMMONCONTROLSEX);
  ICC.dwICC  = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
  InitCommonControlsEx(&ICC);

  /* ------------------------------------------------------------------------ */
  /* 配列全体のバイト数は、配列を宣言した関数に限ってsizeof演算子で求められる */
  /* ------------------------------------------------------------------------ */
  hCtlCBOClient =  WinCtlInit("COMBOBOX",  hWnd, NULL,     0);
  hCtlLVWClient =  WinCtlInit(WC_LISTVIEW, hWnd, LVColumn, sizeof(LVColumn));

  GetComboBoxItems(hCtlCBOClient);
  GetListViewItems(hCtlLVWClient);

  return;
}



/* ========================================================================== */
/*          FILE        Filelist.cpp                                          */
/*          CREATE      2007/03/11                                            */
/*          MODIFY      ----/--/--                                            */
/*          REMARK                                                            */
/*          COPYRIGHT   2000-2007, Latin. All Rights Reserved                 */
/* ========================================================================== */
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>

#include "Common.h"


/* -------------------------------------------------------------------------- */
/*                      グローバル(EXTERN)関数宣言部                          */
/* -------------------------------------------------------------------------- */
extern void GetComboBoxItems(HWND);
extern void GetListViewItems(HWND);

/* -------------------------------------------------------------------------- */
/*                      ローカル(STATIC)関数宣言部                            */
/* -------------------------------------------------------------------------- */
static int CALLBACK LVWCmpProc(LPARAM, LPARAM, LPARAM);


/* -------------------------------------------------------------------------- */
/*          PROCEDURE   GetComboBoxItems                                      */
/*          PARAM       HWND        hCtlCombo   : コンボボックスハンドル      */
/*          RETURN      void                                                  */
/*          REMARK      コンボボックスフォルダ情報取得                        */
/* -------------------------------------------------------------------------- */
void GetComboBoxItems(HWND hCtlCombo)
{
  /* 変数宣言部 */
  char              szCurDir[MAX_PATH + 1];
  char              szDrives[128];


  /* 関数本体部 */
  // カレントディレクトリ取得
  if (! GetCurrentDirectory(_MAX_DIR + 1, szCurDir))
  {
    MessageBox(NULL, "現在のディレクトリを取得できません", "ERR", MB_OK);
    return;
  }

  ComboBox_SetText(hCtlCombo, szCurDir);
  ComboBox_AddString(hCtlCombo, szCurDir);

  // ドライブ名取得
  GetLogicalDriveStrings(sizeof(szDrives), szDrives);

  for (int i = 0; szDrives[i] != '\0'; i++)
  {
    ComboBox_AddString(hCtlCombo, &szDrives[i]);

    // 次のドライブ名へ
    while (szDrives[i] != '\0')
    {
      i++;
    }
  }

  return;
}

/* -------------------------------------------------------------------------- */
/*          PROCEDURE   GetListViewItems                                      */
/*          PARAM       HWND        hCtlLView   : リストビュウハンドル        */
/*          RETURN                                                            */
/*          REMARK      リストビュウファイル情報取得                          */
/* -------------------------------------------------------------------------- */
void GetListViewItems(HWND hCtlLView)
{
  /* 変数宣言部 */
  WIN32_FIND_DATA   Win32FindData;
  HANDLE            hFind;
  LVITEM            LVItem;
  SYSTEMTIME        SystemTime;
  FILETIME          FileTime;


  /* 関数本体部 */
  if (INVALID_HANDLE_VALUE == (hFind = FindFirstFile("*.*", &Win32FindData)))
  {
    if (GetLastError() != ERROR_NO_MORE_FILES)
    {
      MessageBox(NULL, "ファイル一覧の取得に失敗しました", "ERR", MB_OK);
      return;
    }
  }

  do
  {
    // ルートディレクトリ・カレントディレクトリは無視
    if (lstrcmp(Win32FindData.cFileName, "." ) == 0 ||
        lstrcmp(Win32FindData.cFileName, "..") == 0)
    {
      continue;
    }

    // 1項目(名前)
    LVItem.mask     = LVIF_TEXT;
    LVItem.iItem    = 0;
    LVItem.iSubItem = 0;
    LVItem.pszText  = Win32FindData.cFileName;
    ListView_InsertItem(hCtlLView, &LVItem);

    // 2項目(サイズ)
    LVItem.iSubItem = 1;
    if (Win32FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      wsprintf(LVItem.pszText, "%s", "<DIR>");
    }
    else
    {
      wsprintf(LVItem.pszText, "%d", Win32FindData.nFileSizeHigh * MAXDWORD + Win32FindData.nFileSizeLow);
    }
    ListView_SetItem(hCtlLView, &LVItem);

    // 3項目(更新日時)
    LVItem.iSubItem = 2;
    // 協定世界時(UTC)を地域標準時に変更
    FileTimeToLocalFileTime(&Win32FindData.ftLastWriteTime, &FileTime);
    FileTimeToSystemTime(&FileTime, &SystemTime);
    wsprintf(LVItem.pszText, "%d/%02d/%02d %d:%02d", SystemTime.wYear, SystemTime.wMonth,
                                                        SystemTime.wDay,  SystemTime.wHour,  SystemTime.wMinute);
    ListView_SetItem(hCtlLView, &LVItem);
  }
  while (FindNextFile(hFind, &Win32FindData));

  FindClose(hFind);

  // ソート
  ListView_SortItemsEx(hCtlLView, LVWCmpProc, hCtlLView);

  return;
}

/* -------------------------------------------------------------------------- */
/*          CALLBACK    LVWCmpProc                                            */
/*          PARAM       LPARAM      lParam1     : 比較される 2 つのアイテム1 */
/*                      LPARAM      lParam2     : 比較される 2 つのアイテム2 */
/*                      LPARAM      lParamSort  : アプリケーション定義(任意)*/
/*          RETURN      -1 : lParam1 のアイテムが lParam2 のアイテムより前    */
/*                       0 : lParam1 のアイテムが lParam2 のアイテムと等しい  */
/*                       1 : lParam1 のアイテムが lParam2 のアイテムより後    */
/*                      lParamSortにはポインタ指定で出力先の指定が可能        */
/* -------------------------------------------------------------------------- */
int CALLBACK LVWCmpProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
  /* 変数宣言部 */
  HWND              hCtlLView;
  char              szItemText1[_MAX_PATH];
  char              szItemText2[_MAX_PATH];
  char              szAttribute1[256];
  char              szAttribute2[256];


  /* 関数本体部 */
  hCtlLView = (HWND) lParamSort;

  // パラム1情報取得
  ListView_GetItemText(hCtlLView, lParam1, 0, szItemText1,  sizeof(szItemText1));
  ListView_GetItemText(hCtlLView, lParam1, 1, szAttribute1, sizeof(szAttribute1));

  // パラム2情報取得
  ListView_GetItemText(hCtlLView, lParam2, 0, szItemText2,  sizeof(szItemText2));
  ListView_GetItemText(hCtlLView, lParam2, 1, szAttribute2, sizeof(szAttribute2));

  if (lstrcmp(szAttribute1, "<DIR>") == 0 &&
      lstrcmp(szAttribute2, "<DIR>") == 0)
  {
    return lstrcmpi(szItemText1, szItemText2);
  }
  else if (lstrcmpi(szAttribute1, "<DIR>") == 0)
  {
    return -1;
  }
  else if (lstrcmpi(szAttribute2, "<DIR>") == 0)
  {
    return 1;
  }
  else
  {
    return lstrcmpi(szItemText1, szItemText2);
  }
}


3.static と extern

よくよく見ると、今までとは変数の位置が変わっているところがあります。
実は、こっそりとですが、Message.cpp の コントロールのハンドルを格納する変数が、OnCreate() 関数の中から、外に出ていています。
さらには、なにやら static という指定子がついています。
通常、関数の外で変数を宣言すると、その変数は、グローバル変数になります。
これは、先ほどやったヘッダファイルで変数宣言したことと、まったく同じ意味になります。
しかし、static 指定子をつけると、スコープが宣言したファイル内に限られるようになるのです。
※ static は、英語的には「静的」という意味ですが、なんのこっちゃわかりません。
ここでの意味は、一度確保すると「同一メモリ上にあり続ける(動かない = 静的)」という様に理解するとよいでしょう。
ちなみに普通の変数は、自動変数といったりします。

そもそも、今回は、OnCreate() 関数の中でも、外でも動作にまったく影響ありませんが、次回以降に問題があるのでここで先んじてやりました。
その場面が出てきたときにまた触れることにしましょう。

static 指定子が関数のプロトタイプ宣言についているものがあります。
Filelist.cpp() ファイルにある LVWCmpProc() 関数です。
実をいうと関数は、static 指定子をつけてもつけなくても、デフォルトで宣言したファイル内にスコープが限られます。
つまり、つけてもつけなくてもいいことになりますが、ファイル内でしか参照されないということを明示するためにつけました。

extern 指定子が関数のプロトタイプ宣言についているものがあります。
ヘッダファイルにプロトタイプ宣言した関数を、定義があるファイルでもう一度、プロトタイプ宣言しています。
extern 指定子は、「本物はどこかで定義している」という意味しかありません。
ここでは、グローバルの関数のファイルでの再宣言に extern を指定しています。
※ もちろん、再宣言しなくてもかまいませんし、extern を指定しなくてもかまいません。

今回も、機能的には新しいことはありませんが、今後のプログラム開発には必要なことをやりました。

本日のソース: FTPManager109(VC8).zip   FTPManager109(BCC).zip


※小言

ファイルや関数を分割していくと、視認性は上がりますが、一つの処理の中で、いろんなところに処理が飛んで行って
プログラム全体の流れが、つかみにくくなることがあります。
このため、何かしら、関数の連携関係を示したものを作っておく必要があります。
一応、エクセルにまとめたものを、本日のソースの中に入れておきました。各関数には、リンクが張ってあります。

全体の流れが読みづらくなったときには、一度自分で書いてみてください。
世の中にはもっと良い関数の関連図があると思いますので、自分が理解しやすい関数関連図を作成してみてください。


Last Update 2014/02/06 22:43
[Index] [Prev] [Next]