Redis原理

一、SDS介紹

Redis沒有使用C語言傳統(tǒng)的字符串表示(以空字符結(jié)尾的字符串?dāng)?shù)組,以下簡稱C字符串),?而是自己構(gòu)建了一種名為簡單動(dòng)態(tài)字符串(simple dynamic string,SDS)的抽象類型厂捞,并將SDS作為Redis默認(rèn)的字符串表示。

在 Redis 里面, C字符串只會(huì)作為字符串字面量(string literal)主卫, 用在一些無須對(duì)字符串值進(jìn)行修改的地方, 比如打印日志:

redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");

當(dāng) Redis 需要的不僅僅是一個(gè)字符串字面量鹃愤, 而是一個(gè)可以被修改的字符串值時(shí)簇搅,?Redis 就會(huì)使用 SDS 來表示字符串值: 比如在 Redis 的數(shù)據(jù)庫里面, 包含字符串值的鍵值對(duì)在底層都是由 SDS 實(shí)現(xiàn)的软吐。

舉個(gè)例子瘩将, 如果客戶端執(zhí)行命令:

redis>SET msg "hello world"

OK

那么 Redis 將在數(shù)據(jù)庫中創(chuàng)建了一個(gè)新的鍵值對(duì), 其中:

????鍵值對(duì)的鍵是一個(gè)字符串對(duì)象, 對(duì)象的底層實(shí)現(xiàn)是一個(gè)保存著字符串?"msg"?的 SDS 姿现。

????鍵值對(duì)的值也是一個(gè)字符串對(duì)象肠仪, 對(duì)象的底層實(shí)現(xiàn)是一個(gè)保存著字符串?"hello?world"?的 SDS 。

又比如說备典, 如果客戶端執(zhí)行命令:

redis>RPUSH fruits "apple" "banana" "cherry"

(integer) 3

那么 Redis 將在數(shù)據(jù)庫中創(chuàng)建一個(gè)新的鍵值對(duì)异旧, 其中:

????鍵值對(duì)的鍵是一個(gè)字符串對(duì)象, 對(duì)象的底層實(shí)現(xiàn)是一個(gè)保存了字符串?"fruits"?的 SDS 提佣。

????鍵值對(duì)的值是一個(gè)列表對(duì)象吮蛹, 列表對(duì)象包含了三個(gè)字符串對(duì)象, 這三個(gè)字符串對(duì)象分別由三個(gè) ????SDS 實(shí)現(xiàn): 第一個(gè) SDS 保存著字符串?"apple"?拌屏, 第二個(gè) SDS 保存著字符串?"banana"?潮针, 第三個(gè) ????SDS 保存著字符串?"cherry"?。

除了用來保存數(shù)據(jù)庫中的字符串值之外倚喂, SDS 還被用作緩沖區(qū)(buffer): AOF 模塊中的 AOF 緩沖區(qū)然低, 以及客戶端狀態(tài)中的輸入緩沖區(qū), 都是由 SDS 實(shí)現(xiàn)的务唐。

每個(gè)?sds.h/sdshdr?結(jié)構(gòu)表示一個(gè) SDS 值:

struct sdshdr {

// 記錄 buf 數(shù)組中已使用字節(jié)的數(shù)量

// 等于 SDS 所保存字符串的長度

int len;

// 記錄 buf 數(shù)組中未使用字節(jié)的數(shù)量

int free;

// 字節(jié)數(shù)組雳攘,用于保存字符串

char buf[];

};

下圖展示了一個(gè) SDS 示例:

????free?屬性的值為?0?, 表示這個(gè) SDS 沒有分配任何未使用空間枫笛。

????len?屬性的值為?5?吨灭, 表示這個(gè) SDS 保存了一個(gè)五字節(jié)長的字符串。

????buf?屬性是一個(gè)?char?類型的數(shù)組刑巧, 數(shù)組的前五個(gè)字節(jié)分別保存了?'R'?喧兄、?'e'?、?'d'?啊楚、?'i'?吠冤、?'s'?五個(gè)字符, 而最后一個(gè)字節(jié)則保存了空字符?'\0'?恭理。


