泛型演算法萃取實例

Similar documents
Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

C C C The Most Beautiful Language and Most Dangerous Language in the Programming World! C 2 C C C 4 C Project 30 C Project 3 60 Project 40

Scott Effective C++ C++ C++ Roger Orr OR/2 ISO C++ Effective Modern C++ C++ C++ Scoot 42 Bart Vandewoestyne C++ C++ Scott Effective Modern C++ Damien

CC213

ENGG1410-F Tutorial 6

Important Notice SUNPLUS TECHNOLOGY CO. reserves the right to change this documentation without prior notice. Information provided by SUNPLUS TECHNOLO

Microsoft Word doc

C++ 程式設計

一 -50-

地質調査研究報告/Bulletin of the Geological Survey of Japan

中 國 學 研 究 期 刊 泰 國 農 業 大 學 บ นทอนเช นก น และส งผลก บการด ดแปลงจากวรรณกรรมมาเป นบทภาพยนตร และบทละคร โทรท ศน ด วยเช นก น จากการเคารพวรรณกรรมต นฉบ บเป นหล

2/80 2

Microsoft Word - 物件導向編程精要.doc

by industrial structure evolution from 1952 to 2007 and its influence effect was first acceleration and then deceleration second the effects of indust

Improved Preimage Attacks on AES-like Hash Functions: Applications to Whirlpool and Grøstl

中 國 茶 詩 與 文 人 茶 道 生 活 顏 鸝 慧 人 社 科 院 / 人 文 藝 術 教 學 中 心 摘 要 飲 茶 的 起 源, 歷 來 眾 說 紛 紜, 根 據 文 獻 資 料 顯 示, 在 唐 代 之 前, 飲 茶 只 是 一 種 區 域 性 的 生 活 風 俗 然 西 漢 時 已 有

<4D F736F F D205F FB942A5CEA668B443C5E9BB73A740B5D8A4E5B8C9A552B1D0A7F75FA6BFB1A4ACFC2E646F63>

_汪_文前新ok[3.1].doc

封面.PDF

Preface This guide is intended to standardize the use of the WeChat brand and ensure the brand's integrity and consistency. The guide applies to all d

2006中國文學研究範本檔

untitled

硕 士 学 位 论 文 论 文 题 目 : 北 岛 诗 歌 创 作 的 双 重 困 境 专 业 名 称 : 中 国 现 当 代 文 学 研 究 方 向 : 中 国 新 诗 研 究 论 文 作 者 : 奚 荣 荣 指 导 老 师 : 姜 玉 琴 2014 年 12 月

國家圖書館典藏電子全文

20

國立中山大學學位論文典藏.PDF

南華大學數位論文

Microsoft Word doc

BC04 Module_antenna__ doc

2015年4月11日雅思阅读预测机经(新东方版)


ebook8-30

Microsoft Word - Final Exam Review Packet.docx


Untitled-3

4. 每 组 学 生 将 写 有 习 语 和 含 义 的 两 组 卡 片 分 别 洗 牌, 将 顺 序 打 乱, 然 后 将 两 组 卡 片 反 面 朝 上 置 于 课 桌 上 5. 学 生 依 次 从 两 组 卡 片 中 各 抽 取 一 张, 展 示 给 小 组 成 员, 并 大 声 朗 读 卡

摘要

曹美秀.pdf

