<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cross-Platform on kenji.blog</title><link>http://kenji.blog/zh-tw/categories/cross-platform/</link><description>Recent content in Cross-Platform on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-tw</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 08:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-tw/categories/cross-platform/index.xml" rel="self" type="application/rss+xml"/><item><title>在 Mac 與 Windows 跨平台開發中應注意的事項</title><link>http://kenji.blog/zh-tw/p/cross-platform-development-mac-windows/</link><pubDate>Sun, 13 Sep 2026 08:00:00 +0900</pubDate><guid>http://kenji.blog/zh-tw/p/cross-platform-development-mac-windows/</guid><description>&lt;img src="http://kenji.blog/p/cross-platform-development-mac-windows/img/eyecatch.jpg" alt="Featured image of post 在 Mac 與 Windows 跨平台開發中應注意的事項" />&lt;p>跨越 Mac（macOS）與 Windows，甚至 Linux（包含 WSL）等多個作業系統（OS）的跨平台開發，是現代軟體工程中不可避免的課題。在建構網頁開發、行動應用程式後端，或跨平台桌面應用程式（如 Electron、Tauri、Qt 等）時，如果團隊內使用不同的 OS，就會遭遇許多「起因於 OS 差異的 Bug」。&lt;/p>
&lt;p>各個 OS 擁有不同的歷史背景與設計理念。Windows 擁有源自 MS-DOS 的獨特架構（Win32 API、NT 核心）；macOS 則以 UNIX（基於 FreeBSD 的 Darwin）為基礎；而 Linux 則遵循 POSIX 標準。這個根本上的差異，在檔案系統、網路、程序處理等各種場合，都會產生讓開發者困擾的「陷阱」。&lt;/p>
&lt;p>本篇文章將針對混合使用 Mac 與 Windows 的開發團隊，或是以雙 OS 為目標的應用程式開發，極為詳細且實用地解說絕對必須知道的技術差異與最佳實踐。&lt;/p>
&lt;hr>
&lt;h2 id="1-換行字元的陷阱-crlf-vs-lf-與-git-的嚴格設定">1. 換行字元的陷阱 (CRLF vs LF) 與 Git 的嚴格設定
&lt;/h2>&lt;p>最頻繁發生且容易讓團隊開發陷入混亂的原因之一，就是「換行字元（Line Endings）」的問題。這是可以追溯至打字機時代的歷史問題。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Windows&lt;/strong>：使用歸位字元（Carriage Return, CR, &lt;code>\r&lt;/code>, &lt;code>0x0D&lt;/code>）與換行字元（Line Feed, LF, &lt;code>\n&lt;/code>, &lt;code>0x0A&lt;/code>）組合而成的 &lt;strong>CRLF&lt;/strong> 作為標準換行字元。&lt;/li>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>：僅使用單一換行字元 &lt;strong>LF&lt;/strong> 作為標準換行字元。（※早期的 Mac OS 9 之前僅使用 CR，但 Mac OS X 之後因為變成基於 UNIX 系統，所以改為 LF）&lt;/li>
&lt;/ul>
&lt;p>因為這個差異，在 Git 儲存庫內共享原始碼時，差異（diff）可能會擴及整個檔案；或者原本預期在 Linux 環境執行的 Shell Script（&lt;code>.sh&lt;/code>），因為在 Windows 上被編輯而變成 CRLF，導致執行時 &lt;code>\r&lt;/code> 被視為無效字元，引發如 &lt;code>\r: command not found&lt;/code> 之類的錯誤。&lt;/p>
&lt;h3 id="git-中的解決方案利用-gitattributes-進行管理">Git 中的解決方案：利用 &lt;code>.gitattributes&lt;/code> 進行管理
&lt;/h3>&lt;p>雖然 Git 有一個名為 &lt;code>core.autocrlf&lt;/code> 的設定，但依賴它是危險的。因為這會依賴開發者個人本機機器的全域設定，當新成員加入團隊時，很容易因為忘記設定而引發問題。&lt;/p>
&lt;p>最佳實踐是在儲存庫的根目錄配置 &lt;code>.gitattributes&lt;/code> 檔案，在儲存庫層級明確定義換行字元的處理方式。這樣一來，無論在什麼環境下被 Clone，都能保證一致的行為。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl"># 預設視為文字檔，並在儲存庫內（Git 的資料庫中）正規化為 LF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Checkout 時會被轉換為各 OS 的標準換行字元
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">* text=auto
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 不過，原始碼等特定副檔名，無論在哪個 OS 都一律強制使用 LF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.sh text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.py text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.cpp text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.hpp text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.js text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.json text eol=lf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># Windows 專用的批次檔等強制使用 CRLF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.cmd text eol=crlf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.bat text eol=crlf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># 圖片或已編譯的二進位檔等檔案不進行換行字元轉換（防止檔案損壞）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.png binary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.jpg binary
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">*.pdf binary
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="2-檔案系統的大小寫區分-case-sensitivity">2. 檔案系統的大小寫區分 (Case Sensitivity)
&lt;/h2>&lt;p>檔案系統中是否區分大小寫（Case Sensitivity），也是跨平台開發中最大的難關之一。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>macOS (APFS / HFS+)&lt;/strong>：預設為 &lt;strong>不區分大小寫（Case-Insensitive）&lt;/strong>，但 &lt;strong>會保留狀態（Case-Preserving）&lt;/strong>。也就是說，如果存成 &lt;code>File.txt&lt;/code> 就會顯示為 &lt;code>File.txt&lt;/code>，但程式中以 &lt;code>file.txt&lt;/code> 去存取也能成功讀取。&lt;/li>
&lt;li>&lt;strong>Windows (NTFS)&lt;/strong>：與 macOS 相同，預設規格為 &lt;strong>不區分大小寫（Case-Insensitive）&lt;/strong> 且 &lt;strong>保留狀態（Case-Preserving）&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>Linux / WSL (ext4 等)&lt;/strong>：&lt;strong>完全區分大小寫（Case-Sensitive）&lt;/strong>。&lt;code>File.txt&lt;/code> 與 &lt;code>file.txt&lt;/code> 可以作為完全不同的檔案共存於同一個目錄中。&lt;/li>
&lt;/ul>
&lt;h3 id="經常發生的典型-bug">經常發生的典型 Bug
&lt;/h3>&lt;p>在 Mac 或 Windows 上開發時，如果在原始碼中以小寫指定 &lt;code>#include &amp;quot;myclass.h&amp;quot;&lt;/code>（或 &lt;code>import &amp;quot;./myclass&amp;quot;&lt;/code>），而實際的檔案是 &lt;code>MyClass.h&lt;/code>，由於本機環境的 OS 是不區分大小寫的，所以建置（Build）會成功。&lt;/p>
&lt;p>然而，當把這段程式碼 Commit 上去，並在 CI/CD 伺服器（通常是 Ubuntu 等 Linux）執行建置時，因為 Linux 的 ext4 檔案系統區分大小寫，就會導致「找不到檔案」的編譯錯誤。&lt;/p>
&lt;h3 id="從演算法角度看待檔案搜尋的時間複雜度與正規化">從演算法角度看待：檔案搜尋的時間複雜度與正規化
&lt;/h3>&lt;p>讓我們以數學的角度來思考一下，當檔案系統在解析檔案路徑時，內部進行了什麼處理。&lt;/p>
&lt;p>在區分大小寫的 ext4 中，目錄內的項目（Entry）是以雜湊表（Hash Table）或 B-Tree 等結構來管理的。假設目錄內的檔案數為 $N$，檔名的長度為 $L$，在單純的二元搜尋或樹狀搜尋下，時間複雜度如下：&lt;/p>
$$ T_{search}(N) = O(L \log N) $$&lt;p>另一方面，在 NTFS 或 APFS 等不區分大小寫的檔案系統中，在比較字串前，必須將雙方的字串轉換為相同的大小寫（全大寫或全小寫），這個過程稱為正規化（Case Folding）。考量到 Unicode 的正規化及地區設定（Locale）的大小寫轉換，這並非單純的 ASCII 位元運算可以解決，而是需要查表（Table Lookup）。&lt;/p>
&lt;p>假設轉換函式的運算成本為常數 $C_{fold}$，那麼每一次的字串比較都會產生額外的負擔（Overhead）。&lt;/p>
$$ T_{insensitive\_search}(N) = O( (L \times C_{fold}) \log N ) $$&lt;p>雖然近代的 OS 對此有高度的快取（Cache），但根本上的行為差異只能靠開發層級的規範來約束。&lt;strong>「檔案名稱與目錄名稱全部統一使用小寫與連字號（Kebab-case）或底線（Snake-case）」&lt;/strong> 是最安全的專案規範。&lt;/p>
&lt;hr>
&lt;h2 id="3-路徑分隔符號-path-separators-與檔案路徑的抽象化">3. 路徑分隔符號 (Path Separators) 與檔案路徑的抽象化
&lt;/h2>&lt;p>表示目錄階層的分隔符號處理方式，反映了 OS 之間最根本的差異。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Windows&lt;/strong>：使用反斜線 &lt;code>\&lt;/code>（在日文環境下的部分字體會顯示為日圓符號 &lt;code>¥&lt;/code>），並且存在磁碟機代號（例如：&lt;code>C:\&lt;/code>）與 UNC 路徑（例如：&lt;code>\\Server\Share&lt;/code>）的概念。&lt;/li>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>：使用斜線 &lt;code>/&lt;/code>，且所有的檔案系統都具有從單一根目錄 &lt;code>/&lt;/code> 開始的階層結構（Single Root Hierarchy）。&lt;/li>
&lt;/ul>
&lt;p>許多程式語言在 Windows 上也能自動將 &lt;code>/&lt;/code> 解析為檔案分隔符（因為 Win32 API 本身在某些部分也支援 &lt;code>/&lt;/code>）。但是，如果作為命令列參數傳遞路徑、直接呼叫系統呼叫（System Call），或是將路徑作為字串進行比較或解析時，就會引發致命的錯誤。&lt;/p>
&lt;h3 id="各語言的最佳實踐os-的抽象化">各語言的最佳實踐（OS 的抽象化）
&lt;/h3>&lt;p>&lt;strong>絕對要避免&lt;/strong>使用字串連接（例如：&lt;code>path + &amp;quot;\\&amp;quot; + filename&lt;/code>）來建構檔案路徑。請使用各個語言所提供的標準路徑操作函式庫（OS 抽象層）。&lt;/p>
&lt;h4 id="c-的例子-stdfilesystem">C++ 的例子 (&lt;code>std::filesystem&lt;/code>)
&lt;/h4>&lt;p>在 C++17 之後引入了 &lt;code>&amp;lt;filesystem&amp;gt;&lt;/code>，可以抽象化平台間路徑的差異。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;filesystem&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">namespace&lt;/span> &lt;span class="n">fs&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">filesystem&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 建立與 OS無關的路徑 (透過運算子多載進行抽象化)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;config.json&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fs&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">path&lt;/span> &lt;span class="n">full_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 在 Windows 上會變成 &amp;#34;data\config.json&amp;#34;, 在 Mac/Linux 上會變成 &amp;#34;data/config.json&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Full path: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">full_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="python-的例子-pathlib">Python 的例子 (&lt;code>pathlib&lt;/code>)
&lt;/h4>&lt;p>以前經常使用 &lt;code>os.path.join()&lt;/code>，但現在標準做法是使用物件導向的 &lt;code>pathlib&lt;/code> 模組。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">pathlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 覆寫了 / 運算子，會生成適合當前 OS 的路徑物件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">base_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Path&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;user_data&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">config_file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">base_dir&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="s2">&amp;#34;settings&amp;#34;&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="s2">&amp;#34;app.ini&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 解析路徑或讀取檔案也能使用一致的方法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">config_file&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">exists&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">config_file&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read_text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="nodejs-的例子-path-模組">Node.js 的例子 (&lt;code>path&lt;/code> 模組)
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;path&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// path.join 接收參數，並以適合當前 OS 的分隔符號進行組合
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">configPath&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;config&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;default.json&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">configPath&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Windows: &amp;#34;config\default.json&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// macOS/Linux: &amp;#34;config/default.json&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="4-字元編碼-utf-8-vs-cp932shift-jis-與-unicode-之壁">4. 字元編碼 (UTF-8 vs CP932/Shift-JIS) 與 Unicode 之壁
&lt;/h2>&lt;p>在 Windows 的日文環境中，最大的困擾就是字元編碼。
在現代的開發中，macOS 與 Linux 無論是系統整體、終端機到檔案編碼，已經完全統一為 &lt;strong>UTF-8&lt;/strong>。然而，日文版 Windows 的標準編碼（基於系統地區設定的「ANSI Code Page」）在許多場合仍然預設以 &lt;strong>CP932（微軟擴展的 Shift-JIS）&lt;/strong> 運作。
※ Win32 API 內部的字串表示方式為 UTF-16LE (&lt;code>wchar_t&lt;/code>)。&lt;/p>
&lt;p>在 Python 等語言中讀寫檔案時，若未明確指定編碼，在 Windows 上就會試圖按照 &lt;code>locale.getpreferredencoding()&lt;/code> 的結果（CP932）來解析。這會導致在嘗試讀取儲存為 UTF-8 的檔案時，發生 &lt;code>UnicodeDecodeError&lt;/code>，或是出現亂碼（Mojibake）。&lt;/p>
&lt;h3 id="字元編碼轉換的數學模型與負擔-overhead">字元編碼轉換的數學模型與負擔 (Overhead)
&lt;/h3>&lt;p>將字串從某種編碼（UTF-8）轉換為另一種編碼（UTF-16 或是 CP932）時，最糟的時間複雜度會與字串長度成正比。假設字串的位元組長度為 $B$，轉換的時間複雜度為 $O(B)$。但是，變動長度編碼的 UTF-8 的解析、代理對（Surrogate Pair）的計算，以及轉換表的查詢（Lookup），都會產生無法忽略的負擔。&lt;/p>
&lt;p>假設字串長度為 $N$，從多位元組字元對應到 Unicode 碼位（Code Point）的函式為 $f_{decode}$，從碼位對應到目標編碼的函式為 $f_{encode}$，那麼總轉換時間 $T_{conv}$ 可以近似為：&lt;/p>
$$ T_{conv} = \sum_{i=1}^{N} \Big( C_{decode} \cdot f_{decode}(x_i) + C_{encode} \cdot f_{encode}(y_i) \Big) \approx O(N) $$&lt;p>在跨平台應用程式中，我們必須意識到，每次呼叫 OS 的原生 API（跨越 I/O 邊界）時，都會產生這種轉換成本（特別是在為 Windows 開發 C++ 程式碼時，經常會頻繁地使用 &lt;code>MultiByteToWideChar&lt;/code> 等轉換為 UTF-16）。&lt;/p>
&lt;h3 id="針對編碼的對策">針對編碼的對策
&lt;/h3>&lt;p>最保險的對策是**「無論何時都明確指定 UTF-8」**。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Python 的優良範例：隨時指定 encoding=&amp;#34;utf-8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;data.txt&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">encoding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;你好，世界！&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>此外，為了在 Windows 的終端機（命令提示字元或 PowerShell）正確顯示 UTF-8 輸出，有時必須在啟動應用程式時設定環境變數 &lt;code>PYTHONUTF8=1&lt;/code>，或者若是 Node.js，則需利用 &lt;code>chcp 65001&lt;/code> 指令暫時將主控台的 Code Page 變更為 UTF-8。&lt;/p>
&lt;hr>
&lt;h2 id="5-環境變數與-shell-環境的差異-bashzsh-vs-powershell">5. 環境變數與 Shell 環境的差異 (bash/zsh vs PowerShell)
&lt;/h2>&lt;p>執行建置腳本或開發用工具時，Shell（命令列直譯器）的差異也是跨平台的一大障礙。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>macOS / Linux&lt;/strong>：以 &lt;code>bash&lt;/code> 或 &lt;code>zsh&lt;/code> 為主流。執行基於文字的管線（Pipeline）處理。&lt;/li>
&lt;li>&lt;strong>Windows&lt;/strong>：命令提示字元 (&lt;code>cmd.exe&lt;/code>) 或 &lt;code>PowerShell&lt;/code>。PowerShell 建立於 .NET 之上，擁有強大的物件導向管線，但語法與 POSIX Shell 完全不同。&lt;/li>
&lt;/ul>
&lt;p>由於參照和設定環境變數的方法不同，如果在 Node.js 的 &lt;code>package.json&lt;/code> 中的 &lt;code>scripts&lt;/code> 區域寫出依賴 OS 的寫法，在其他環境下就會無法運作。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ❌ 不良範例：在 Windows 中「NODE_ENV」不會被視為指令而報錯
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NODE_ENV=production webpack&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="解決方案活用跨平台專用工具">解決方案：活用跨平台專用工具
&lt;/h3>&lt;p>若是 Node.js 環境，可使用 &lt;code>cross-env&lt;/code> 等套件來抽象化環境變數的設定。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ✅ 優良範例：cross-env 會吸收 OS 的差異，正確地設定環境變數並啟動 webpack
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="s2">&amp;#34;scripts&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;build&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;cross-env NODE_ENV=production webpack&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;clean&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;rimraf dist/&amp;#34;&lt;/span> &lt;span class="c1">// 不使用 rm -rf，改用跨平台的移除工具
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>如果在大型專案中需要複雜的 Shell Script，目前的最佳實踐是讓 Windows 環境的開發者也標準備配 WSL (Windows Subsystem for Linux) 或 Git Bash，並將所有的批次處理統一為 &lt;code>.sh&lt;/code> 腳本來管理。&lt;/p>
&lt;hr>
&lt;h2 id="6-跨平台的建置系統與編譯器">6. 跨平台的建置系統與編譯器
&lt;/h2>&lt;p>在處理 C++ 或 Rust 等原生程式碼（直接編譯為機器碼的語言）時，除了 OS 特有的 API，我們還需要克服建置系統與編譯器的差異。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>編譯器&lt;/strong>：
&lt;ul>
&lt;li>Windows: MSVC (Microsoft Visual C++), MinGW (GCC for Windows)&lt;/li>
&lt;li>macOS: Apple Clang&lt;/li>
&lt;li>Linux: GCC, Clang&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>二進位格式&lt;/strong>：
&lt;ul>
&lt;li>Windows: PE (Portable Executable) &lt;code>.exe&lt;/code> / &lt;code>.dll&lt;/code>&lt;/li>
&lt;li>macOS: Mach-O&lt;/li>
&lt;li>Linux: ELF (Executable and Linkable Format) &lt;code>.so&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="利用-cmake-作為元建置系統-meta-build-system">利用 CMake 作為元建置系統 (Meta-build System)
&lt;/h3>&lt;p>在 C/C++ 專案中，實現跨平台的世界級業界標準是 &lt;strong>CMake&lt;/strong>。CMake 不直接編譯原始碼，而是作為一個「生成器（Generator）」，用來產生符合各環境的原生建置設定檔（Windows 就是 Visual Studio 的 Solution 檔，Linux/Mac 則是 Makefile 或 Ninja 的建置腳本）。&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;CMakeLists.txt (獨立於平台)&amp;#34;] --&amp;gt; B(&amp;#34;CMake 引擎&amp;#34;)
B --&amp;gt; C{&amp;#34;目標作業系統&amp;#34;}
C --&amp;gt;|Windows| D[&amp;#34;Visual Studio 解決方案 / MSBuild&amp;#34;]
C --&amp;gt;|macOS| E[&amp;#34;Xcode 專案 / Apple Clang&amp;#34;]
C --&amp;gt;|Linux| F[&amp;#34;Makefile / Ninja / GCC&amp;#34;]
D --&amp;gt; G[&amp;#34;Windows 執行檔 (.exe)&amp;#34;]
E --&amp;gt; H[&amp;#34;macOS 執行檔 (Mach-O)&amp;#34;]
F --&amp;gt; I[&amp;#34;Linux 執行檔 (ELF)&amp;#34;]
&lt;/pre>
&lt;p>透過使用 CMake，能夠吸收環境間的差異，並由單一的設定檔（&lt;code>CMakeLists.txt&lt;/code>）為每個 OS 生成最佳的二進位檔。依賴函式庫的解析（&lt;code>find_package&lt;/code>），或是連結各 OS 特有的函式庫，也可以透過條件分支輕鬆撰寫。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># CMakeLists.txt 的部分範例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">WIN32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># 連結 Windows 特有函式庫（如 WS2_32.lib 等）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">ws2_32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_WINDOWS&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">elseif&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">APPLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># 連結 macOS 特有的 Framework
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s2">&amp;#34;-framework Foundation&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_MACOS&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">elseif&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">UNIX&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">NOT&lt;/span> &lt;span class="s">APPLE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="c"># 針對 Linux 的連結（如 pthread 等）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span> &lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">my_app&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">pthread&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">OS_LINUX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-活用架構模式os-抽象層-osal">7. 活用架構模式：OS 抽象層 (OSAL)
&lt;/h2>&lt;p>將依賴於系統的處理（檔案操作、建立程序 / 執行緒、記憶體管理、Socket 通訊等）與應用程式核心的業務邏輯完全分離，是跨平台開發的要點。&lt;/p>
&lt;p>為了實現這一點，我們可以使用 &lt;strong>OS 抽象層 (OS Abstraction Layer, OSAL)&lt;/strong> 模式。&lt;/p>
&lt;p>以下是封裝各 OS 專屬 API 並提供通用介面的類別設計範例。我們可以利用多型（Polymorphism），或者編譯時的巨集切換來切換實作。&lt;/p>
&lt;pre class="mermaid">
classDiagram
class SystemInterface {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
class WindowsSystem {
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
class PosixSystem {
+createDirectory(path: string) bool
+getSystemMemoryUsage() uint64
+spawnProcess(command: string) int
}
SystemInterface &amp;lt;|-- WindowsSystem
SystemInterface &amp;lt;|-- PosixSystem
&lt;/pre>
&lt;p>透過將平台專屬程式碼隔離在同一個地方（通常是 &lt;code>src/platform/windows/&lt;/code> 或 &lt;code>src/platform/posix/&lt;/code> 等目錄），可以確保其餘 95% 的程式碼（GUI 邏輯、資料處理、通訊協定的解析等）完全跨平台，並保持可測試的狀態。&lt;/p>
&lt;hr>
&lt;h2 id="8-在-cicd-中的跨平台驗證-矩陣建置">8. 在 CI/CD 中的跨平台驗證 (矩陣建置)
&lt;/h2>&lt;p>無論開發者在本機環境多麼謹慎地編寫程式碼，跨平台支援的最後一道防線仍是 &lt;strong>CI/CD (Continuous Integration / Continuous Deployment) 管線&lt;/strong>。在本機環境（例如 Mac）可以順利運作，但在其他 OS（Windows）上出現編譯錯誤的情況層出不窮。&lt;/p>
&lt;p>活用 GitHub Actions 或 GitLab CI 等最新的 CI 工具，在每次建立 Pull Request 時設定&lt;strong>同時在 Windows, macOS, Linux 的所有環境下並行執行建置與測試&lt;/strong>的矩陣建置（Matrix Build）吧。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># GitHub Actions 的跨平台 CI 設定範例&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Cross-Platform Build and Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">push, pull_request]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 就算其中一個 OS 失敗，也繼續執行其他 OS 的測試&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 指定 Windows, macOS, Linux 這 3 個 Runner&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">ubuntu-latest, windows-latest, macos-latest]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Set up Python Environment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/setup-python@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">python-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.11&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cache&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;pip&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 在跨平台中也快取相依套件&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Install dependencies&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">python -m pip install --upgrade pip &amp;amp;&amp;amp; pip install -r requirements.txt&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Run Test Suite&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">pytest -v&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>將此 CI/CD 的流程視覺化後如下圖所示。&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;開發者&amp;#34;
participant GitHub as &amp;#34;GitHub Actions (協調者)&amp;#34;
participant Ubuntu as &amp;#34;Linux Runner (VM)&amp;#34;
participant Windows as &amp;#34;Windows Runner (VM)&amp;#34;
participant Mac as &amp;#34;macOS Runner (VM)&amp;#34;
Dev-&amp;gt;&amp;gt;GitHub: &amp;#34;git push origin feature-branch&amp;#34;
GitHub-&amp;gt;&amp;gt;Ubuntu: &amp;#34;指派工作 (ubuntu-latest)&amp;#34;
GitHub-&amp;gt;&amp;gt;Windows: &amp;#34;指派工作 (windows-latest)&amp;#34;
GitHub-&amp;gt;&amp;gt;Mac: &amp;#34;指派工作 (macos-latest)&amp;#34;
par Parallel Execution Matrix
Ubuntu--&amp;gt;&amp;gt;Ubuntu: &amp;#34;Checkout, 設定環境, 建置, 測試&amp;#34;
Windows--&amp;gt;&amp;gt;Windows: &amp;#34;Checkout, 設定環境, 建置, 測試&amp;#34;
Mac--&amp;gt;&amp;gt;Mac: &amp;#34;Checkout, 設定環境, 建置, 測試&amp;#34;
end
Ubuntu--&amp;gt;&amp;gt;GitHub: &amp;#34;結果: 成功 (Pass)&amp;#34;
Windows--&amp;gt;&amp;gt;GitHub: &amp;#34;結果: 失敗 (Fail - 編碼錯誤)&amp;#34;
Mac--&amp;gt;&amp;gt;GitHub: &amp;#34;結果: 成功 (Pass)&amp;#34;
GitHub--&amp;gt;&amp;gt;Dev: &amp;#34;狀態: 失敗 (Windows 檢查失敗)&amp;#34;
&lt;/pre>
&lt;p>自動收集各 OS 的測試結果，並設定分支保護規則，&lt;strong>只有在所有環境都顯示綠燈（成功）時才允許合併（Merge）至 main 分支&lt;/strong>，藉此防範依賴平台的 Bug 混入正式環境或發布版本中。&lt;/p>
&lt;hr>
&lt;h2 id="總結">總結
&lt;/h2>&lt;p>Mac 與 Windows 的跨平台開發，存在許多根植於歷史背景的廣泛課題。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>換行字元&lt;/strong>：利用 &lt;code>.gitattributes&lt;/code> 強制進行儲存庫層級的正規化（如統一 LF 等）。&lt;/li>
&lt;li>&lt;strong>大小寫區分&lt;/strong>：不要依賴 macOS/Windows「不區分大小寫」的行為，應嚴格制定檔案命名規則，並致力於嚴謹的大小寫配對。&lt;/li>
&lt;li>&lt;strong>路徑分隔符號&lt;/strong>：利用語言標準的路徑操作 API（&lt;code>std::filesystem&lt;/code>, &lt;code>pathlib&lt;/code>, &lt;code>path&lt;/code> 模組）來吸收 OS 差異。&lt;/li>
&lt;li>&lt;strong>編碼&lt;/strong>：永遠指定 UTF-8，徹底排除 Windows 預設行為 CP932 的影響。&lt;/li>
&lt;li>&lt;strong>環境變數與 Shell&lt;/strong>：使用 &lt;code>cross-env&lt;/code> 等抽象化工具，或將執行環境統一為 WSL/Docker 等。&lt;/li>
&lt;li>&lt;strong>建置系統&lt;/strong>：若為 C/C++ 則活用 CMake 等元建置系統，為每個 OS 產生最佳的原生工具鏈。&lt;/li>
&lt;li>&lt;strong>OS 依賴程式碼&lt;/strong>：設計 OS 抽象層 (OSAL)，分離並隔離依賴平台的邏輯。&lt;/li>
&lt;li>&lt;strong>CI/CD&lt;/strong>：導入矩陣建置，自動化所有目標 OS 上的乾淨建置與測試，排除依賴個人的狀況。&lt;/li>
&lt;/ol>
&lt;p>現今雖然有 Electron, Tauri, .NET 等強大的框架能吸收許多差異，但基底 OS 原生行為（檔案系統與編碼）的知識，在解決嚴重的效能問題或艱深的 Bug 時，仍然是不可或缺的。從專案初期階段就讓整個團隊共享並徹底落實這些最佳實踐，將能大幅減少因 OS 差異所導致且毫無意義的除錯時間，讓我們能集中精力創造軟體的本質價值。&lt;/p></description></item></channel></rss>