SDS 遵循 C 字符串以空字符結(jié)尾的慣例拯辙, 保存空字符的?1?字節(jié)空間不計(jì)算在 SDS 的?len?屬性里面, 并且為空字符分配額外的?1?字節(jié)空間颜价, 以及添加空字符到字符串末尾等操作都是由 SDS 函數(shù)自動(dòng)完成的涯保, 所以這個(gè)空字符對(duì)于 SDS 的使用者來說是完全透明的。

遵循空字符結(jié)尾這一慣例的好處是周伦, SDS 可以直接重用一部分 C 字符串函數(shù)庫里面的函數(shù)夕春。

舉個(gè)例子, 如果我們有一個(gè)指向下圖所示 SDS 的指針?s?专挪, 那么我們可以直接使用?stdio.h/printf?函數(shù)及志, 通過執(zhí)行以下語句:

printf("%s", s->buf);

根據(jù)傳統(tǒng)片排, C 語言使用長度為?N+1?的字符數(shù)組來表示長度為?N?的字符串, 并且字符數(shù)組的最后一個(gè)元素總是空字符?'\0'?速侈。

比如說划纽,?下圖就展示了一個(gè)值為?"Redis"?的 C 字符串:


C 語言使用的這種簡單的字符串表示方式, 并不能滿足 Redis 對(duì)字符串在安全性锌畸、效率勇劣、以及功能方面的要求,接下來的內(nèi)容將詳細(xì)對(duì)比 C 字符串和 SDS 之間的區(qū)別潭枣, 并說明 SDS 比 C 字符串更適用于 Redis 的原因比默。

因?yàn)?C 字符串并不記錄自身的長度信息, 所以為了獲取一個(gè) C 字符串的長度盆犁, 程序必須遍歷整個(gè)字符串命咐, 對(duì)遇到的每個(gè)字符進(jìn)行計(jì)數(shù), 直到遇到代表字符串結(jié)尾的空字符為止谐岁, 這個(gè)操作的復(fù)雜度為?O(N)?醋奠。

和 C 字符串不同, 因?yàn)?SDS 在?len?屬性中記錄了 SDS 本身的長度伊佃, 所以獲取一個(gè) SDS 長度的復(fù)雜度僅為?O(1)?窜司。

舉個(gè)例子, 對(duì)于下圖所示的 SDS 來說航揉, 程序只要訪問 SDS 的?len?屬性塞祈, 就可以立即知道 SDS 的長度為?5?字節(jié):


設(shè)置和更新 SDS 長度的工作是由 SDS 的 API 在執(zhí)行時(shí)自動(dòng)完成的, 使用 SDS 無須進(jìn)行任何手動(dòng)修改長度的工作帅涂。

通過使用 SDS 而不是 C 字符串议薪, Redis 將獲取字符串長度所需的復(fù)雜度從?O(N)?降低到了?O(1)?, 這確保了獲取字符串長度的工作不會(huì)成為 Redis 的性能瓶頸媳友。

比如說斯议, 因?yàn)樽址I在底層使用 SDS 來實(shí)現(xiàn), 所以即使我們對(duì)一個(gè)非常長的字符串鍵反復(fù)執(zhí)行?STRLEN?命令醇锚, 也不會(huì)對(duì)系統(tǒng)性能造成任何影響哼御, 因?yàn)?STRLEN?命令的復(fù)雜度僅為?O(1)?。

除了獲取字符串長度的復(fù)雜度高之外搂抒, C 字符串不記錄自身長度帶來的另一個(gè)問題是容易造成緩沖區(qū)溢出(buffer overflow)艇搀。

舉個(gè)例子尿扯,?<string.h>/strcat?函數(shù)可以將?src?字符串中的內(nèi)容拼接到?dest?字符串的末尾:

char *strcat(char *dest,const char *src);

因?yàn)?C 字符串不記錄自身的長度求晶, 所以?strcat?假定用戶在執(zhí)行這個(gè)函數(shù)時(shí), 已經(jīng)為?dest?分配了足夠多的內(nèi)存衷笋, 可以容納?src?字符串中的所有內(nèi)容芳杏, 而一旦這個(gè)假定不成立時(shí)矩屁, 就會(huì)產(chǎn)生緩沖區(qū)溢出。

舉個(gè)例子爵赵, 假設(shè)程序里有兩個(gè)在內(nèi)存中緊鄰著的 C 字符串?s1?和?s2?吝秕, 其中?s1?保存了字符串?"Redis"?, 而?s2?則保存了字符串?"MongoDB"?空幻, 如下圖所示烁峭。


如果一個(gè)程序員決定通過執(zhí)行:

strcat(s1," Cluster");

將?s1?的內(nèi)容修改為?"Redis?Cluster"?, 但粗心的他卻忘了在執(zhí)行?strcat?之前為?s1?分配足夠的空間秕铛, 那么在?strcat?函數(shù)執(zhí)行之后约郁,?s1?的數(shù)據(jù)將溢出到?s2?所在的空間中, 導(dǎo)致?s2?保存的內(nèi)容被意外地修改但两, 如下圖所示。


與 C 字符串不同, SDS 的空間分配策略完全杜絕了發(fā)生緩沖區(qū)溢出的可能性: 當(dāng) SDS API 需要對(duì) SDS 進(jìn)行修改時(shí)进鸠, API 會(huì)先檢查 SDS 的空間是否滿足修改所需的要求各拷, 如果不滿足的話, API 會(huì)自動(dòng)將 SDS 的空間擴(kuò)展至執(zhí)行修改所需的大小紧阔, 然后才執(zhí)行實(shí)際的修改操作坊罢, 所以使用 SDS 既不需要手動(dòng)修改 SDS 的空間大小, 也不會(huì)出現(xiàn)前面所說的緩沖區(qū)溢出問題擅耽。

舉個(gè)例子艘绍, SDS 的 API 里面也有一個(gè)用于執(zhí)行拼接操作的?sdscat?函數(shù), 它可以將一個(gè) C 字符串拼接到給定 SDS 所保存的字符串的后面秫筏, 但是在執(zhí)行拼接操作之前诱鞠,?sdscat?會(huì)先檢查給定 SDS 的空間是否足夠, 如果不夠的話这敬,?sdscat?就會(huì)先擴(kuò)展 SDS 的空間航夺, 然后才執(zhí)行拼接操作。

比如說崔涂, 如果我們執(zhí)行:

sdscat(s, " Cluster");

其中 SDS 值?s?如下圖所示阳掐, 那么?sdscat?將在執(zhí)行拼接操作之前檢查?s?的長度是否足夠, 在發(fā)現(xiàn)?s?目前的空間不足以拼接?"?Cluster"?之后冷蚂,?sdscat?就會(huì)先擴(kuò)展?s?的空間缭保, 然后才執(zhí)行拼接?"?Cluster"?的操作, 拼接操作完成之后的 SDS 如下圖所示蝙茶。


正如前面所說艺骂, 因?yàn)?C 字符串并不記錄自身的長度, 所以對(duì)于一個(gè)包含了?N?個(gè)字符的 C 字符串來說隆夯, 這個(gè) C 字符串的底層實(shí)現(xiàn)總是一個(gè)?N+1?個(gè)字符長的數(shù)組(額外的一個(gè)字符空間用于保存空字符)钳恕。

因?yàn)?C 字符串的長度和底層數(shù)組的長度之間存在著這種關(guān)聯(lián)性别伏, 所以每次增長或者縮短一個(gè) C 字符串, 程序都總要對(duì)保存這個(gè) C 字符串的數(shù)組進(jìn)行一次內(nèi)存重分配操作:

如果程序執(zhí)行的是增長字符串的操作忧额, 比如拼接操作(append)厘肮, 那么在執(zhí)行這個(gè)操作之前, 程序需要先通過內(nèi)存重分配來擴(kuò)展底層數(shù)組的空間大小 —— 如果忘了這一步就會(huì)產(chǎn)生緩沖區(qū)溢出睦番。

如果程序執(zhí)行的是縮短字符串的操作类茂, 比如截?cái)嗖僮鳎╰rim), 那么在執(zhí)行這個(gè)操作之后托嚣, 程序需要通過內(nèi)存重分配來釋放字符串不再使用的那部分空間 —— 如果忘了這一步就會(huì)產(chǎn)生內(nèi)存泄漏大咱。

舉個(gè)例子, 如果我們持有一個(gè)值為?"Redis"?的 C 字符串?s?注益, 那么為了將?s?的值改為?"Redis?Cluster"?碴巾, 在執(zhí)行:

strcat(s, " Cluster");

之前, 我們需要先使用內(nèi)存重分配操作丑搔, 擴(kuò)展?s?的空間厦瓢。

之后, 如果我們又打算將?s?的值從?"Redis?Cluster"?改為?"Redis?Cluster?Tutorial"?啤月, 那么在執(zhí)行:

strcat(s, " Tutorial");

之前煮仇, 我們需要再次使用內(nèi)存重分配擴(kuò)展?s?的空間, 諸如此類谎仲。

因?yàn)閮?nèi)存重分配涉及復(fù)雜的算法浙垫, 并且可能需要執(zhí)行系統(tǒng)調(diào)用, 所以它通常是一個(gè)比較耗時(shí)的操作:

在一般程序中郑诺, 如果修改字符串長度的情況不太常出現(xiàn)夹姥, 那么每次修改都執(zhí)行一次內(nèi)存重分配是可以接受的。

但是 Redis 作為數(shù)據(jù)庫辙诞, 經(jīng)常被用于速度要求嚴(yán)苛辙售、數(shù)據(jù)被頻繁修改的場(chǎng)合, 如果每次修改字符串的長度都需要執(zhí)行一次內(nèi)存重分配的話飞涂, 那么光是執(zhí)行內(nèi)存重分配的時(shí)間就會(huì)占去修改字符串所用時(shí)間的一大部分旦部, 如果這種修改頻繁地發(fā)生的話, 可能還會(huì)對(duì)性能造成影響较店。

為了避免 C 字符串的這種缺陷士八, SDS 通過未使用空間解除了字符串長度和底層數(shù)組長度之間的關(guān)聯(lián): 在 SDS 中,?buf?數(shù)組的長度不一定就是字符數(shù)量加一梁呈, 數(shù)組里面可以包含未使用的字節(jié)婚度, 而這些字節(jié)的數(shù)量就由 SDS 的?free?屬性記錄。

通過未使用空間捧杉, SDS 實(shí)現(xiàn)了空間預(yù)分配和惰性空間釋放兩種優(yōu)化策略陕见。

空間預(yù)分配

空間預(yù)分配用于優(yōu)化 SDS 的字符串增長操作: 當(dāng) SDS 的 API 對(duì)一個(gè) SDS 進(jìn)行修改秘血, 并且需要對(duì) SDS 進(jìn)行空間擴(kuò)展的時(shí)候味抖, 程序不僅會(huì)為 SDS 分配修改所必須要的空間评甜, 還會(huì)為 SDS 分配額外的未使用空間。

其中仔涩, 額外分配的未使用空間數(shù)量由以下公式?jīng)Q定:

如果對(duì) SDS 進(jìn)行修改之后忍坷, SDS 的長度(也即是?len?屬性的值)將小于?1?MB?, 那么程序分配和?len?屬性同樣大小的未使用空間熔脂, 這時(shí) SDS?len?屬性的值將和?free?屬性的值相同佩研。 舉個(gè)例子, 如果進(jìn)行修改之后霞揉, SDS 的?len?將變成?13?字節(jié)旬薯, 那么程序也會(huì)分配?13字節(jié)的未使用空間, SDS 的?buf?數(shù)組的實(shí)際長度將變成?13?+?13?+?1?=?27?字節(jié)(額外的一字節(jié)用于保存空字符)适秩。