國立高雄大學○○○○○○學系(研究所)(標楷體18號字

Microsoft Word - 第四組心得.doc

ISSN

Microsoft PowerPoint - Class5.pptx

Microsoft Word - 11月電子報1130.doc

A dissertation for Master s degree Metro Indoor Coverage Systems Analysis And Design Author s Name: Sheng Hailiang speciality: Supervisor:Prof.Li Hui,

A VALIDATION STUDY OF THE ACHIEVEMENT TEST OF TEACHING CHINESE AS THE SECOND LANGUAGE by Chen Wei A Thesis Submitted to the Graduate School and Colleg


中山大學學位論文典藏

Shanghai International Studies University THE STUDY AND PRACTICE OF SITUATIONAL LANGUAGE TEACHING OF ADVERB AT BEGINNING AND INTERMEDIATE LEVEL A Thes

K301Q-D VRT中英文说明书141009

國立中山大學學位論文典藏

Microsoft Word 谢雯雯.doc

Microsoft Word - 09王充人性論_確定版980317_.doc


D A

Microsoft PowerPoint - STU_EC_Ch08.ppt

<4D F736F F D203033BDD7A16DA576B04FA145A4ADABD2A5BBACF6A16EADBAB6C0ABD2A4A7B74EB8712E646F63>

mode of puzzle-solving

3戴文鋒-人文.indd

35-55

: : : : : ISBN / C53:H : 19.50

附件1:

施工災害防治建築師、各專業技師及承包商責任制度之研究

93年度分項計畫執行報告-子計畫八.doc

马 大 华 人 文 学 与 文 化 学 刊 Journal of Chinese Literature and Culture 6 前 言 顾 城 曾 在 接 受 德 国 汉 学 家 顾 彬 及 张 穗 子 专 访 中, 将 其 诗 歌 创 作 分 为 四 个 时 期, 即 自 然 阶 段 文 化


錫安教會2015年11月29日分享


國立中山大學學位論文典藏.PDF

PowerPoint Presentation

McGraw-Hill School Education Group Physics : Principles and Problems G S 24


: ( ),,,,, 1958,,, , 263, 231, ,,,,,,, 4, 51, 5, 46, 1950, :,, 839, 3711, ( ) ( ) 20 ( ),, 56, 2, 17, 2, 8, 1,,,,, :,,,, ;,,,,

南華大學數位論文


從詩歌的鑒賞談生命價值的建構

(procedure-oriented)?? 2

Microsoft Word - 08_76-93_¦ó³B¬O¡§Âk¡¨®a¡H.doc

~ ~ ~

Microsoft Word - 口試本封面.doc

Excel VBA Excel Visual Basic for Application

CC213

Abstract Yiwei( 易 緯 ) is one of the most important elements in Yi ( 易 )study of Han dynasty. It had rich meanings and close relation with other parts

星河33期.FIT)

影響新產品開發成效之造型要素探討

PowerPoint Presentation


1對外華語文詞彙教學的策略研究_第三次印).doc

林教授2.PDF

416 Journal of Software 4(3) 2003,1. Java, Java, Java : ; ; ;Java : TP311 : A (binding time analysis ), (partial evaluation)., [1~3]., C Java, Java.,.

Microsoft PowerPoint - Bronson-v3-ch07.ppt [相容模式]

中国科学技术大学学位论文模板示例文档

國家圖書館典藏電子全文

untitled

03施琅「棄留臺灣議」探索.doc

hks298cover&back

<4D F736F F D20B5DAC8FDB7BDBE57C9CFD6A7B8B6D6AEB7A8C2C98696EE7DCCBDBEBF2E646F63>

成 大 中 文 學 報 第 四 十 四 期 The Body Metaphors in The Travels of Lao Can Hsu Hui-Lin Assistant Professor, Department of Chinese Literature, National Taiwan


中國的科學與中國的公民:大陸研究在台灣的困境\\

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

Transcription:

