i-focus i-focus BB
    • カテゴリ
    • 最近
    • タグ
    • 人気
    • ユーザー
    • グループ
    • 登録
    • ログイン

    実開発に使われるC言語 [基礎から裏技まで] (2) Why C?? コンパイラの力

    C言語
    c言語 windows api gcc
    1
    1
    227
    もっと見る
    • 古いものから新しい順
    • 新しいものから古い順
    • 最高評価
    返信
    • スレッドとして返信する
    投稿するのにログインして下さい
    このスレッドが削除されました。スレッド管理権を持っているユーザーにしか読めません。
    • Z
      zhengyu.chen
      最後に編集した時間

      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). ほかの諸々

      1 件の返信 最後の返信 返信 引用 1
      • First post
        Last post