如果對(duì) SDS 進(jìn)行修改之后绊序, SDS 的長度將大于等于?1?MB?, 那么程序會(huì)分配?1?MB?的未使用空間秽荞。 舉個(gè)例子骤公, 如果進(jìn)行修改之后, SDS 的?len?將變成?30?MB?扬跋, 那么程序會(huì)分配?1?MB?的未使用空間阶捆, SDS 的?buf?數(shù)組的實(shí)際長度將為?30?MB?+?1?MB?+?1?byte?。

通過空間預(yù)分配策略钦听, Redis 可以減少連續(xù)執(zhí)行字符串增長操作所需的內(nèi)存重分配次數(shù)洒试。

舉個(gè)例子, 對(duì)于下圖所示的 SDS 值?s?來說朴上, 如果我們執(zhí)行:

sdscat(s, " Cluster");

那么?sdscat?將執(zhí)行一次內(nèi)存重分配操作儡司, 將 SDS 的長度修改為?13?字節(jié), 并將 SDS 的未使用空間同樣修改為?13?字節(jié)余指。


如果這時(shí)捕犬, 我們?cè)俅螌?duì)?s?執(zhí)行:

sdscat(s, " Tutorial");

那么這次?sdscat?將不需要執(zhí)行內(nèi)存重分配: 因?yàn)槲词褂每臻g里面的?13?字節(jié)足以保存?9?字節(jié)的?"?Tutorial"?, 執(zhí)行?sdscat?之后的 SDS 如圖?


在擴(kuò)展 SDS 空間之前酵镜, SDS API 會(huì)先檢查未使用空間是否足夠碉碉, 如果足夠的話, API 就會(huì)直接使用未使用空間淮韭, 而無須執(zhí)行內(nèi)存重分配垢粮。

通過這種預(yù)分配策略, SDS 將連續(xù)增長?N?次字符串所需的內(nèi)存重分配次數(shù)從必定?N?次降低為最多?N?次靠粪。

惰性空間釋放

惰性空間釋放用于優(yōu)化 SDS 的字符串縮短操作: 當(dāng) SDS 的 API 需要縮短 SDS 保存的字符串時(shí)蜡吧, 程序并不立即使用內(nèi)存重分配來回收縮短后多出來的字節(jié)毫蚓, 而是使用?free?屬性將這些字節(jié)的數(shù)量記錄起來, 并等待將來使用昔善。

舉個(gè)例子元潘,?sdstrim?函數(shù)接受一個(gè) SDS 和一個(gè) C 字符串作為參數(shù), 從 SDS 左右兩端分別移除所有在 C 字符串中出現(xiàn)過的字符君仆。

比如對(duì)于下圖所示的 SDS 值?s?來說翩概, 執(zhí)行:

sdstrim(s, "XY");????// 移除 SDS 字符串中的所有 'X' 和 'Y'


注意執(zhí)行?sdstrim?之后的 SDS 并沒有釋放多出來的?8?字節(jié)空間, 而是將這?8?字節(jié)空間作為未使用空間保留在了 SDS 里面返咱, 如果將來要對(duì) SDS 進(jìn)行增長操作的話钥庇, 這些未使用空間就可能會(huì)派上用場(chǎng)。

舉個(gè)例子咖摹, 如果現(xiàn)在對(duì)?s?執(zhí)行:

sdscat(s, " Redis");

那么完成這次?sdscat?操作將不需要執(zhí)行內(nèi)存重分配: 因?yàn)?SDS 里面預(yù)留的?8?字節(jié)空間已經(jīng)足以拼接?6?個(gè)字節(jié)長的?"?Redis"?评姨, 如下圖所示。