指標與陣列 柯向上 Josh Ko 2005.12.28 核心概念 指標與陣列之間的關係, 可算是 C 語言最有趣的設計之一 但 C/C++ 的學習者往往沒辦法掌握關鍵所 在, 而弄不清楚指標 陣列的互換關係 說穿了, 陣列實用上只有以下一個規則 : 出現在算式之中的陣列名稱可被隱喻轉換為 指向陣列第一個元素 的指標 就是這個規則而已 ( 事實上本文至此就可以結束了 ) 我在文中暫且把它稱為 (the) Fundamental Rule 除此之外, 陣列沒有別的操作了 別的操作 應用都是由這個規則以及其他 C/C++ 語言基本規 則組合推導而得 所謂 Array Subscripting 我說 : 除此之外, 陣列沒有別的操作, 第一個丟出來的問題一定是 : 那 a[k] 這動作怎麼說? 事實 上,subscripting 運算子 (operator[]) 從來就不是針對陣列, 而是指標的操作 當我們寫 a[k] 因為這是個算式, 根據 Fundamental Rule,a 可以轉換為指標, 進而施行 subscripting 後面就較為大眾 所熟知了 : 對於一個指標 p, 若寫 p[k] 這個算式完全等價於 *(p + k) 於是存取到陣列 a 的第 k 項元素 又因為指標與整數的加法具有交換性, 上面的算式可寫為 *(k + p) 於是等價於 k[p] 所以如果寫 1

k[a] 以此存取 a 的第 k 項元素, 也毫無問題 此時, 要把 operator[] 解釋成針對陣列的操作, 恐怕就比較 困難了 陣列引數傳遞 首先我們必須了解 : 不能以一個陣列初始化另一個陣列 也就是說 int a[5]; int c[5] = a; // 錯誤 : 不能以陣列初始化另一個陣列 第二行無法通過編譯 而函式引數傳遞的方式, 是以引數將參數初始化 舉例 : void f(int j){ } //... int i; //... f(i); 進行呼叫時,f 的參數 j 會以對應引數 i 的值進行初始化 陣列無法進行初始化, 因此不能當作函式參 數使用 那麼 : void g(int[]); // 或是 void g(int[n]); 這又是什麼呢? 這參數是隻披上羊皮的狼 ( 對很多初學者而言 ), 我們知道它不可能是個陣列參數 很 多人都清楚, 這個參數事實上是個指標 : void g(int*); 當我們呼叫 g 時 : int a[5]; g(a); 因為 a 出現在算式中, 而 g 的參數是個指標, 於是 Fundamental Rule 介入, 進行 array-to-pointer 轉換, 實際傳入函式的是 指向 a 的第 0 項元素 的指標 當 C++ reference 出現時, 情形變得比較不一樣, 但仍未脫出 Fundamental Rule 的規範 以下手法相當 常見 : 2

template<typename T, size_t N> inline size_t array_size(const T (&)[N]){ return N; } 這個 function template 可用來取得一個靜態陣列的元素個數 因為這個 function template 的參數是個 reference to array, 可以用陣列進行初始化, 所以進行呼叫時, 是以貨真價實的陣列把參數初始化 可能有人問 : 那 Fundamental Rule 不就沒派上用場? 哈,Fundamental Rule 在此是沒派上用場, 但也沒有錯 : 出現在算式之中的陣列名稱可被隱喻轉換為 指向陣列第一個元素 的指標 也就是說, 陣列在必要時可進行轉換, 但在 array_size 的例子裡, 陣列不必轉換就已適用, 真的進行 轉換還會出問題呢 再舉一個 C++ templates 的例子 ([6],p. 169): template<typename T> const T& max(const T& a, const T& b); std::cout << max("apple", "Pear") << std::endl; 呼叫者顯然認為 "Apple" 和 "Pear" 兩個 string literals 會以 const char* 的形式傳入, 但因為 max 的 兩個參數都是 reference, 所以無須進行 array-pointer 轉換, 兩個參數的 T 分別會被推導為 const char[6] 和 const char[5], 型別不合, 於是 template argument deduction 失敗 又例如 C 取得陣列元素個數的慣用手法 : #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) sizeof(a) 也是個算式, 如果 a 先被轉換為指標, 整個算式就走樣了 當然, 這種不施行 array-to-pointer 轉換的程式碼相對而言較為少見 證據在此 為了避免有人不服氣, 我摘錄幾段權威的描述作為佐證 首先是 C++03 Standard [1] 裡面所提的 array-to-pointer 轉換 : 4.2 Array-to-pointer conversion [conv.array] (p. 60) An lvalue or rvalue of type array of N T or array of unknown bound of T can be converted to an rvalue of type pointer to T. The result is a pointer to the first element of the array. 正是 Fundamental Rule 的嚴格定義版 接下來, 看看關於 subscript 運算子的部份 : 5.2.1 Subscripting [expr.sub] (p. 68) 3

