轉行做嵌入式也有一段時間了,原來做c#以及一些其它的上層語言, 本想的是也就是僅僅是語法上有點不一樣。但是實際使用的切身體會真的是只有自己才知道。很多方面刷新了我對c語言以及電腦結構體系的認知 ,絕對不僅僅是語法不一樣那麼簡單。 關於字元串傳遞函數引起的 一切源於給函數傳遞字元串變數這種 原來在其 ...
轉行做嵌入式也有一段時間了,原來做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相比真的是低一個級別的。諸多限制,更偏向於一種野人式的,要求你的功力要更高才能駕馭。也正是由於這種第一個級別 所有的這些不順手都不得不逼著你去想 底層到底是怎麼運作的。