通過惰性空間釋放策略萤晴, SDS 避免了縮短字符串時(shí)所需的內(nèi)存重分配操作吐句, 并為將來可能有的增長操作提供了優(yōu)化。

與此同時(shí)硫眯, SDS 也提供了相應(yīng)的 API 蕴侧, 讓我們可以在有需要時(shí), 真正地釋放 SDS 里面的未使用空間两入, 所以不用擔(dān)心惰性空間釋放策略會(huì)造成內(nèi)存浪費(fèi)净宵。

二、字符串對(duì)象

字符串對(duì)象的編碼可以是?int?裹纳、?raw?或者?embstr?择葡。

如果一個(gè)字符串對(duì)象保存的是整數(shù)值, 并且這個(gè)整數(shù)值可以用?long?類型來表示剃氧, 那么字符串對(duì)象會(huì)將整數(shù)值保存在字符串對(duì)象結(jié)構(gòu)的?ptr屬性里面(將?void*?轉(zhuǎn)換成?long?)敏储, 并將字符串對(duì)象的編碼設(shè)置為?int?。

舉個(gè)例子朋鞍, 如果我們執(zhí)行以下?SET?命令已添, 那么服務(wù)器將創(chuàng)建一個(gè) int?編碼的字符串對(duì)象作為?number?鍵的值:

redis>SET number 10086?

OK

redis>OBJECT ENCODING number

"int"

如果字符串對(duì)象保存的是一個(gè)字符串值, 并且這個(gè)字符串值的長度大于?39?字節(jié)滥酥, 那么字符串對(duì)象將使用一個(gè)簡單動(dòng)態(tài)字符串(SDS)來保存這個(gè)字符串值更舞, 并將對(duì)象的編碼設(shè)置為?raw?。

舉個(gè)例子坎吻, 如果我們執(zhí)行以下命令缆蝉, 那么服務(wù)器將創(chuàng)建一個(gè)如 raw?編碼的字符串對(duì)象作為?story?鍵的值:

redis>SET story "Long, long, long ago there lived a king ..."

OK

redis>STRLEN story

(integer) 43

redis>OBJECT ENCODING story

"raw"

如果字符串對(duì)象保存的是一個(gè)字符串值, 并且這個(gè)字符串值的長度小于等于?39?字節(jié), 那么字符串對(duì)象將使用?embstr?編碼的方式來保存這個(gè)字符串值刊头。

embstr?編碼是專門用于保存短字符串的一種優(yōu)化編碼方式黍瞧, 這種編碼和?raw?編碼一樣, 都使用?redisObject?結(jié)構(gòu)和?sdshdr?結(jié)構(gòu)來表示字符串對(duì)象原杂, 但?raw?編碼會(huì)調(diào)用兩次內(nèi)存分配函數(shù)來分別創(chuàng)建?redisObject?結(jié)構(gòu)和?sdshdr?結(jié)構(gòu)印颤, 而?embstr?編碼則通過調(diào)用一次內(nèi)存分配函數(shù)來分配一塊連續(xù)的空間, 空間中依次包含?redisObject?和?sdshdr?兩個(gè)結(jié)構(gòu)污尉, 如下圖所示膀哲。


embstr?編碼的字符串對(duì)象在執(zhí)行命令時(shí)往产, 產(chǎn)生的效果和?raw?編碼的字符串對(duì)象執(zhí)行命令時(shí)產(chǎn)生的效果是相同的被碗, 但使用?embstr?編碼的字符串對(duì)象來保存短字符串值有以下好處:

embstr?編碼將創(chuàng)建字符串對(duì)象所需的內(nèi)存分配次數(shù)從?raw?編碼的兩次降低為一次。

釋放?embstr?編碼的字符串對(duì)象只需要調(diào)用一次內(nèi)存釋放函數(shù)仿村, 而釋放?raw?編碼的字符串對(duì)象需要調(diào)用兩次內(nèi)存釋放函數(shù)锐朴。

