轉行做嵌入式也有一段時間了,原來做c#以及一些其它的上層語言, 本想的是也就是僅僅是語法上有點不一樣,但是實際使用的切身體會真的是只有自己才知道,很多方面重繪了我對c語言以及計算機結構體系的認知 ,絕對不僅僅是語法不一樣那么簡單,
關于字串傳遞函式引起的
一切源于給函式傳遞字串變數這種 原來在其它高級語言地方寫的 再常見不過的功能,
1 void changeStr(char * ch) 2 { 3 *(ch + 1) = 'a'; 4 } 5 6 //char * cstr = "hello";//報錯 7 char cstr[] = "hello";//這種可以 8 //陣列也是傳的參考函式里面更改的是此處陣列的內容這個不用多說 9 changeStr(cstr); 10 printf(cstr);
當然對于這個問題的bug 你百度一下就會有很多帖子 ,基本上呢也會給你講的大概差不多,如果非要死記硬背的話這種代碼:
1 char * ch="hello";
他是以常量的形式存在于環境中的,具體的請看:
https://www.qb5200.com/article/405141.html
https://blog.csdn.net/u013066730/article/details/84231452
https://blog.csdn.net/silently_frog/article/details/96607516
總而言之 char * ch=“hello” 然后再去更改ch里的元素 是一個陷阱,至于這個"hello" 的地址大意估計是由編譯器更底層的控制的我們不用管,
又啰嗦一遍 記憶體地址和資料
歸根結底還是 指標的問題,記憶體地址 值 ,學了單片機后深深的認識到這 其實這就是計算機 匯編 ,還有編程的本質,本質就是記憶體管理 ,到處的計算機書 作業系統 或者其它方面講的也都是這個,記憶體資料處理,單片機入門就是地址上的記憶體資料處理,c語言中處理字串總是會遇到各種各樣的問題也是因為沒理解透,
還是回傳c語言字串的議題上來吧
c語言字串的本質:需要一段連續地址的值 以\0結尾,處理的時候不要超出這個概念,以前在技術群里討論也模糊的記得 有這個說法 ,上面的"hello"編譯后是作為靜態字串存盤的,在各處如果寫的一模一樣的"hello"那么他們在記憶體里本身就是同一個東西, "hello" 這種以引號引起來的 表示方式 它本質就代表了一個地址標識,另外一個"hello" 這種形式的寫法編譯器末尾會自動給帶一個\0結尾 來確定字串結束, 這個你是看不見 但他是存在的,再貼一遍字串的代碼:
1 char * str2 = "hello";
一些坑以及產生的原因(指標與分配確切空間):
而c語言指標不允許你指標隨便指向一個未初始化的 地址,并不允許你一個指標隨意指向一個地址 然后去修改 值 ,這是不被允許的 ,千言萬語 歸咎于此的根本原因,
1 char ch1 = 0x65; 2 char ch2 = 0x66; 3 char ch3 = 0x00; 4 5 char str1[] = "hello";//{ ch1, ch2, ch3 }這種寫法意思是一樣的 6 char * str2 = "hello"; 7 str1[1] = 'a'; 8 //*(str1 + 1) = 'a';陣列指標式操作 老套路 9 10 //str2[1] = 'a';//報錯 11 //*(str2 + 1) = 'a';//我們想當然的指標地址++ 進行操作是不行的 12 //str2 = &ch1;//這種是可以 但是指向單個字符地址 并不能形成連續的字串 13 14 printf("%s--\r\n", str1); 15 printf("%s--\r\n", str2);//字串引數給的是地址 16 printf("%c--\r\n", *str2);//注意字符引數給的是*p
這也是為什么陣列字串 能夠 更改元素 (陣列的記憶體空間是確切的),char* (記憶體空間不確切) ,所以不能隨意更改,一切都歸咎于記憶體管理, 使用記憶體時必須必須必須要固定的確切的 經過分配了的 確定的 空間,然后另一個 他們的這種指標的機制是相同的 所以 陣列可以用 *p 的形式來訪問,
1 int a = 44;
這個 a看似平平無奇 ,實際上這是一個記憶體空間確定的程序 , a代表了一個地址,編譯程序只不過把這個程序變成了對應的匯編指令,
字串不就是連續地址的值以\0結尾嗎 ,于是乎有些大聰明就有了這種代碼:
1 char ch1 = 'a'; 2 char * str1; 3 char * str2; 4 char * str3; 5 6 str1 = &ch1; 7 str2 = str1 + 1; 8 9 //這樣更是錯誤,指標只能指向一個 給一個地址值 10 //char * str2 = { 'h', 'e', '\0' }; 11 12 //還想到 不是連續的指標 么 陣列么 ,于是大聰明 這樣 13 //又犯了迂回的錯誤 ,陣列 里面 每一個元素都是指標 ,這其實是一個二維陣列, 14 //char * str1[]
單片機中除了有定義的外設暫存器以外 ,普通區域 誰允許你擅自通過地址+1的方式 去訪問未知的區域的,而單獨一個char 一個char 的定義 變數又不可能 給你分配到連續的區域 讓你char*去關聯上,這是由于c語言本身的機制決定的 ,所以我們就不要去專這個牛角尖了,
也正是由于指標跟陣列的相關性 ,指標跟陣列有時候 實參 形參可以互相換,比如最開始的代碼 形參定義的是指標 確可傳陣列,定義的是陣列也可以傳指標 都是可以的 是等價的 ,都相安無事 ,前提是你要自己眼睛看仔細了 傳進去的是一個char * 的話 如果你函式內部 進行了元素更改的話 你就掛了,
因為這這些缺陷種種限制, 所以微量氧里到處都是用的陣列 形式的字串,陣列就是一串連續確切空間地址的指標,
關于回傳一個陣列
當然 通過單片機函式不能回傳陣列 ,只可回傳指標 ,回傳一個指標么指向那里?函式大括號里的部分在函式運行結束就會被銷毀,去哪里找 ,
1 char * getChPointer() 2 { 3 //如果想使此函式起作用可以把cc定義成全域的 4 //static char cc = 'b'; 5 char cc = 'b'; 6 char * ch; 7 ch = &cc; 8 //其實c這種單細胞難用玩意兒,也沒啥動態記憶體分配不動態記憶體分配 9 //單片機編程中根本就沒用到動態記憶體也就是堆,管理太復雜了, 10 11 //到此方法體內所有的都是復制的 ,包括指標 則復制一模一樣的指標 12 //每次呼叫方法都是獨立副本運行,方法結束堆疊結束 所有內容銷毀 13 14 //當堆疊結束時回傳的是復制的跟ch一模一樣的指標 都指向cc所以沒區別 15 return ch; 16 } 17 char * ch = getChPointer(); 18 printf("dd\r\n"); 19 printf("%c", *ch);
c語言 真的非常非常的低能 單細胞動物 ,深深的感受到c 語言為什么僅僅只是匯編的抽象 ,真是一種單細胞思考方式 ,因為真的是在是太低級了 ,跟其他什么Java相比真的是低一個級別的,諸多限制,更偏向于一種野人式的,要求你的功力要更高才能駕馭,也正是由于這種第一個級別 所有的這些不順手都不得不逼著你去想 底層到底是怎么運作的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501704.html
標籤:其他
