javascript
DSP学习 -- cJSON使用教程
如需轉載請注明出處 https://juyou.blog.csdn.net/article/details/107870229
JSON 需要簡單的了解一下。誰知道啥時候用上呢~
一、JSON簡介
參看:JSON簡介
參看:cJSON使用詳細教程 | 一個輕量級C語言JSON解析器
參看:千萬不要點開,Cjson入門快速使用看這篇就夠了(CJSON入門操作以及問題講解,CJSON對象刪除出錯)
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易于人閱讀和編寫。同時也易于機器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON采用完全獨立于語言的文本格式,但是也使用了類似于C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成為理想的數據交換語言。
JSON建構于兩種結構:
“名稱/值”對的集合(A collection of name/value pairs)。不同的語言中,它被理解為對象(object),紀錄(record),結構(struct),字典(dictionary),哈希表(hash table),有鍵列表(keyed list),或者關聯數組 (associative array)。
值的有序列表(An ordered list of values)。在大部分語言中,它被理解為數組(array)。
這些都是常見的數據結構。事實上大部分現代計算機語言都以某種形式支持它們。這使得一種數據格式在同樣基于這些結構的編程語言之間交換成為可能。
JSON具有以下這些形式:
對象是一個無序的“‘名稱/值’對”集合。一個對象以 {左括號 開始, }右括號 結束。每個“名稱”后跟一個 :冒號 ;“‘名稱/值’ 對”之間使用 ,逗號 分隔。
數組是值(value)的有序集合。一個數組以 [左中括號 開始, ]右中括號 結束。值之間使用 ,逗號 分隔。
值(value)可以是雙引號括起來的字符串(string)、數值(number)、true、false、 null、對象(object)或者數組(array)。這些結構可以嵌套。
字符串(string)是由雙引號包圍的任意數量Unicode字符的集合,使用反斜線轉義。一個字符(character)即一個單獨的字符串(character string)。
字符串(string)與C或者Java的字符串非常相似。
數值(number)也與C或者Java的數值非常相似。除去未曾使用的八進制與十六進制格式。除去一些編碼細節。
空白可以加入到任何符號之間。 以下描述了完整的語言。
二、數據結構
cJSON使用cJSONstruct數據類型表示JSON數據:
/ * cJSON結構:* /typedef 結構 cJSON {struct cJSON *下一步;struct cJSON *上一頁;struct cJSON * child;INT類型;char * valuestring;/ *不建議寫入valueint,請改用cJSON_SetNumberValue * /int valueint;double valuedouble;字符 * string; } cJSON;此類型的項目表示JSON值。類型存儲type為位標記(這意味著您不能僅通過比較的值來找到類型type)。
要檢查項目的類型,請使用相應的cJSON_Is…功能。它會先執行NULL檢查,然后進行類型檢查,如果該項屬于此類型,則返回布爾值。
類型可以是以下之一:
-
cJSON_Invalid(選中cJSON_IsInvalid):表示一個不包含任何值的無效項目。如果將項目設置為全零字節,則將自動具有此類型。
-
cJSON_False(選中cJSON_IsFalse):表示false布爾值。您也可以使用來檢查布爾值cJSON_IsBool。
-
cJSON_True(選中cJSON_IsTrue):表示true布爾值。您也可以使用來檢查布爾值cJSON_IsBool。
-
cJSON_NULL(選中cJSON_IsNull):表示一個null值。
-
cJSON_Number(用選中cJSON_IsNumber):表示一個數字值。該值存儲為double in valuedouble和in valueint。如果數字超出整數范圍,INT_MAX或INT_MIN用于valueint。
-
cJSON_String(用選中cJSON_IsString):表示一個字符串值。它以零終止字符串的形式存儲在中valuestring。
-
cJSON_Array(用選中cJSON_IsArray):表示一個數組值。這是通過指向表示數組中值child的cJSON項目的鏈接列表來實現的。使用next和將元素鏈接在一起prev,其中第一個元素具有prev.next == NULL和最后一個元素next == NULL。
-
cJSON_Object(用選中cJSON_IsObject):表示一個對象值。對象的存儲方式與數組相同,唯一的區別是對象中的項將其鍵存儲在中string。
-
cJSON_Raw(選中cJSON_IsRaw):表示以JSON字符存儲的零終止形式的任何JSON valuestring。例如,可以使用它來避免一遍又一遍地打印相同的靜態JSON以節省性能。解析時,cJSON永遠不會創建此類型。另請注意,cJSON不會檢查其是否為有效JSON。
此外,還有以下兩個標志: -
cJSON_IsReference:指定child指向和/或valuestring不屬于該項目的項目,它僅是參考。因此,cJSON_Deleteand其他函數只會取消分配該項目,而不會取消分配child/ valuestring。
-
cJSON_StringIsConst:這意味著string指向一個常量字符串。這意味著cJSON_Delete和其他函數將不會嘗試解除分配string。
三、處理數據結構
對于每種值類型,都有一個cJSON_Create…函數可用于創建該類型的項目。所有這些都將分配一個cJSON結構,以后可以使用刪除該結構cJSON_Delete。請注意,您必須在某些時候將其刪除,否則會發生內存泄漏。
重要提示:如果您已經將項目添加到數組或對象中,則不能使用刪除它cJSON_Delete。將其添加到數組或對象將轉移其所有權,以便在刪除該數組或對象時也將其刪除。您還可以使用cJSON_SetValuestring來更改cJSON_String的valuestring,而無需valuestring手動釋放前一個。
基本類型
- 用創建空值cJSON_CreateNull
- 布爾與創建cJSON_CreateTrue,cJSON_CreateFalse或cJSON_CreateBool
- 用創建數字cJSON_CreateNumber。這將同時設置valuedouble和valueint。如果數字超出整數范圍,INT_MAX或者INT_MIN用于valueint
- 字符串是使用cJSON_CreateString(復制字符串)或使用cJSON_CreateStringReference(直接指向字符串。)創建的,valuestring因此不會被刪除,cJSON_Delete并且您將對其生命負責,這對常量很有用。
數組
您可以使用創建空數組cJSON_CreateArray。cJSON_CreateArrayReference可以用于創建不“擁有”其內容的數組,因此不會被刪除其內容cJSON_Delete。
要將項目添加到數組,請使用cJSON_AddItemToArray將項目追加到末尾。cJSON_AddItemReferenceToArray可以將使用元素添加為對另一個項目,數組或字符串的引用。這意味著cJSON_Delete不會刪除該項目child或valuestring屬性,因此,如果已經在其他地方使用過,則不會發生雙重釋放。要在中間插入項目,請使用cJSON_InsertItemInArray。它將在基于0的給定索引處插入一個項目,并將所有現有項目向右移動。
如果要從給定索引的數組中取出項目并繼續使用,請使用cJSON_DetachItemFromArray,它將返回分離的項目,因此請確保將其分配給指針,否則會發生內存泄漏。
用刪除項目cJSON_DeleteItemFromArray。它的工作方式類似于cJSON_DetachItemFromArray,但通過刪除了分離的項目cJSON_Delete。
您也可以替換數組中的項目。要么cJSON_ReplaceItemInArray使用索引或與cJSON_ReplaceItemViaPointer給定的一個指針的元素。如果失敗cJSON_ReplaceItemViaPointer將返回0。這在內部所做的是分離舊項目,將其刪除,然后將新項目插入其位置。
要獲取數組的大小,請使用cJSON_GetArraySize。使用cJSON_GetArrayItem給定的索引處得到的元素。
由于數組存儲為鏈接列表,因此通過索引對其進行迭代效率不高(O(n2)),因此您可以使用cJSON_ArrayForEach宏在O(n)時間復雜度上對數組進行迭代。
對象
您可以使用創建一個空對象cJSON_CreateObject。cJSON_CreateObjectReference可用于創建不“擁有”其內容的對象,因此不會通過刪除其內容cJSON_Delete。
要將項目添加到對象,請使用cJSON_AddItemToObject。使用cJSON_AddItemToObjectCS將項目添加到一個物體是一個常量或引用(該項目的密鑰,名稱string中的cJSON結構),以便它不會被釋放cJSON_Delete。cJSON_AddItemReferenceToArray可以將使用元素添加為對另一個對象,數組或字符串的引用。這意味著cJSON_Delete不會刪除該項目child或valuestring屬性,因此,如果已經在其他地方使用過,則不會發生雙重釋放。
如果要從對象中取出一個項目,請使用cJSON_DetachItemFromObjectCaseSensitive,它將返回分離的項目,因此請確保將其分配給指針,否則會發生內存泄漏。
用刪除項目cJSON_DeleteItemFromObjectCaseSensitive。它的工作原理是cJSON_DetachItemFromObjectCaseSensitive其次cJSON_Delete。
您還可以替換對象中的項目。要么用cJSON_ReplaceItemInObjectCaseSensitive使用密鑰或與cJSON_ReplaceItemViaPointer給定的一個指針的元素。如果失敗cJSON_ReplaceItemViaPointer將返回0。這在內部所做的是分離舊項目,將其刪除,然后將新項目插入其位置。
要獲取對象的大小,可以使用cJSON_GetArraySize,因為內部對象存儲為數組,所以可以使用。
如果要訪問對象中的項目,請使用cJSON_GetObjectItemCaseSensitive。
要遍歷對象,可以使用與cJSON_ArrayForEach數組相同的方式使用宏。
cJSON還提供了便捷的幫助器功能,用于快速創建新項目并將其添加到對象中,例如cJSON_AddNullToObject。他們返回一個指向新項目的指針,或者NULL如果它們失敗了。
四、解析JSON
在以零結尾的字符串中提供一些JSON的情況下,您可以使用進行解析cJSON_Parse。
cJSON * json = cJSON_Parse(string);給定字符串中的JSON(無論是否以零終止),您可以使用解析cJSON_ParseWithLength。
cJSON * json = cJSON_ParseWithLength(string,buffer_length);它將解析JSON并分配cJSON代表它的項目樹。返回后,您將完全負責將其與一起使用后進行分配cJSON_Delete。
cJSON_Parseis malloc和free默認情況下使用的分配器,但是可以使用進行更改(全局)cJSON_InitHooks。
如果發生錯誤,則可以使用來訪問輸入字符串中錯誤位置的指針cJSON_GetErrorPtr。不過,請注意這可能會產生競態條件在多線程的情況,在這種情況下,最好是使用cJSON_ParseWithOpts帶有return_parse_end。默認情況下,輸入字符串中已解析的JSON后面的字符將不被視為錯誤。
如果需要更多選項,請使用cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)。 return_parse_end返回指向輸入字符串中JSON末尾或錯誤發生位置的指針(從而以cJSON_GetErrorPtr線程安全的方式進行替換)。require_null_terminated,如果設置為,則1如果輸入字符串包含JSON之后的數據,則會導致錯誤。
如果需要更多選項來指定緩沖區長度,請使用cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)。
五、打印JSON
給定一個cJSON項目樹,您可以使用將它們打印為字符串cJSON_Print。
char * string = cJSON_Print(json);
它將分配一個字符串并在其中打印樹的JSON表示形式。一旦返回,您將完全負責與分配器一起使用后對其進行分配。(通常free,取決于設置的內容cJSON_InitHooks)。
cJSON_Print將使用空格打印以進行格式化。如果要打印而不格式化,請使用cJSON_PrintUnformatted。
如果您對結果字符串的大小有大致了解,可以使用cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)。fmt是一個布爾值,用于打開和關閉帶有空格的格式化。prebuffer指定用于打印的第一個緩沖區大小。cJSON_Print當前使用的第一個緩沖區大小為256個字節。一旦打印空間不足,將分配一個新的緩沖區,并在繼續打印之前復制舊的緩沖區。
使用可以完全避免這些動態緩沖區分配cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)。它需要一個緩沖區來指向要打印的指針及其長度。如果達到該長度,則打印將失敗并返回0。如果成功,1則返回。請注意,您應該提供比實際需要更多的5個字節,因為cJSON在估計提供的內存是否足夠時不是100%準確的。
六、示例
在此示例中,我們要構建并解析以下JSON:
{“ name ”:“ Awesome 4K ”,“ resolutions ”:[{“寬度”: 1280,“高度”: 720},{“寬度”: 1920,“高度”: 1080},{“寬度”: 3840,“高度”: 2160}] }列印
讓我們構建上面的JSON并將其打印為字符串:
另外,我們可以使用cJSON_Add…ToObject輔助函數使我們的生活更輕松一些:
//注意:返回堆分配的字符串,使用后需要釋放它。 char * create_monitor_with_helpers( void) {const unsigned int resolution_numbers [ 3 ] [ 2 ] = {{ 1280,720 },{ 1920,1080 },{ 3840,2160 }};char * string = NULL ;cJSON * resolutions = NULL ;size_t 索引 = 0 ;cJSON * monitor = cJSON_CreateObject();if(cJSON_AddStringToObject(監視器,“ name ”,“ Awesome 4K ”)== NULL){轉到結尾}分辨率= cJSON_AddArrayToObject(監視器,“分辨率”);如果(分辨率== NULL){轉到結尾}對于(index = 0 ; index <(sizeof(resolution_numbers)/(2 * sizeof(int))); ++ index){cJSON * resolution = cJSON_CreateObject();if(cJSON_AddNumberToObject(resolution,“ width ”,resolution_numbers [ index ] [ 0 ])== NULL){轉到結尾}if(cJSON_AddNumberToObject(resolution,“ height ”,resolution_numbers [ index ] [ 1 ])== NULL){轉到結尾}cJSON_AddItemToArray(分辨率,分辨率);}字符串= cJSON_Print(監視器);如果(string == NULL){fprintf(stderr,“無法打印監視器。\ n ”);}結束:cJSON_Delete(監視器);返回字符串; }解析中
在此示例中,我們將解析上述格式的JSON,并在打印某些診斷輸出時檢查監視器是否支持Full HD分辨率:
請注意,除了Result以外沒有NULL檢查,cJSON_Parse因為因為已經cJSON_GetObjectItemCaseSensitive檢查了NULL輸入,因此NULL僅傳播一個值cJSON_IsNumber,如果輸入為,則cJSON_IsString返回0 NULL
七、注意事項
零字符
cJSON不支持包含零字符’\0’或的字符串\u0000。對于當前的API,這是不可能的,因為字符串以零結尾。
字符編碼
cJSON僅支持UTF-8編碼的輸入。但是在大多數情況下,它不會拒絕無效的UTF-8作為輸入,而只是將其原樣傳播。只要輸入不包含無效的UTF-8,輸出將始終是有效的UTF-8。
C標準
cJSON用ANSI C(或C89,C90)編寫。如果您的編譯器或C庫未遵循此標準,則不能保證正確的行為。
注意:ANSI C不是C ++,因此不應使用C ++編譯器進行編譯。您可以使用C編譯器進行編譯,然后將其與C ++代碼鏈接。盡管可以使用C ++編譯器進行編譯,但不能保證正確的行為。
浮點數字
double除IEEE754雙精度浮點數外,cJSON不正式支持任何實現。它可能仍然可以與其他實現一起使用,但是這些實現的錯誤將被視為無效。
目前,cJSON支持的浮點文字的最大長度為63個字符。
數組和對象的深層嵌套
cJSON不支持嵌套太深的數組和對象,因為這會導致堆棧溢出。為了防止這種CJSON_NESTING_LIMIT情況,默認情況下,cJSON將深度限制為1000,但可以在編譯時進行更改。
線程安全
通常,cJSON 不是線程安全的。
但是,在以下情況下它是線程安全的:
- cJSON_GetErrorPtr永遠不會使用(可以改用return_parse_end參數cJSON_ParseWithOpts)
- cJSON_InitHooks 僅在任何線程中使用cJSON之前才被調用。
- setlocale 在返回對cJSON函數的所有調用之前,永遠不會調用它。
區分大小寫
最初創建cJSON時,它不遵循JSON標準,并且沒有區分大寫和小寫字母。如果您想要正確的,符合標準的行為,則需要使用CaseSensitive可用的功能。
復制對象成員
cJSON支持解析和打印JSON,該JSON包含具有多個具有相同名稱的成員的對象。cJSON_GetObjectItemCaseSensitive但是始終只會返回第一個。
二、下載使用
下載地址: https://github.com/DaveGamble/cJSON
因為整個庫只有一個C文件和一個頭文件,所以您只需復制cJSON.h并復制cJSON.c到項目源并開始使用它。
示例下載:https://github.com/whik/cJSON_Demo
/*Copyright (c) 2009 Dave GamblePermission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE. */#include <stdio.h> #include <stdlib.h> #include "cJSON.h"/* Parse text to JSON, then render back to text, and print! */ void doit(char *text) {char *out;cJSON *json;json=cJSON_Parse(text);if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());}else{out=cJSON_Print(json);cJSON_Delete(json);printf("%s\n",out);free(out);} }/* Read a file, parse, render back, etc. */ void dofile(char *filename) {FILE *f;long len;char *data;f=fopen(filename,"rb");fseek(f,0,SEEK_END);len=ftell(f);fseek(f,0,SEEK_SET);data=(char*)malloc(len+1);fread(data,1,len,f);fclose(f);doit(data);free(data); }/* Used by some code below as an example datatype. */ struct record {const char *precision;double lat,lon;const char *address,*city,*state,*zip,*country; };/* Create a bunch of objects as demonstration. */ void create_objects() {cJSON *root,*fmt,*img,*thm,*fld;char *out;int i; /* declare a few. *//* Our "days of the week" array: */const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};/* Our matrix: */int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};/* Our "gallery" item: */int ids[4]={116,943,234,38793};/* Our array of "records": */struct record fields[2]={{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};/* Here we construct some JSON standards, from the JSON site. *//* Our "Video" datatype: */root=cJSON_CreateObject(); cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());cJSON_AddStringToObject(fmt,"type", "rect");cJSON_AddNumberToObject(fmt,"width", 1920);cJSON_AddNumberToObject(fmt,"height", 1080);cJSON_AddFalseToObject (fmt,"interlace");cJSON_AddNumberToObject(fmt,"frame rate", 24);out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out); /* Print to text, Delete the cJSON, print it, release the string. *//* Our "days of the week" array: */root=cJSON_CreateStringArray(strings,7);out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);/* Our matrix: */root=cJSON_CreateArray();for (i=0;i<3;i++) cJSON_AddItemToArray(root,cJSON_CreateIntArray(numbers[i],3));/* cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);/* Our "gallery" item: */root=cJSON_CreateObject();cJSON_AddItemToObject(root, "Image", img=cJSON_CreateObject());cJSON_AddNumberToObject(img,"Width",800);cJSON_AddNumberToObject(img,"Height",600);cJSON_AddStringToObject(img,"Title","View from 15th Floor");cJSON_AddItemToObject(img, "Thumbnail", thm=cJSON_CreateObject());cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");cJSON_AddNumberToObject(thm,"Height",125);cJSON_AddStringToObject(thm,"Width","100");cJSON_AddItemToObject(img,"IDs", cJSON_CreateIntArray(ids,4));out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);/* Our array of "records": */root=cJSON_CreateArray();for (i=0;i<2;i++){cJSON_AddItemToArray(root,fld=cJSON_CreateObject());cJSON_AddStringToObject(fld, "precision", fields[i].precision);cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);cJSON_AddStringToObject(fld, "Address", fields[i].address);cJSON_AddStringToObject(fld, "City", fields[i].city);cJSON_AddStringToObject(fld, "State", fields[i].state);cJSON_AddStringToObject(fld, "Zip", fields[i].zip);cJSON_AddStringToObject(fld, "Country", fields[i].country);}/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */out=cJSON_Print(root); cJSON_Delete(root); printf("%s\n",out); free(out);}int main (int argc, const char * argv[]) {/* a bunch of json: */char text1[]="{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}"; char text2[]="[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n";char text4[]="{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http:/*www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": \"100\"\n },\n \"IDs\": [116, 943, 234, 38793]\n }\n }";char text5[]="[\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.7668,\n \"Longitude\": -122.3959,\n \"Address\": \"\",\n \"City\": \"SAN FRANCISCO\",\n \"State\": \"CA\",\n \"Zip\": \"94107\",\n \"Country\": \"US\"\n },\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.371991,\n \"Longitude\": -122.026020,\n \"Address\": \"\",\n \"City\": \"SUNNYVALE\",\n \"State\": \"CA\",\n \"Zip\": \"94085\",\n \"Country\": \"US\"\n }\n ]";/* Process each json textblock by parsing, then rebuilding: */doit(text1);doit(text2); doit(text3);doit(text4);doit(text5);/* Parse standard testfiles: */ /* dofile("../../tests/test1"); */ /* dofile("../../tests/test2"); */ /* dofile("../../tests/test3"); */ /* dofile("../../tests/test4"); */ /* dofile("../../tests/test5"); *//* Now some samplecode for building objects concisely: */create_objects();return 0; }如需轉載請注明出處 https://juyou.blog.csdn.net/article/details/107870229
總結
以上是生活随笔為你收集整理的DSP学习 -- cJSON使用教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DSP学习 -- Visual Stud
- 下一篇: 手把手教你写竞品分析