A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type pointer to T and the other shall have enumeration or integral type. The result is an lvalue of type T. The type T shall be a completely-defined object type. The expression E1[E2] is identical (by definition) to *((E1)+(E2)). C99 Standard [2] 在這部份的文字處理比較有趣 : 6.5.2.1 Array subscripting (p. 70) Constraints One of the expressions shall have type pointer to object type, the other expression shall have integer type, and the result has type type. Semantics A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero). 就高階語意而言, 的確沒錯, 因為 pointer arithmetic 本來就應該針對 指向陣列元素的指標 施行 不過就底層語意而言,operator[] 仍然只能施行於指標之上 C99 Standard 在此段下面補充一個例子 : EXAMPLE Consider the array object defined by the declaration int x[3][5]; Here x is a 3 x 5 array of ints; more precisely, x is an array of three element objects, each of which is an array of five ints. In the expression x[i], which is equivalent to (*((x)+(i))), x is first converted to a pointer to the initial array of five ints. Then i is adjusted according to the type of x, which conceptually entails multiplying i by the size of the object to which the pointer points, namely an array of five int objects. The results are added and indirection is applied to yield an array of five ints. When used in the expression x[i][j], that array is in turn converted to a pointer to the first of the ints, so x[i][j] yields an int. 最後一句的 that array 指的是 x[i] 由此例子可看出, 任何陣列在施行 subscripting 之前, 必然都會先被轉換為指標, 符合我先前的描述 最後是 C++03 Standard 裡面關於函式宣告的 陣列參數 部份 : 8.3 Meaning of declarators / 8.3.5 Functions [dcl.fct] (p. 139)... The type of a function is determined using the following rules. The type of each parameter is 4

determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type array of T or function returning T is adjusted to be pointer to T or pointer to function returning T, respectively.... 可見沒有 陣列參數 這種東西 講古 最後提一點 C 演化出陣列 指標的歷史, 作為終曲 C 的 predecessor 是 B,B 的 predecessor 是 BCPL, 後兩者都沒有型別的概念 (typeless) 在 BCPL 和 B 的世界裡, 記憶體是一塊塊大小固定的 cell, 呈線性排列 變數基本上都當作整數看待, 所有的操作都不分型別 : 例如看到 operator+, 就對兩個 cells 執行機器的整數加法 等等 BCPL 和 B 的記憶體模型呈線性排列, 又因為 cell 沒有型別, 一個 cell 裡面存放的值可能是單純的數值資料, 也可能是另一個 cell 在記憶體內的 index 因此, 若對一個 cell 施行 unary operator*, 就會把該 cell 所存的值當作 index, 跳到這個 index 所示的位置去 因為每個 cell 都是整數, 所以把一個 cell 的值加上某個值之後再施行 unary operator*, 就會存取到鄰近的 cell 這就是最原始的指標和指標算術 由此我們也可看出, 指標與整數的加法具有交換律, 歷史上是將整數加法 ( 因為 BCPL/B 沒有型別概念 ) 的交換律套上型別而演化出來的 *(a + k) 看起來比較冗贅,BCPL 於是將此縮寫為 a!k,b 對此的縮寫則是 a[k] 這在數學領域有個恰當的 counterpart:a k, 也就是一般對 C 陣列 取下標 的想法 BCPL 和 B 的陣列語意很有趣 在 B 裡, auto V[10]; 這個述句會配置出 11 個 cells: 首先配置出一個名為 V 的 cell, 再另外配置 10 個連續的 cells, 最後把陣列第一個 cell 的 index 存於 V 中 所以,*V 就是陣列的第一個元素, 也就是 *(V + 0), 也就是 V[0] 因為 C 繼承了 BCPL 和 B 的陣列設計, 也就是以 指向第一個元素的指標 存取陣列, 因此如果以慣常方法詮釋 a[i] 的話, 就會產生 陣列元素編號從 0 開始 的感覺 V 本身是個變數, 所以在 B 裡, 我們甚至可以寫 V = V - 1; V 這個陣列 的 index 上下限就變成了 1 到 10 到了 C, 陣列的語意有了相當大的轉變, 但對使用方式幾無影響 : 在 BCPL 和 B 裡建立陣列時, 必須實際儲存一個指標, 指向陣列的第一個元素 但 C 不再將這個指標儲存起來, 而直接把陣列名稱看作是指向陣列第一個元素的指標 除了對陣列名稱的 assignment 以外, 其他的陣列操作語法都不受影響 直接引用原文 [4]:... Problems became evident when I tried to extend the type notation, especially to add structured 5

