楊志璿
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    pointer I === :::success 本篇著重介紹 raw pointer,下篇才會介紹到 smart pointer 建議搭配 Jserv 老師的「[你所不知道的C語言:指標篇](https://hackmd.io/@sysprog/c-pointer)」閱讀 但是會著重介紹 Jserv 老師沒有介紹到的部份。 :::warning 但不是代表要讀完老師的內容,才能閱讀本篇 這篇會比該篇短 ::: ## 指標就是力量 如果你要測驗一個工程師是不是對 C 語言有所掌握,應該從指標開始測驗起。 指標用以表示軟體最後一層的抽象關係,指標相鄰就可以預期物件於記憶體中相鄰。 只是筆者這邊主要探討 C\+\+,所以先看到這個這個例子: ```cpp struct S { int a; int b; }; ``` 編譯器會如何看待這個結構體 `S` 呢? - S 會是一種使用者定義的資料型態 - S 佔用 4 byte + 4 byte 的儲存空間[^1] - `S::a` 就是指涉 S 這段記憶體空間當中的前 4 個 byte - 同理,`S::b` 就是指涉,這段記憶體空間中的後 4 個 byte [^1]: 考慮 data model 是 LP64 以下,詳見 [data model wiki](https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models) 所以如果有一個未定義的指標傳入 function: ```cpp void foo(void *data) { S* t = (S*)data; } ``` 就表示我把 `data` 看作是指向 `S` 結構體的指標,命名為 `t`,於是,不論如何 這段記憶體空間就可以被當作是有 `sizeof(S)` 這麼大的連續空間,S 物件其成員(data members)就只是個這段空間的偏移量而已。 註: - ABI issue 改篇另外探討 - \_\_randomize_layout macro 另篇探討 ### Enerything is an offset 對於電腦來說,一切只是在儲存空間中的排列方式而已。 對一個物件的排列,從哪裡開始解讀,怎麼解讀都只是工程師規範的,大家說好就好。 ![](https://hackmd.io/_uploads/rkAAE3nJT.jpg) src: https://www.sohu.com/a/237400576_720176 只是這樣就會遇到 memory safty 的問題,在寫程式的當下編譯器可能可以幫你檢查,這塊記憶體,這個儲存空間是什麼、該做什麼。但,在執行時期大多編譯語言就不會幫你做檢查了。你需要對你的行為負責。 因此這樣的特性,既是 C\+\+ 語言的優點,也是 C\+\+ 語言的缺點。 > Trust me, I know what I do. 於是衍生出了 memory safty 的觀念,讀者可以搭配參考 [RAII](https://hackmd.io/@25077667/raii) ## The C-style casting 先討論一個基本問題: :::info C++ 的指標跟 C 語言的指標有什麼區別? ::: 這邊會有兩部份答案: 1. 本質沒有區別,就是一個指向記憶體位置的數值 2. 有資料型態 (data type) 的型態保證 直接上範例: ```cpp= #include <stdlib.h> int main(int argc, char**argv) { int *p = malloc(sizeof(int)); free(p); } ``` 如果你認為 C 語言只是 C++ 的子集合,那麼你在這範例 line 4 就會出現問題。 在 C\+\+98 當中, C\+\+ 就已經規範資料型態如果資料型態不一致,且沒有宣告隱式轉型,則無法轉型:[標準(expr.general)](https://eel.is/c++draft/conv#general-3),說到 > An expression E can be implicitly converted to a type T if and only if the declaration T t=E; is well-formed, for some invented temporary variable t. 如果一個 expression $E$ 可以被隱式轉型成 $T$ 資料型態,若且唯若,宣告 $T$ t = $E$ 是有定義的。 通常 CS101 會讓小朋友們直接寫上 ```cpp int *p = (int *)malloc(sizeof(int)); ``` 希望讀者可以知道為什麼? 因為 `malloc` 的 signature 是 `(void*)(*malloc)(size_t)` 。function signature 筆者會寫另外一篇部落格探討。 我們從這可以看到 malloc 返回、回傳的資料型態是 void pointer。 ![](https://hackmd.io/_uploads/H1DT7o21a.png) 好的,所以 `void *` 到 `int *` 的轉換在 C++ 當中沒有宣告嗎? 你在標準當中找不到一個 function 如下: ```cpp operator int*(void* p) { return reinterpret_cast<int*>(p); } ``` 而你是沒有辦法把這 `(int*)(*(int*))(void*)` 正確定義的,因為 an lvalue that refers to a non-static member function cannot be obtained. 同時因為 void 是內建資料型態(builtin type),所以你有沒有辦法 hack 這些轉換的函式到 void 當中。 ### C-style casting 是如何運作的? https://en.cppreference.com/w/cpp/language/explicit_cast ## this pointer 在許多 C++ 課程中,都把 C++ 的物件導向當作 **advanced** 議題。 而筆者認為:不,在西元 1998 年之後,這已經不是 C++ 的 **advanced** issue 了。 我們直接來看範例: 你該如何撰寫 C 語言的物件導向程式? 範例一 ```c typedef struct _Object { int _m_data; (int (*)(void)) getter; (void (*)(int)) setter; } Object, *PObject; void InitObject(PObject o); void FreeObject(Pobject o); ``` 是吧!你只能這樣寫對吧!而且這邊很 Microsoft style。 了不起也是把 function type 額外 typedef 出來: 範例二 ```c typedef int(*fObjectGetter)(void); typedef void(*fObjectSetter)(int); typedef struct _Object { int _m_data; fObjectGetter getter; fObjectSetter setter; } Object, *PObject; ``` 又或者你會看到比較成熟的作法: 範例三 ```c typedef struct _Object { int _m_data; } Object, *PObject; int ObjectGetter(PObject o); void ObjectSetter(PObject o, int in); ``` 這邊比較成熟的範例三作法,確實就是 C++ underlying 的作法。 其實你也會在其他語言中,發現這樣的作法,只是語法不同而已。 那麼 C\+\+ 的 this pointer 在做什麼事呢? 範例四 ```cpp struct Object { int _m_data; int getter() const; void setter(int); }; ``` 除了你可以在你的 member function 當中去描述「這個 instance」的 member data 之外。它還會隱藏在 member function 中的第一個變數: 你會看到 Object::getter 的 function signatute 像是: ```cpp int Object::getter(const Object *this) const; // already demangled ``` 同時,你也會觀察到 C\+\+ 的 sizeof(Object) 與範例三 C 語言的只有一個 data member 版本相同。 因為在 C\+\+ ,只有 data member 與 virtual functions 會被保存在 runtime instance 裡,其餘的 member functions 都可以看做事 global functions ! 因此 C\+\+ 物件的 instance 會像: ```mermaid classDiagram class Object { int data; } ``` 相對的,如果你觀察 C 語言的範例一、二,你會看到該 instance 會是: ```mermaid classDiagram class Object { int data; function pointer getter; function pointer setter; } ``` 所以如果是 32 位元電腦,會是 4 + 4 + 4 bytes,當然如果 `malloc` 聰明一點,有可能會給你多 4 bytes 的空間以利記憶體對齊。 如此可見 C++ 可以減少「共用」function pointer 開銷。 ### virtual functions 但,當你引入 virtual 函式之後 C++ instance 的 layout 就相當不同了 考慮下面的 範例五 ```cpp struct Object5 { int data; virtual void foo() {} }; ``` 這邊的 memory layout 則會因為你有 virtual function,進而需要動態變更 member function pointer,而引入 vtable ,長的像這樣 ```graphviz digraph G { node [shape=record]; struct_Object5 [label="{Object5 | +[1] data : int | +[0] vtable : ptr}"]; vtable_Object5 [label="{vtable_Object5 | +[0] : foo }"]; struct_Object5 -> vtable_Object5 [label="vtable"]; _ -> struct_Object5 [label="this"]; } ``` Object5 的 this pointer 顯而易見就是指向一個 Object5 instance 的指標 那麼我們可以把 Object5 instance 看做是一個一維的 array - 第 0 個偏移量是 vtable 的指標 - 第 1 個偏移量是 data 而一個 vtable 是另外一個共用的一維 array,由上自下,順序為該物件 virtual 的宣告順序。[^2] 詳細建議查看 《Inside the C++ Object Model》 一書,但其實筆者改篇會介紹。 [^2]: data model 我會有另外一篇介紹,因為講起來會有點多,更多可見 Inside the C++ Object Model 因此我們可以做到這 hack: ```cpp= #include <iostream> #if (UINTPTR_MAX / 255 % 255) #define pointer_type int #else #define pointer_type long long #endif struct A { int data; virtual int foo() { return 1024; } }; int main() { A *a = new A; a->data = 42; std::cout << ((pointer_type*)a)[1] << " "; auto fp = (int(*)(A*))((pointer_type**)a)[0][0]; std::cout << fp(a); delete a; } ``` 筆者提供 godbolt 給讀者自己操作 https://godbolt.org/z/s71EKGKbq 我們看到 line 20,先把 a 物件壓成 pointer type,從上面介紹可以知道第一個偏移量,就是 data,因此輸出會是 "42 " 看到 line 22,我們同樣可以從上面的介紹知道 instance 是一個一維陣列。第 0 個偏移量是 vtable 的指標 所以我們把 a 壓成指標的指標,對這取 0 找到 vtable 的指標,再取 0 找到 vtable 上面第 0 個元素,好,這就是 foo function pointer。 所以 invoke fp 就可以獲得 1024。 題外話,這邊其實也可以不用帶 a 參數,因為根據 x86, arm 等等的 calling convention ... 這說下去也太多了。改篇說! 以上,先這樣!

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully