i-focus i-focus BB
    • カテゴリ
    • 最近
    • タグ
    • 人気
    • ユーザー
    • グループ
    • 登録
    • ログイン
    1. ホーム
    2. zhengyu.chen
    3. ベスト
    Z
    • プロフィール
    • フォロー中 0
    • フォロワー 0
    • スレッド 6
    • 投稿 7
    • ベスト 4
    • Controversial 0
    • グループ 0

    zhengyu.chenのベストな投稿

    • 実開発に使われるC言語 [基礎から裏技まで] (1)

      (1)
      /**

      • ここに書いてある内容は、
      • すべて個人的な開発経験に基づいたもので、
      • ときには間違いが免れないと思います。
      • ご指摘、ご意見を是非お願い致します。
        */

      <1>. Cの標準
      C言語といえば、まず標準というものが存在している。僕の大学時代(2000年初頭)に教わったC言語はC89で、その後、実際に日本で仕事する際、ほとんどC99を使ってました。

      C89とC99の違いをいくつかありますが、下記に自分がよくあうものを挙げてみました。
      ・コメント表記の // を導入したのはC99で、C89では使ってはいけません。
      ・C99に新しいDataタイプを導入した:例えばbool (#include <stdbool.h>, true, false)
      ・C89には、変数宣言をブロックの最初の部分にしか書けないが、C99はどうなのか確認していませんけど、いい習慣として、やはりC89の規定に従ったほうがいいと思います。

      どの標準でCのソースをコンパイルするかは、コンパイラのコンパイル命令で指定できます。
      例えばコンパイラがgccの場合
      【例】gcc xxx --std=c99 あるいは gcc xxx --std=c89で指定できます。

      <2>. コンパイラはそこまで賢くない。
      例えば、今2つの.hファイルa.hとb.hがあるとして、各自に下記のソースコードを書いております:

      // a.h
      #include "b.h"
      
      // b.h
      #include "a.h"
      

      今main.cというファイルはこの2つのheaderを利用したい場合、下記のように書いています:

      // main.c
      #include "a.h"
      #include "b.h"
      

      これでmain.cをコンパイルすると、どうなるでしょうか?

      a.hとb.hをお互いにincludeしてて、コンパイラはa.hとb.hを繰り返してincludeし、無限ループになってしまいます。includeが重複しているのをわかって、自動的にincludeを停止するとか、コンパイラはそこまで賢くない

      こういった問題を避けるためには、Cのheaderファイルを書くとき、大体下記のような定番的な書き方をします:

      #ifndef A_H
      #define A_H
      
      
      #include "b.h"
      
      #if defined (__cplusplus)
      extern "C" {
      #endif
      
      // TODO:structやenum、関数などの宣言はここから書く
      
      #if defined (__cplusplus)
      }
      #endif
      
      
      #endif  /* A_H */
      

      ここでマクロの#ifndef A_H ... #endifで囲んで、上記の繰り返しinclude問題を解決できます。

      visual studio C++を使ったことのある方は、もしかしたら下記のような書き方を見たことがあるかもしれません:

      #pragma once
      
      // TODO:structやenum、関数などの宣言はここから書く
      

      ここの#pragma onceも上記と同じ意味で繰り返しinclude問題に対応していますが、あくまでコンパイラに依存しているマクロ命令なので、
      標準的でどこでもどの環境でもコンパイルできるようなCコードを書きたい場合、あまりお勧めしません。

      <3>. CとC++
      CとC++の混合的プログラミングの話になると、きりがないので、ここでまず一番簡単なextern "C" {xxx}について説明致します。
      多くの方は、もしかしたらCとC++を同じように見る傾向があるかもしれませんが、実は別々の言語で見たほうが、のちに変なコンパイルエラーが少なくで済むかもしれません。
      原因の一つとして、C++のコンパイラを使ってコンパイルする場合、すべての関数や構造体に対して、付加情報をその名前に追加して、新しい名前にしているのです。そうなると、のちのリンク段階では、元の名前を求めるC言語のソースコードと、名前が変わった関数や構造体との整合性が失われてしまって、使いたい関数や構造体が見つからないというコンパイルエラーが出てしまいます。

      そこで、ここのextern "C" { xxx }の登場になります。その意味は、「extern "C" { xxx }の括弧に囲まれたソースコードの部分を、付加情報などせずに元の名前のままにしてください」と、C++のコンパイラに教えています。

      もしC++のコンパイラを使ってコンパイルする場合、必ず<2>の書き方で書いてください。とにかく無難ですので

      C言語に投稿されました c言語の基礎 c++ macro header
      Z
      zhengyu.chen
    • 実開発に使われるC言語 [基礎から裏技まで]

      実開発に使われるC言語 [基礎から裏技まで]
      基礎ソフト開発のためのCを盛り上げよう!

      技術ブログに投稿されました
      Z
      zhengyu.chen
    • 実開発に使われるC言語 [基礎から裏技まで] (2) Why C?? コンパイラの力

      2回目のスレッドですが、今回はみんなさんにCを学ぶ積極性を少しでも上げたいのと、IDEツール依存症から少しでも解放させたいです。

      <1>. 今の時代に、どうしてCを学ぶ/使う必要があるのか?

      それは、i-focusのロゴの「技術で世界を変える」想いを実現するためだと、私は考えています。「誰でもできるもののみをいくら作っても、結局何も変えられない」、と私は思います。「世界を変える」ためには、やはりGoogleなどの最強の会社と比べないとなりませんから。

      これらの会社は何を作っているのかを見てみると、開発ツールやフレームワーク、簡単なスクリプト・プログラミング言語、そして各種の開発標準を作っています。要するに、基礎的な部分をやっているのです。彼らはCなどの基礎言語を使って、簡単な開発ツールを作って、ほかの応用開発会社に売ったり、この業界を牽引して膨大な資金をもらって、まさに世界を変えているところです。基礎言語を牛耳るのは会社にとって如何に大事かがわかりましょう。

      そして、個人の技術者についてはどうでしょうか?残酷ながらプログラマには年齢が大敵です。簡単な言語ほど、できる人が多くて、年を取ると競争に負けやすくなって、結局管理職など少ない職位に競うしかなくなります。ですが、Cは難しくて (ほとんどの実装は自分で0からする必要がある) そもそも完全に深くまで身につけるものが少なくて、年を取ると経験は積み重ねてより価値が高くなっていくものです。「難しい技術ほど、年を取るとより価値は高くなる」。なので、個人の技術者の発展としてもいいことしかないと私は思います。

      <2>. C言語は、無機質なコンソールプログラムしか作れないでしょうか?

      今のGUIプログラムの時代に、Cのような無機質なコンソールプログラムを作って何の意味があるかと思うかもしれませんが、実は、CもGUIプログラムを作れるし、しかも何のIDEを要りません。

      下記の部分的なプログラムは、昔、僕の作ったWindowsのプログラムで、IDE一切使わずに (Visual Studioなど要りません)、Cの定番なコンパイラであるGCCだけで、生成できます。 (ここで必要なResourceファイルを張り付けていません、全ソースが見たい方がいらっしゃいましたら、私に言ってください)

      test_main.c

      #include <windows.h>
      #include <gdiplus.h>
      //#include <gdiplusimaging.h>
      #include "resource.h"
      
      #pragma comment(lib, "gdiplus.lib")
      
      Gdiplus::GdiplusStartupInput    gdiSI;
      ULONG_PTR                       gdiToken;
      HWND                            hWnd;
      int                             state;
      
      static LRESULT CALLBACK windProc(HWND, UINT, WPARAM, LPARAM);
      
      extern "C" int WINAPI WinMain(  HINSTANCE   hCurInst,
                                      HINSTANCE   hPrevInst,
                                      PSTR        pCmdLine,
                                      int         nCmdShow    )
      {
          BOOL            ret;
          MSG             msg;
          WNDCLASSEX      wndClass;
          TCHAR           szClassName[]   = TEXT("Preprocess Demo");
      
          Gdiplus::GdiplusStartup(&gdiToken, &gdiSI, nullptr);
      
          wndClass.cbSize         = sizeof(WNDCLASSEX);
          wndClass.style          = CS_HREDRAW | CS_VREDRAW;
          wndClass.lpfnWndProc    = windProc;
          wndClass.cbClsExtra     = 0;
          wndClass.cbWndExtra     = 0;
          wndClass.hInstance      = hCurInst;
          wndClass.hIcon          = (HICON)::LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
          wndClass.hIconSm        = (HICON)::LoadImage(nullptr, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
          wndClass.hCursor        = (HCURSOR)::LoadImage(nullptr, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
          wndClass.hbrBackground  = (HBRUSH)::GetStockObject(WHITE_BRUSH);
          wndClass.lpszMenuName   = TEXT(MAKEINTRESOURCE(DEMO_MENU)); //TEXT("DEMO_MENU");
          wndClass.lpszClassName  = szClassName;
      
          if (!::RegisterClassEx(&wndClass)) {
              return FALSE;
          }
      
          hWnd = ::CreateWindow(  szClassName,
                                  szClassName,
                                  WS_OVERLAPPEDWINDOW,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  nullptr, nullptr, hCurInst, nullptr );
          if (!hWnd) {
              return FALSE;
          }
      
          ::ShowWindow(hWnd, nCmdShow);
          ::UpdateWindow(hWnd);
      
          while ((ret = ::GetMessage(&msg, nullptr, 0, 0)) != 0) {
              if (ret == -1) break;
              else {
                  ::TranslateMessage(&msg);
                  ::DispatchMessage(&msg);
              }
          }
      
          Gdiplus::GdiplusShutdown(gdiToken);
      
          return (int)msg.wParam;
      }
      
      
      static
      LRESULT
      CALLBACK
      windProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
      {
          const TCHAR             *szFileName = TEXT("image.jpg");
          static Gdiplus::Bitmap   *pImage     = nullptr;
          static Gdiplus::Bitmap   *pImage2    = nullptr;
          static CLSID            id;
          static UINT             num         = 0;
          static UINT             size        = 0;
          static Gdiplus::Rect             mem_rect;
          static Gdiplus::BitmapData      bData;
          static BYTE             *pSrcData   = nullptr;
          static BYTE             *pDstData   = nullptr;
          static UINT             w, h, stride, dstStride, j;
          int                     x, y;
          //static Gdiplus::ImageCodecInfo      *pImageCodecInfo    = nullptr;
          PAINTSTRUCT             ps;
          HDC                     hdc;
          static RECT    cRect;
      
          switch (msg) {
              case WM_CREATE:
                  {
                      Gdiplus::ImageCodecInfo      *pImageCodecInfo    = nullptr;
      #ifndef UNICODE
                      WCHAR       szFileNameW[MAX_PATH];
      
                      ::MultiByteToWideChar(932, 0, szFileName, -1, szFileNameW, sizeof(szFileNameW) / sizeof(TCHAR));
                      pImage      = Gdiplus::Bitmap::FromFile(szFileNameW);
      #else
                      pImage      = Gdiplus::Bitmap::FromFile(szFileName);
      #endif
                      //pImage2     = pImage->Clone();
      
                      h               = pImage->GetHeight();
                      w               = pImage->GetWidth();
      
                      //stride      = ((w + 3) / 4) * 4;
      //                const Gdiplus::Rect mRect = Gdiplus::Rect(0, 0, w, h);
      /*
                      mem_rect    = 0;
                      mem_rect.right  = w;
                      mem_rect.top    = 0;
                      mem_rect.bottom = h;
      */
      /*
                      Gdiplus::GetImageEncodersSize(&num, &size);
                      pImageCodecInfo = new Gdiplus::ImageCodecInfo[size];
                      Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
      
                      for (j = 0; j < num; ++j) {
                          if (wcscmp(L"image/jpeg", pImageCodecInfo[j].MimeType) == 0) {
                              id  = pImageCodecInfo[j].Clsid;
                              break;
                          }
                      }
                      delete [] pImageCodecInfo;
      */
      
      //                pSrcData            = (BYTE*)malloc(stride * h * sizeof(BYTE));
      /*
                      bData.Width         = w;
                      bData.Height        = h;
                      bData.Stride        = stride;
                      bData.PixelFormat   = PixelFormat24bppRGB;
                      bData.Scan0         = (void*)pSrcData;
      */
      /*
                      Gdiplus::Status st  = pImage->LockBits(&mRect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bData);
                          {
                                      TCHAR szText[100] = {0};
                                      ::wsprintf(szText, "status=%d", st);
                                      ::MessageBox(NULL, szText, TEXT("Print Value"), MB_OK);
                          }
      
                      pSrcData            = (BYTE*)(bData.Scan0);
      */
                      stride              = ((w * 3 + 3) / 4) * 4;
                      //dstStride           = ((w + 3) / 4) * 4;
                      dstStride           = stride;
                      pDstData            = (BYTE*)malloc(dstStride * h * sizeof(BYTE));
                      state               = 0;
                      // Get Bits
                  }
                  break;
      
              case WM_LBUTTONDOWN:
                  break;
      
              case WM_PAINT:
                  {
                      hdc         = ::BeginPaint(hWnd, &ps);
                      ::GetClientRect(hWnd, &cRect);
                      Gdiplus::Graphics   myGraphics(hdc);
                      myGraphics.DrawImage(pImage, 0, 0, (cRect.right - cRect.left) / 2 - 10, (cRect.bottom - cRect.top));
      
                      if (pImage2) {
                          myGraphics.DrawImage(pImage2, (cRect.right - cRect.left) / 2 + 10, 0, (cRect.right - cRect.left) / 2 - 10, (cRect.bottom - cRect.top));
                      }
                      myGraphics.~Graphics();
      
      
                      ::EndPaint(hWnd, &ps);
                  }
                  break;
      
              case WM_COMMAND:
                  switch (LOWORD(wp)) {
                      case IDM_EXIT:
                          ::SendMessage(hWnd, WM_CLOSE, 0, 0);
                          break;
      
                      case IDM_FILEOPEN:
                          break;
      
                      case IDM_GRAY:
                          if (state == 0) {
      /*
                          {
                                      TCHAR szText[100] = {0};
                                      ::wsprintf(szText, "real format=%d, rgb=%d, argb=%d", pImage->GetPixelFormat(), PixelFormat24bppRGB, PixelFormat32bppARGB);
                                      ::MessageBox(NULL, szText, TEXT("Print Value"), MB_OK);
                          }
      */
      
                      ::GetClientRect(hWnd, &cRect);
                      cRect.left  = (cRect.right - cRect.left) / 2 + 10;
      
                              const Gdiplus::Rect mRect = Gdiplus::Rect(0, 0, w, h);
                              Gdiplus::Status st  = pImage->LockBits(&mRect, Gdiplus::ImageLockModeRead, pImage->GetPixelFormat(), &bData);
                              pSrcData            = (BYTE*)(bData.Scan0);
                              for (int y = 0; y < bData.Height; ++y) {
                                  for (int x = 0; x < bData.Width; ++x) {
                                      int src_index   = (x * 3) + (y * bData.Stride);
                                      //int dst_index   = x + y * dstStride;
                                      int tmp         = (int)(pSrcData[src_index+0] * 0.114 + pSrcData[src_index+1] * 0.587 + pSrcData[src_index+2] * 0.299 + 0.5);
                                      tmp             = (tmp < 0) ? 0 : ((tmp > 255) ? 255 : tmp);
                                      //pDstData[dst_index] = tmp;
                                      pDstData[src_index+2] = pDstData[src_index+1] = pDstData[src_index+0] = tmp;
      /*
                                  if (x == 100 && y == 100) {
                                      TCHAR szText[100] = {0};
                                      ::wsprintf(szText, "GRAY = %3d, R=%d, G=%d, B=%d", pDstData[dst_index], pSrcData[src_index+2], pSrcData[src_index+1], pSrcData[src_index]);
                                      ::MessageBox(NULL, szText, TEXT("Print Value"), MB_OK);
                                  }
      */
                                  }
                              }
      
                          {
                              Gdiplus::Bitmap image(w, h, dstStride, PixelFormat24bppRGB, (BYTE*)pDstData);
      //                        hdc         = ::BeginPaint(hWnd, &ps);
                              pImage2     = image.Clone();
                              image.~Image();
      /*
                              Gdiplus::Graphics   myGraphics(hdc);
                              myGraphics.DrawImage(pImage2, (ps.rcPaint.right - ps.rcPaint.left) / 2 + 10, 0, (ps.rcPaint.right - ps.rcPaint.left) / 2 - 10, (ps.rcPaint.bottom - ps.rcPaint.top));
                              myGraphics.~Graphics();
      
                              ::EndPaint(hWnd, &ps);
      */
                          }
                              pImage->UnlockBits(&bData);
                              state = 1;
                              ::InvalidateRect(hWnd, &cRect, TRUE);
                              //::InvalidateRect(hWnd, nullptr, TRUE);
                              ::UpdateWindow(hWnd);
                          }
                              break;
      /*
                      case IDM_CUT_RECTANGLE:
                          break;
      */
                  }
                  break;
      
              case WM_DESTROY:
                  if (pImage) {
      
                      pImage->~Image();
                  }
                  if (pImage2) pImage2->~Image();
                  //if (pSrcData) free(pSrcData);
                  if (pDstData) free(pDstData);
                  
                  ::PostQuitMessage(0);
                  break;
      
              default:
                  return (::DefWindowProc(hWnd, msg, wp, lp));
          }
          return 0;
      }
      

      ・必要な環境:gcc 【code blocksを検索して、mingwコンパイル環境(windows)をインストールする必要がある。】
      ・コンパイルコマンド:

      $ windres.exe -i resource.rc -o resource.o    // resourceを生成する
      $ g++ -o test_main -O3 ./*.c resource.o -lgdi32 -lgdiplus -mwindows
      

      これで、下記のようなWindowsプログラムができた:
      demo_graph.jpg

      これが、CとC compilerの力です。何の特定なIDEも依存せずに、メモ帳でCのソースを書いて、コマンドラインでコンパイル命令を打っただけで、こういったプログラムができました。このことで、1つのCプログラムでMulti PlatformにコンパイルしてMulti Platform化することが可能になります。(所謂JavaのMulti Platform機能)

      <3>. Cは本当にJavaと同じくらいの速さなのでしょうか?

      CはJavaとの実行効率はほとんど大した差はないと、たくさんの人が言っていますが、ここには一つの前提条件があります。すなわち、JavaはJVMで常に最適化された状態であるが、Cの最適化はプログラマ次第です。なので、みんなよく比較している両者は:常に最適化されたJavaと、全然最適化されてないCです。この両者はほとんど差がないってことになります。
      例えば、上記のGCCのコンパイル命令では-O3を書いておりますが、これはGCCに最適化するよう命令したものです。これを指定しないと、プログラムは最適されません。また、各CPUには、SIMDコマンドがありますけど、もしプログラムにはSIMDで書いてないと、SIMDも使わないですし、pthreadライブラリを使ってMultiThread化しないと、MultiThread化もしません。これらの最適化する技を使うと、百倍千倍の速さになります。僕は実際にとあるpythonのプロジェクト開発中で、Cython + Cを使って (PythonからCのものを呼び出す技)、単純のPython版より10000倍以上(当時は約3万倍以上)の速さを引き出した経験があります。

      <4>. 総括
      上記の紹介を通して、Cはとっても魅力的な言語だとは思いませんか?
      無論、僕もまだまだ勉強中でCに関してはわからないものもたくさんありますので、みんなさんと一緒にCの魅力を掘り出すことができれば幸いです。

      今後の内容に関してですが、大体は下記のような内容でお話を進めていきたいです:
      (1). Cのオブジェクト指向 (C的面向对象) の書き方
      (2). CのMulti Thread化 (pthreadの使い方)
      (3). CのSIMDプログラミング (Intel / ArmのCPUを例に)
      【(2) + (3) + compilerの最適化の3者同時最適化でどれほど早くなれるかを期待しよう。原付車をフェラーリ車に改造!】
      (4). CとJavaの交互 (JNI層の書き方)
      (5). CとPythonの交互 (Cythonの書き方)
      (6). CとSwift/Objective-Cの交互
      (7). Cによるカメラ(Sensor)の直接操作の仕方【組込み】
      (8). CのNetworkプログラミング
      (9). Windows CのAPIの基本
      (10). ほかの諸々

      C言語に投稿されました c言語 windows api gcc
      Z
      zhengyu.chen
    • 実開発に使われるC言語 [基礎から裏技まで] (4) Object-Oriented C その2 Privateの実現

      前回の話の続きになりますけど、今回は、クラスでPrivate領域はCでどうやって実現するのかについて説明させていただきます。
      ここでまず、C++の仮ソースを書いておきます。

      class TestCpp {
      public:
          int input_val;
          void printVal();
      private:
          int count;
          int calc_value();
      };
      
      int
      TestCpp::calc_value()
      {
          int result = this->input_val + this->count;
          this->count++;
          return result;
      }
      
      void
      TestCpp::printVal()
      {
          std::cout << "Value is " << this->calc_value() << std::endl;
      }
      
      int main(int argc, char *argv[]) {
          TestCpp *obj = new TestCpp();
          
          obj->input_val = 1;
          for (int i = 0; i < 10; ++i) {
              obj->printVal();
          }
          
          delete obj;
      }
      

      ここで、privateの領域のものは、クラスの外部から直接にアクセスすることができないが、Cの場合、structを利用しているので、なかのものすべては直接に外部からアクセスすることができてしまいます。では、privateの領域をCでどう実現するかを今から説明します。

      直球ですが、下記のように前回のClassAのソースコードを改修する:
      ※ 今回に重要な部分を強調するため、直接に関係のない内容を...で省略させていただきます。

      // ifcs_ooc_class_a.h ClassAの定義、void *p_privateでprivateの部分を隠す
      typedef struct IfcsClassA_tag {
      // public:
          int input_val;
          void printVal(struct IfcsClassA_tag* const);
      
      // private:
          void *p_private;
      } IfcsClassA;
      
      // ifcs_ooc_class_a.c の実装、なかでprivateの部分を定義する
      #include "ifcs_ooc_class_a.h"
      
      // <1>. Privateの内容を1つのStruct構造体で格納して、実装を書く
      typedef struct IfcsClassAPrivate_tag {
          int count;
          void calc_value(struct IfcsClassAPrivate_tag* const, int const);
      } IfcsClassAPrivate;
      
      static int calc_value(IfcsClassAPrivate* const p_private, int const origin) {
          if (NULL != p_private) {
              int result = p_private->count + origin;
              p_private->count++;
              return result;
          } else {
              return origin;
          }
      }
      
      static IfcsClassAPrivate* IfcsClassAPrivate_create() {
          IfcsClassAPrivate *p_private = NULL;
          
          p_private = (IfcsClassAPrivate*)malloc(sizeof(IfcsClassAPrivate));
          p_private->calc_value = calc_value;
          p_private->count      = 0;
          ...
          return p_private;
          ...
      }
      ...
      // Privateの部分はここまで
      
      // ここからは、ClassAのpublicの部分の内容です。
      static void printVal(IfcsClassA * const self);
      
      IfcsClassA*
      IfcsClassA_create() {
          ...
          p_obj_a->p_private = IfcsClassAPrivate_create();
          ...
          p_obj_a->printVal = printVal;
          ...
      }
      
      void
      IfcsClassA_destroy(
          IfcsClassA ** const self) {
          ...
          IfcsClassAPrivate_destroy(&p_obj_a->p_private);
          ...
      }
      
      static void IfcsClassA_initialize(IfcsClassA * const self) {
          ...
          self->input_val = 0;
          ...
      }
      
      static void printVal(
          IfcsClassA * const self)
      {
          IfcsClassAPrivate *p_private = NULL;
          int result = -1;
      
          if (NULL != self) {
              result = self->input_val;
              if (NULL != self->p_private) {
                  // Cのポインター変換で、private領域を内部でアクセスする
                  p_private = (IfcsClassAPrivate*)self->p_private;
                  result = p_private->calc_value(p_private, result);
              }
          }
      
          printf("class a value is %d\n", result);
      }
      

      これで、main関数で下記のように書ける:

      int main(int argc, char *argv[]) {
          int i = 0;
          IfcsClassA *p_obj_a = IfcsClassA_create();
          
          p_obj_a->input_val = 1;
          for (; i < 10; ++i) {
              p_obj_a->printVal(p_obj_a);
          }
          
          p_obj_a->finalize(p_obj_a);
          IfcsClassA_destroy(&p_obj_a);
      }
      

      まとめ:private領域を実現するというのは、要するに、privateの領域のものを外部から「直接にアクセス」させないことです。Cの場合は、struct内のものはどうしても外部からアクセスされるのですが、void *ポインタを利用して、
      privateの実現部分を.cで全部定義して、あとはポインタの変換機能を利用すれば、外部からのアクセス禁止は実現できるようになります。

      ちなみに、Cの場合、お客さんにライブラリを渡すときに必要なのは、コンパイルされた.soのライブラリファイル + それを利用するための.hファイルのみで、.cの内部実現はお客さんには完全に見れないので、肝心なアルゴリズムや実装内容を保護する観点からでも、こういった実装もいいかと思います。

      また、C++のクラス定義に比べると、隠蔽性はもっと強いかなと思ったりもしますね。 (「void*」 VS 「Privateの明確的なDeclaration」)

      次回は、Protectedの実現について説明したいですが、その前に、皆さんは自分でその実装に関して考えてみてください。

      C言語に投稿されました
      Z
      zhengyu.chen