C 語言如何實現(xiàn)面向?qū)ο缶幊?/h1>

2018-Read-Record 記錄我的2018學(xué)習(xí)歷程

1. 具體和抽象

具體:客觀存在著的或在認(rèn)識中反映出來的事物的整體托酸,是具有多方面屬性团秽、特點订晌、關(guān)系的統(tǒng)一迄委;
抽象:從具體事物中被抽取出來的相對獨立的各個方面、屬性圾亏、關(guān)系等十拣。

以 Person 為例:“pmst”,“numbbbbb”志鹃,“MM”等都是客觀存在的夭问,稱之為具體;然后我們抽取共同的特性:姓名曹铃,性別缰趋,年齡和介紹自己等(當(dāng)然這是極小、極小的一部分)陕见。

2. C 語言抽象的雛形

先用 C 語言抽象埠胖,實現(xiàn)如下:

typedef struct Person Person;

typedef void (*Method)(Person *my_self);

typedef struct Person {
    char name[12];
    int age;
    int sex;
    Method behavior1; // 行為1
} Person;


void selfIntroducation(Person *my_self) {
    printf("my name is %s,age %d,sex %d\n",my_self->name,my_self->age,my_self->sex);
}

int main(int argc, const char * argv[]) {
    // 1
    Person *pmst = (Person *)malloc(sizeof(Person));
    // 1.1
    strcpy(pmst->name, "pmst");
    pmst->age = 18;
    pmst->sex = 0;
    // 2
    pmst->behavior1 = selfIntroducation;
    // 3
    pmst->behavior1(pmst);

    return 0;
}
  1. intfloat淳玩,struct 等類型在編譯之后轉(zhuǎn)變成對內(nèi)存地址的訪問直撤,比如 1 中的 pmst 指針在調(diào)用 malloc 方法后返回分配的地址為 0x12345678,會標(biāo)識占 sizeof(Person) 個字節(jié)蜕着;pmst->age = 18 其實是對 0x12345678 偏移 12 字節(jié)內(nèi)存的賦值谋竖,占4個字節(jié);
  2. 函數(shù)在編譯之后放置在代碼段承匣,入口為函數(shù)指針蓖乘;
  3. pmst->behavior1(pmst); 先取到 0x12345678 偏移 20 字節(jié)內(nèi)存的值————函數(shù)指針,然后 call 命令調(diào)用

編譯之后代碼都變成了對內(nèi)存地址的訪問韧骗,稱之為靜態(tài)綁定嘉抒;那么該如何實現(xiàn) Runtime 運行時的動態(tài)訪問呢?比如在UI界面上(ps:Terminal那種古老的輸入輸出方式也是OK的)輸入一個類的名稱以及調(diào)用方法名稱袍暴,緊接著我們要實例化一個該類的對象些侍,然后調(diào)用方法。

3. C 語言實現(xiàn)動態(tài)性

3.1 運行時如何實現(xiàn)抽象->具體

想要運行時隨心所欲地將抽象轉(zhuǎn)變成具體政模,就需要在內(nèi)存中保存一份對抽象的描述岗宣,這里的描述并非指 typedef struct Person {...}Person 定義 ———— 這是靜態(tài)的,而是開辟一塊內(nèi)存加載一份 json 抽象描述:

{
  "Name": "Person",
  "VariableList":[
    {
      "VarName":"name",
      "Type":"char[]",
      "MemorySize":12,
    },
    {
      "VarName":"age",
      "Type":"int",
      "MemorySize":4,
    },
    {
      "VarName":"sex",
      "Type":"int",
      "MemorySize":4,
    },
  ],
  "MethodList":[
    {
      "name":"selfIntroducation",
      "methodAddress":0x12345678
    },
  ]
}

關(guān)于這串json描述淋样,可以是在編譯階段生成的耗式,運行時使用 char *description 加載到堆內(nèi)存上,需要時通過對應(yīng)的 Key 取到 Value:例如 Key=Name 可以取到類名,Key=VariableList 可以取到變量列表刊咳,Key=MethodList 可以取到方法列表彪见。這里可能需要有個小小的Parser解析器。

3.2 二次抽象娱挨,生成類對象