因?yàn)?embstr?編碼的字符串對(duì)象的所有數(shù)據(jù)都保存在一塊連續(xù)的內(nèi)存里面, 所以這種編碼的字符串對(duì)象比起?raw?編碼的字符串對(duì)象能夠更好地利用緩存帶來的優(yōu)勢(shì)蔼囊。

作為例子焚志, 以下命令創(chuàng)建了一個(gè)?embstr?編碼的字符串對(duì)象作為?msg?鍵的值, 值對(duì)象的樣子如下圖所示:

redis>SET msg "hello"

OK

redis>OBJECT ENCODING msg?

"embstr"


最后要說的是畏鼓, 可以用?long?double?類型表示的浮點(diǎn)數(shù)在 Redis 中也是作為字符串值來保存的: 如果我們要保存一個(gè)浮點(diǎn)數(shù)到字符串對(duì)象里面酱酬, 那么程序會(huì)先將這個(gè)浮點(diǎn)數(shù)轉(zhuǎn)換成字符串值, 然后再保存起轉(zhuǎn)換所得的字符串值云矫。

舉個(gè)例子膳沽, 執(zhí)行以下代碼將創(chuàng)建一個(gè)包含?3.14?的字符串表示?"3.14"?的字符串對(duì)象:

redis>SET pi 3.14

OK

redis>OBJECT ENCODING pi

"embstr"

在有需要的時(shí)候, 程序會(huì)將保存在字符串對(duì)象里面的字符串值轉(zhuǎn)換回浮點(diǎn)數(shù)值让禀, 執(zhí)行某些操作, 然后再將執(zhí)行操作所得的浮點(diǎn)數(shù)值轉(zhuǎn)換回字符串值巡揍, 并繼續(xù)保存在字符串對(duì)象里面痛阻。

舉個(gè)例子, 如果我們執(zhí)行以下代碼的話:

redis>INCRBYFLOAT pi 2.0

"5.14"

redis>OBJECT ENCODING pi

"embstr"

那么程序首先會(huì)取出字符串對(duì)象里面保存的字符串值?"3.14"?腮敌, 將它轉(zhuǎn)換回浮點(diǎn)數(shù)值?3.14?阱当, 然后把?3.14?和?2.0?相加得出的值?5.14?轉(zhuǎn)換成字符串?"5.14"?, 并將這個(gè)?"5.14"?保存到字符串對(duì)象里面糜工。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弊添,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子啤斗,更是在濱河造成了極大的恐慌表箭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異免钻,居然都是意外死亡彼水,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門极舔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凤覆,“玉大人,你說我怎么就攤上這事拆魏《㈣耄” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵渤刃,是天一觀的道長拥峦。 經(jīng)常有香客問我,道長卖子,這世上最難降的妖魔是什么略号? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮洋闽,結(jié)果婚禮上玄柠,老公的妹妹穿的比我還像新娘。我一直安慰自己诫舅,他們只是感情好羽利,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刊懈,像睡著了一般这弧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俏讹,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天当宴,我揣著相機(jī)與錄音,去河邊找鬼泽疆。 笑死户矢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殉疼。 我是一名探鬼主播梯浪,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瓢娜!你這毒婦竟也來了挂洛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤眠砾,失蹤者是張志新(化名)和其女友劉穎虏劲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柒巫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年励堡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堡掏。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡应结,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泉唁,到底是詐尸還是另有隱情鹅龄,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布亭畜,位于F島的核電站扮休,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贱案。R本人自食惡果不足惜肛炮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一止吐、第九天 我趴在偏房一處隱蔽的房頂上張望宝踪。 院中可真熱鬧,春花似錦碍扔、人聲如沸瘩燥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厉膀。三九已至,卻和暖如春二拐,著一層夾襖步出監(jiān)牢的瞬間服鹅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工百新, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留企软,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓饭望,卻偏偏與公主長得像仗哨,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铅辞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345