(record) types. Structures, it seemed, should map in an intuitive way onto memory in the machine, but in a structure containing an array, there was no good place to stash the pointer containing the base of the array, nor any convenient way to arrange that it be initialized. For example, the directory entries of early Unix systems might be described in C as struct { int inumber; char name[14]; }; I wanted the structure not merely to characterize an abstract object but also to describe a collection of bits that might be read from a directory. Where could the compiler hide the pointer to name that the semantics demanded? Even if structures were thought of more abstractly, and the space for pointers could be hidden somehow, how could I handle the technical problem of properly initializing these pointers when allocating a complicated object, perhaps one that specified structures containing arrays containing structures to arbitrary depth? The solution constituted the crucial jump in the evolutionary chain between typeless BCPL and typed C. It eliminated the materialization of the pointer in storage, and instead caused the creation of the pointer when the array name is mentioned in an expression. The rule, which survives in today s C, is that values of array type are converted, when they appear in expressions, into pointers to the first of the objects making up the array. This invention enabled most existing B code to continue to work, despite the underlying shift in the language s semantics. The few programs that assigned new values to an array name to adjust its origin possible in B and BCPL, meaningless in C were easily repaired. More important, the new language retained a coherent and workable (if unusual) explanation of the semantics of arrays, while opening the way to a more comprehensive type structure. 結語 根據我自己的 C/C++ 學習經驗, 學習程式語言時, 若能多研究其語法語意 ( 例如 Fundamental Rule), 並觀察其歷史演進 ( 例如 typeless BCPL/B 到 typed C 的種種轉變 ) 和重要設計決策 ( 例如 BCPL/B/C 的陣列語意演化 ), 將能對該語言有更深一層了解, 運用起來也會更加得心應手 初學者常問的一些基本問題 ( 例如 陣列 index 為何從 0 起算? ) 很多都能因此而豁然開朗 在此推薦 [4] [5], 分別描述 C 和 C++ 的演化歷程與重大決策, 相當值得一讀 6

參考資料 [1] ISO/IEC 14882:2003, International Standard Programming Languages C++. [2] ISO/IEC 9899:1999, International Standard Programming Languages C. [3] The C Programming Language, 2/e, by Brian W. Kernighan and Dennis M. Ritchie, Prentice Hall PTR 1988. [4] The Development of the C Language, by Dennis M. Ritchie, Second History of Programming Languages conference, Cambridge, Mass., April, 1993. [5] The Design and Evolution of C++, by Bjarne Stroustrup, Addison-Wesley 1994. [6] C++ Templates 全覽, 侯捷 榮耀 姜宏合譯, 碁峰 2004 原文本 :C++ Templates: the Complete Guide, by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley 2002. 7