倘若每次用到時就要進(jìn)行一次 char *description 信息 parser 解析余指,性能這關(guān)都過不去,正確做法是解析成某個數(shù)據(jù)結(jié)構(gòu)让蕾,保存到堆內(nèi)存中:

// 偽代碼如下
typedef struct Variable {
  char *name;
  char *type;
  int memorySize;
}Variable;

typedef struct Method {
  char *name;
  void (*callAddress)(void *my_self);// 顯然這里多參傳入 當(dāng)然這些暫時不考慮
}Method;

typedef struct Class {
  char *className;
  /// Variable List 是一個數(shù)組 類型為 Variable
  Variable *variableList;
  /// Method List 也是一個數(shù)組 類型為 Method
  Method *methodList
}

上述是最簡單的抽象定義,現(xiàn)在我們將解析json信息或听,然后分配內(nèi)存填充信息(抽象->具體)的過程探孝,首先 Class 是一個抽象概念,抽象出類名誉裆、變量列表和方法列表等信息顿颅,但此刻我們開辟了一塊內(nèi)存填充信息———— 客觀存在了,稱之為對象(通常我們稱之為class object足丢,類對象)

//////////////偽代碼如下/////////////////
//////////////////////////////////////////
// parse person json proccess get result
///////////////////end////////////////////

// 分配一塊內(nèi)存
Class *personClsObject = (Class *)malloc(sizeof(Class));

strcpy(personClsObject->className, "Person");

personClsObject->variableList = (Variable *)malloc(sizeof(Variable) * 3);// 有3個變量

Variable *nameVar = (Variable *)malloc(sizeof(Variable));
strcpy(nameVar->name, "name");
strcpy(nameVar->type, "char[]");
nameVar->memorySize = 12;
//... ageVar & sexVar 生成

personClsObject->variableList[0] = nameVar;
personClsObject->variableList[1] = ageVar;
personClsObject->variableList[2] = sexVar;

// 同理生成一個個Method 然后填充到 personClsObject->methodList;

你粱腻、我、他是客觀存在的稱之為對象斩跌,進(jìn)一步抽象出了姓名绍些、性別和年齡三個方面,使用 struct Person 結(jié)構(gòu)體定義耀鸦,之前說了編譯之后不存在所謂的結(jié)構(gòu)體柬批,都會轉(zhuǎn)而變成對內(nèi)存地址的訪問;我們換了種思路袖订,又抽象出了一個 Class 結(jié)構(gòu)體氮帐,然后分配一塊具體客觀存在的內(nèi)存保存信息,即 personClsObject 類對象(class object)洛姑,然后將所有變量信息存儲到variableList上沐,方法信息存儲到 methodList

舉一反三楞艾,如果繼續(xù)定義typedef struct Animal参咙,typedef struct Car 等一系列的類,那么必定也會各自在堆內(nèi)存上生成有且僅有一個 AnimalClsObject 硫眯、CarClsObject 類對象昂勒!

3.2 使用類對象來生成實例對象

上文說到內(nèi)存中保存了一份對 Person 抽象描述,即PersonClsObject類對象舟铜,包含類名稱戈盈,變量列表,方法列表等信息。此刻進(jìn)一步具體到現(xiàn)實生活中某個具體的人塘娶,生成 “pmst” 博主我归斤,"numbbbbb" 幫主梁杰,“mm”靈魂畫師刁岸,有種God創(chuàng)世的趕腳脏里。這一個個都是現(xiàn)實存在的,即實例對象————自然要分配一塊內(nèi)存給各自虹曙,填充 name名字迫横,sex性別,age年齡酝碳。

[圖片上傳失敗...(image-3d0165-1519970136607)]

///////////// 以下為偽代碼 ////////////////////

// 可以遍歷 personClsObject 中variableList所有變量
// 取到每個變量所占的內(nèi)存大小memorySize矾踱,累加得到總的需要分配的內(nèi)存大小
int size = 0;
for variable in personClsObject->variableList {
  size = variable->memorySize;// 當(dāng)然這里肯定要考慮內(nèi)存對齊問題
}

Person *pmst =  (Person *)malloc(size);   // 分配內(nèi)存 得到指針0x10000000
Person *numbbbb = (Person *)malloc(size); // 分配內(nèi)存 得到指針0x10001000
Person *MM = (Person *)malloc(size);      // 分配內(nèi)存 得到指針0x10002000

note: 這里只為實例變量分配了內(nèi)存,章節(jié)2中我們還包含一個8字節(jié)的函數(shù)指針疏哗,那么問題來了呛讲,現(xiàn)在我們該如何調(diào)用selfIntroducation函數(shù)呢?

3.3 實例對象和類對象

因為我們在內(nèi)存中保存了一份對 Person 的抽象描述返奉,在運行時就知道Person包含哪些允許調(diào)用的函數(shù)名稱贝搁,函數(shù)類型以及位于代碼段的函數(shù)入口地址。

章節(jié) 2 中使用了 pmst->behavior1(pmst) 調(diào)用方式 :先取到函數(shù)指針芽偏,然后把實例對象自身指針作為傳參傳入調(diào)用±啄妫現(xiàn)在有了 personClsObject 我們又該如何實現(xiàn)這種調(diào)用呢?

/// 偽代碼如下
/// C語言函數(shù)返回類型為函數(shù)指針寫法如下:
/// ps:當(dāng)然也可以先typedef 然后替換返回類型污尉,
void (*findMethod(char *methodName))(void *myself) {
  for method in personClsObject->methodList {
    if methodName == method->name {
      return method->callAddress;
    }
  }
  return NULL;
}

可以看到我們會通過傳入函數(shù)名稱关面,遍歷類對象中的方法列表進(jìn)行匹配,找到函數(shù)指針十厢,接下去就是和章節(jié)2調(diào)用一樣等太。

void (*call)(void *) = findMethod("selfIntroducation");
call(pmst);

現(xiàn)在的運行時動態(tài)性方案存在很多缺陷,隨便舉幾點:

  1. 實例對象會有很多個蛮放,但是對應(yīng)的類對象有且僅有一個缩抡,因為類對象是一份抽象描述,一個足矣包颁。但是你會發(fā)現(xiàn)實例對象和類對象并沒有聯(lián)系在一起瞻想,導(dǎo)致我們得到一個實例對象無法運行時得知其屬于什么類(對應(yīng)哪個 class object 類對象)!這也是后面我們要解決的娩嚼;
  2. 存在太多的硬編碼蘑险,比如 findMethod 寫死了是從 personClsObject 中去遍歷方法列表

小總結(jié):1.實例對象允許很多個,但是對應(yīng)的類對象有且僅有一個岳悟,運行時保存在堆上佃迄;2.類對象是一份抽象描述泼差,我們可以在運行通過查詢類對象,拿到關(guān)于類的信息呵俏,比如第一個變量名稱堆缘,占字節(jié)數(shù),變量類型等等普碎,拿到這些信息可以幫助我們實際訪問實例對象指針指向內(nèi)存中的數(shù)據(jù)啦吼肥!—————— 因為我們知道字節(jié)偏移和變量類型。

3.4 改進(jìn):實例對象關(guān)聯(lián)類對象

3.3節(jié)中我們僅考慮只有一個personClsObject麻车,并且在 findMethod 查詢函數(shù)中也硬編碼寫死了是從 Person 類對象方法列表中遍歷匹配缀皱,現(xiàn)在開始加入不同的類對象,findMethod 只需要新增一個入?yún)⒓纯?

/// 分離硬編碼部分动猬,傳入 `Class *classObject` 不同的類對象
void (*findMethod(Class *classObject, char *methodName))(void *myself) {
  for method in classObject->methodList {
    if methodName == method->name {
      return method->callAddress;
    }
  }
  return NULL;
}
/// 現(xiàn)在調(diào)用方式改為:
void (*call)(void *) = findMethod(personClsObject, "selfIntroducation");
call(pmst);

但是這樣實現(xiàn) findMethod 的前提是知道 pmst 這個實例對象對應(yīng)的類對象為 personClsObject啤斗,單純拿到指向?qū)嵗龑ο髢?nèi)存的指針(0x1000 0000)顯然信息不足:

[圖片上傳失敗...(image-e11e38-1519970136607)]

試想知曉一個實例對象的指針 0x1000,0000,指針類型為 void * 枣察,我們可以訪問這塊內(nèi)存的數(shù)據(jù)争占,但是問題來了:

  1. 這個實例對象到底占幾個字節(jié)呢燃逻?
  2. 內(nèi)存布局怎樣————比如第一個成員類型是Int序目,要讀入4個字節(jié),ps:這里可能要考慮內(nèi)存對齊問題伯襟;
  3. 我們依舊不知道這個實例對象對應(yīng)的類對象是哪個猿涨,或者說類對象所占的內(nèi)存地址是多少。

正如第三點指出姆怪,問題根本在于我們的實例對象沒有綁定類對象的內(nèi)存地址叛赚!這樣問題就很好解決了,我們只需在內(nèi)存頭部“塞入”類對象的指針就OK了稽揭,假設(shè) personClsObject 類對象地址為0x2000 0000

—————————————————                 ——————————————————————————————————————————————
|   0x2000 0000 -|--------------->|     "Person"(char *className)              |
|_______________ |                |____________________________________________|
|   "pmst"       |                |     0x2000 1000(Variable *variableList)    |  
|     26         |                |____________________________________________|
|     0          |                |     0x2000 2000(Method *methodList)        |
—————————————————                 |____________________________________________|  

其中 0x2000,1000 0x02000,2000 都是指針俺附,分別指向變量列表和方法列表內(nèi)存地址。

這樣的結(jié)構(gòu)意味著要修改 Person 的結(jié)構(gòu)體:

typedef struct Person {
    Class *clsObject;
    char name[12];
    int age;
    int sex;
} Person;

////////// 偽代碼(前提我們已經(jīng)得到了person 類對象) /////////////
int size = 0;
for variable in personClsObject->variableList {
  size = variable->memorySize;// 當(dāng)然這里肯定要考慮內(nèi)存對齊問題
}
Person *pmst =  (Person *)malloc(size + 8); // 因為多了一個指針溪掀,32位平臺占4字節(jié) 64位平臺占8字節(jié)
pmst->clsObject = personClsObject;
//...

這樣就可以解決我們之前的問題了事镣,給一個實例變量的指針,我們先取到內(nèi)存首地址開始的8個字節(jié)揪胃,解釋成 Class * 指針指向了我們的類對象璃哟,愉快地獲取想要的所有信息。

3.5 關(guān)于實例對象

不過問題來了喊递,上述實現(xiàn)必須在抽象出來的數(shù)據(jù)結(jié)構(gòu)頂部插入一個 Class *clsObject 随闪,如下:

typedef struct Person {
    Class *clsObject; // 指向 personClsObject 類對象
    char name[12];
    int age;
    int sex;
} Person;

typedef struct Car {
    Class *clsObject; // 指向 carClsObject 類對象
    char brand[12];
    int color; 
    int EngineDisplacement;
    //...
} Car;

//... 還有其他很多抽象類定義

不同類的實例對象聲明如下:

// Person 實例對象:pmst numbbbb
Person *pmst = (Person *)malloc(person_size);
pmst->clsObject = personClsObject;

Person *numbbbb = (Person *)malloc(person_size);
numbbbb->clsObject = personClsObject;

// Car 實例對象:pmst's bmw  & benz 以下表示客觀存在的兩輛車 
Car *bmw_pmst = (Car *)malloc(car_size);
bmw_pmst->clsObject = carClsObject;

Car *benz_pmst = (Car *)malloc(car_size);
benz_pmst->clsObject = carClsObject;

盡管 pmst numbbbb bmw_pmst benz_pmst 都是指針,但是指向類型分別是 PersonCar 結(jié)構(gòu)體骚勘,那么在此種情況下铐伴,我們使用能夠使用一種統(tǒng)一的方式來定義一個實例對象呢?

觀察上述實例對象聲明以及抽象類的定義,我們找出相同點:數(shù)據(jù)結(jié)構(gòu)頂部都為 Class *clsObject 指針盛杰。

struct object {
  Class *clsObject; 
};

struct object *pmst = (Person *)malloc(person_size)
pmst->clsObject = personClsObject;

struct object *bmw_pmst = (Car *)malloc(car_size);
bmw_pmst->clsObject = carClsObject;

PersonCar 后面的成員挽荡,我們無法使用 -> 訪問了,轉(zhuǎn)而變成查詢各自的類對象中 variableList 變量列表————變量類型和地址偏移量即供。這樣可以間接訪問pmst這個實例指向內(nèi)存內(nèi)容了(當(dāng)然內(nèi)存前8個字節(jié)保存的是類對象指針)定拟。

至于為什么能這么做,先來說說C語言實現(xiàn)變長結(jié)構(gòu)體逗嫡。

struct Data   
{  
    int length;  
    char buffer[0];  
};

結(jié)構(gòu)體中青自,length 其實表示分配的內(nèi)存大小,而buffer是一個空數(shù)組驱证,可理解為占位名稱罷了延窜;buffer的真實地址緊隨 Data 結(jié)構(gòu)體包含數(shù)據(jù)之后,可以看到這個結(jié)構(gòu)體僅占4個字節(jié)抹锄,倘若我們在malloc時候分配了100個字節(jié)逆瑞,那么剩下100-4=96個字節(jié)就是屬于 buffer 數(shù)組,非常巧妙不是嗎伙单?

char str[10] = "pmst demo";
Data *data = (Data *)malloc(sizeof(Data) + 10); 
data->length = 10;
memcpy(data->data,str, 10); 

回歸正題获高,現(xiàn)在我們可以使用 struct object 結(jié)構(gòu)來統(tǒng)一指向我們的實例對象了,但是這并不意味著我們不需要PersonCar類的定義吻育,只不過現(xiàn)在抽象數(shù)據(jù)結(jié)構(gòu)體的頂部不需要嵌入 Class *clsObject了念秧。

3.6 改寫實例對象的分配方式

前文demo中是這么實例化一個對象的:

/// ...

Person *pmst = (Person *)malloc(person_size)
pmst->clsObject = personClsObject;

/// ...

PersonClsObject 知道 Person 的一切。

(Person *)malloc(person_size) 方式實例化一個對象布疼,首先是要拿到 personClsObject 對象摊趾,然后遍歷 variableList 累加所有變量占的內(nèi)存得到 person_size,最后調(diào)用 malloc 方法開辟內(nèi)存返回指針游两。

分配一塊內(nèi)存給person實例對象 這種行為屬于person類對象的職責(zé)砾层,因此將這種行為添加到 personClsObject 類對象的 methodList 中(其實細(xì)細(xì)想來,是不太恰當(dāng)?shù)募福筮€會繼續(xù)改進(jìn))肛炮,命名為 mallocInstance

/// 簡單修改下定義 真實定義結(jié)構(gòu)名稱改為了 `Person_IMP`
typedef struct object Person;

struct Person_IMP {
    char name[12];
    int age;
    int sex;
}

/// 增加一個分配內(nèi)存的方法 注意傳參為實例對象 對于當(dāng)前方法來說應(yīng)該傳NULL
(struct object *)mallocIntance(void *myself) {
  /// 偽代碼
  int size = 0;
  for variable in personClsObject->variableList {
    size = variable->memorySize;// 當(dāng)然這里肯定要考慮內(nèi)存對齊問題
  }
  return (struct object *)malloc(size);
}
personClsObject->methodList[xx]=mallocInstance;

/// 分配內(nèi)存改寫如下:
void (*mallocIntance)(void *) = findMethod(personClsObject, "mallocIntance");
Person *pmst = mallocIntance(NULL); // 之前是要傳一個實例對象進(jìn)去轰坊,為了方便操作铸董,但是分配內(nèi)存比較特殊,要知道此刻連實例都不存在!

3.8 對象調(diào)用函數(shù)方式的思考

實例對象函數(shù)調(diào)用過程: findMethod 傳入對應(yīng)的類對象和函數(shù)名稱肴沫,遍歷 methodList 找到匹配項返回函數(shù)指針粟害,傳入實例對象指針調(diào)用即可,譬如之前person實例調(diào)用自我介紹方法的demo颤芬。

/// 現(xiàn)在調(diào)用方式改為:
void (*call)(void *) = findMethod(personClsObject, "selfIntroducation");
call(pmst);

那么問題來了悲幅,實例對象的屬性變量如何修改呢套鹅?比如name,agesex。現(xiàn)在已經(jīng)不能像最開始之前那樣直接訪問內(nèi)存地址進(jìn)行修改了汰具,盡管personClsObject知道這些變量的信息:變量名稱卓鹿,類型以及所占內(nèi)存大小。

其實解決方案也很簡單留荔,既然不能直接訪問變量吟孙,間接總可以把!

Any problem in computer science can be solved by anther layer of indirection.

現(xiàn)在為每個屬性變量都創(chuàng)建一個讀寫方法聚蝶,通過調(diào)用這兩個方法來修改實例對象內(nèi)存中的變量值(Note:前8個字節(jié)保存的是類對象地址)

void setName(void *my_self, char *newName) {}
char *getName(void *my_self) {}

注意到不同函數(shù)的傳參個數(shù)也不同杰妓,如 selfIntroducation 傳參僅 void *my_self一個,而 setName 方法傳參個數(shù)為2碘勉。這其實是ok巷挥,Method封裝的是個函數(shù)指針(占4或8個字節(jié)),指向某塊代碼段的地址验靡,C語言又是支持可變參數(shù)的函數(shù)倍宾,原理自行g(shù)oogle關(guān)鍵字"C語言 函數(shù) 可變參數(shù)"。

這里講下我的理解胜嗓,函數(shù)其實就是封裝好的代碼塊高职,編譯之后轉(zhuǎn)成一串指令保存在代碼段。

  • 關(guān)于函數(shù)調(diào)用:正常的調(diào)用方式 functionName(variable1,variable2,variable3)兼蕊,編譯器會把functionName替換成函數(shù)地址(0x12345678)初厚,匯編可能是使用類似 call 0x12345678 來調(diào)用函數(shù)件蚕;
  • 關(guān)于函數(shù)入?yún)崿F(xiàn):variable1 variable2 variable3孙技,應(yīng)該是會調(diào)用 push 指令一個個入棧(這里注意是先 push variable1 還是 push variable3 是由ABI決定的!)
  • 如果說函數(shù)是指令排作,那么棧就是給函數(shù)提供數(shù)據(jù)的源牵啦!函數(shù)實現(xiàn)是一串指令,使用pushpop操作棧上的數(shù)據(jù)妄痪,拿上面的函數(shù)入?yún)碚f哈雏,我們就使用 push 命令將variable1 variable2 variable3壓到棧里,其中 ebp 寄存器指向當(dāng)前函數(shù)調(diào)用上下文的棧 base address衫生,而esp寄存器則是根據(jù)pushpop改變指針地址裳瘪,一開始ebpesp指針都是指向 base address。

[圖片上傳失敗...(image-f28fc2-1519970136607)]

上面就是簡單的一個調(diào)用方式罪针,至于variable1這些函數(shù)入?yún)⑷绾稳∨砀瑧?yīng)該是依靠 ebp+ offset得到。

3.9 對象調(diào)用函數(shù)改進(jìn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末泪酱,一起剝皮案震驚了整個濱河市派殷,隨后出現(xiàn)的幾起案子还最,更是在濱河造成了極大的恐慌,老刑警劉巖毡惜,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拓轻,死亡現(xiàn)場離奇詭異,居然都是意外死亡经伙,警方通過查閱死者的電腦和手機(jī)扶叉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帕膜,“玉大人辜梳,你說我怎么就攤上這事∮镜” “怎么了作瞄?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長危纫。 經(jīng)常有香客問我宗挥,道長,這世上最難降的妖魔是什么种蝶? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任契耿,我火速辦了婚禮,結(jié)果婚禮上螃征,老公的妹妹穿的比我還像新娘搪桂。我一直安慰自己,他們只是感情好盯滚,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布踢械。 她就那樣靜靜地躺著,像睡著了一般魄藕。 火紅的嫁衣襯著肌膚如雪内列。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天背率,我揣著相機(jī)與錄音话瞧,去河邊找鬼。 笑死寝姿,一個胖子當(dāng)著我的面吹牛交排,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饵筑,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼埃篓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翻翩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎况毅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞椎,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年睛低,在試婚紗的時候發(fā)現(xiàn)自己被綠了案狠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡钱雷,死狀恐怖骂铁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情罩抗,我是刑警寧澤拉庵,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站套蒂,受9級特大地震影響钞支,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜操刀,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一烁挟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骨坑,春花似錦撼嗓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匈辱,卻和暖如春振湾,著一層夾襖步出監(jiān)牢的瞬間杀迹,已是汗流浹背亡脸。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留树酪,地道東北人浅碾。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像续语,于是被迫代替她去往敵國和親垂谢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容