
這一篇文章要探討的是“如何用算法實現十進制轉十六進制”並不涉及什么特別的知識點。屬於C語言基礎篇。
在翻找素材的時候,發現一篇以前寫的挺有意思的代碼,這篇代碼里面涉及的知識點沒有什么好講的,也沒有什么特別的邏輯概念在里面,僅僅只是想要分享一下個人對於編程方面的一些思考和堅持而已。
先看代碼:
#include <stdio.h>
#include <string.h>
int Judge(int n);
int Detection(void);
int main(void)
{
char x[16] = {'0','1','2','3','4','5','6','7','8',
'9','a','b','c','d','e','f'};//十進制與十六進制對應的數組
printf("請輸入一個十進制的數:");
int n = Detection();//輸入數據檢查再賦值
int k = Judge(n);//檢測數組需要的長度
char y[k];//創建數組儲存十六進制數
memset(y,'\0',sizeof(y));//清空數組
int i,j;
j = 0;
while( n >= 16 )//把轉換好的十六進制數依次輸入數組
{
i = (n % 16);//先是求余
y[j] = x[i];//把得到的余數轉為十六進制數(例如“11”轉“b”)
j++;//數組下標移位
n /= 16;//求商再賦值給它自己(方便下個循環再除)
if(n < 16)
y[j] = x[n];
}
//此時數組y內的十六進制數是倒過來儲存的
printf("你輸入的數轉換成十六進制為:0x");//先輸出“0x”前綴
while(j >= 0)//把儲存了十六進制數的數組倒着輸出
{
if(j > 0)//判斷是不是最后一個,是的話換行
printf("%c", y[j]);
else
printf("%c\n",y[j]);
j--;
}
return 0;
}
int Judge(int n)//這個函數的作用是用來判斷需要定義的數組大小的
{
int k = 1;
int m = n;
while(m > 16)//如果小於16,那么1位就行了
{
m /= 16;//如果大於16先除與16
k++;//加一位
}
return k;
}
int Detection(void)//這個是第一篇博客里面的那個代碼的封裝版,用來保證輸入的數為合法
{
int n;
while(1)
{
if(scanf("%d", &n) != 1 || getchar() != '\n')
{
printf("你輸入的數據有誤,請再輸一遍:");
while(getchar() != '\n');
}else
break;
}
return n;
}
這篇代碼所要實現的功能很簡單,就是把十進制轉換為十六進制輸出,當然也是有前提的,就是不能用那些轉換符(例如%x)或者用一些現有的函數,需要自己寫一個算法來實現轉換。至於這個算法也不難,無非就是了解一下在數學上面如何把十進制轉為十六進制,然后把那個過程用代碼來實現罷了,網上已經有很多說明了。十進制轉其他進制最常用的辦法就是不斷對其要求的進制數求余,然后余數反轉。
大概類似於這個樣子的過程(靈魂畫師附體)

這個過程在算法上面具體體現為先獲取用戶的十進制輸入,然后不斷的對其與16進行求商求余,把得到的余數反轉輸出,即為所要求的十六進制數。在這個過程中,由於需要把余數反轉輸出,沒法每求一個余數就輸出一個。所以就需要一個數組來存儲這些已經求得的余數,然后再反向輸出。在這整個輸出的過程中,所涉及算法的難度並不大。但如果僅僅只是這樣的話,我也沒有必要專門寫一篇文章來分享這個代碼了。

我在寫這個代碼的時候,遇到一個問題,是關於數組的。既然我在輸出之前需要把余數用數組儲存起來,那么我這個數組需要定義多大呢?由於用戶的輸入並不確定,所以最終所得到的十六進制數也不確定,這樣也就沒有辦法事先知道我需要的數組大小。定義大了浪費空間,定義小了又不夠放。當初在做這道題的時候,老師給我們的建議是定義個“char x[20]”就可以了。
我當時的第一反應就是,老師這是在給我們降低難度啊,這么隨便的嗎?萬一用戶輸入的數據轉換為十六進制之后超過
20位呢?且不說浪費不浪費空間的問題,你這明顯就是存在着bug啊!怎么也得要給個1024吧,20哪里夠了。不過后來想想,如今的操作系統也就64位,轉換成十六進制的話,也就16個數就可以表示完了,連20都給多了,四舍五入剛好取整嘛。而且也沒有bug的存在,也就浪費了4個字節的內存而已。要是我當時意識到這點,我可能就會直接“char x[16]”完事。也就沒有后面的什么事了。
不過在當時的我看了來,這簡直就是一個要逼死強迫症的bug啊!我可以容許浪費空間,也可以容許效率低下,但是絕不能放任bug不管啊。所以,我那天苦思冥想,最終認識到,我需要的是一個可變數組。這個數組要能夠實現我放多少東西進去它就能存多少東西,我拿多少東西出來它就能縮小多少。為了實現這個需求,我上網找了一下可變數組的實現方法。但是不外乎兩種情況,一種是在說C語言中沒有可變數組,另一種就是在用代碼實現可變數組。只可惜我當時的技術水平有限,實在是看不懂那些天書,而且那些大佬的代碼一長,就不寫注釋的了,通篇博客就一整篇代碼,一點介紹性的文字都沒有,別說我當時沒那個技術看不懂啊,我就是現在看的懂也沒耐心看你這么一整篇沒有注釋的代碼啊!簡直神煩(這也是我想要寫博客的一個原因)。
所以我當時在這模凌兩可的網絡環境下面,我認為是有可變數組的,只是藏着某個函數庫里面而已,只是屬於深度C而已,只是我還沒有學到而已,於是那一整個下午我就都在探索可變數組。
直到后來我才知道,在C語言里面,本身就不存在什么可變數組,在C++中倒是存在可變數組的概念,網絡上面的所謂可變數組,不過是利用了指針來存儲好原數組的內存位置然后當再次需要改變數組大小的時候,在原位置上面創建或者把原數據拷貝到新的內存地址上面再返回新地址的值給原指針而已。說白了,就是假的,假的。根本就不存在什么可變數組。C語言本身就不支持可變數組這個功能。
直到的最后,當然是沒有成功啦,本來就是不存在的東西,不過現在想想用儲存原數組地址這個方法來實現可變數組用在我這篇代碼中或許也很合適,雖然我用的方法是在數據輸入之后先判斷一下需要的數組大小,然后再創建數組的方法。不過那也是我當時的權宜之計而已,

有意思的是當初我在不知道C語言中有沒有可變數組的時候,曾想過用“指針對於非法內存的訪問”來實現可變數組。具體表現可以看下面的代碼
int main(void)
{
int i = 0;
char a[i];//根本沒有分配空間,用“i”代替0是為了防止編譯器報警告
char *c = a;//用一個指針來存儲數組“a”的地址值
int j = 3;
a[j] = '7';//這里用“j”來代替3是為了防止編譯器報警告
printf("%c ", c[3]);//這里用“c”來代替“a”輸出也是為了防止編譯器報警告
printf("\n");
return 0;
}
在這段代碼里面數組“a”我根本沒有分配空間給它,但是我卻給它第3個位置“a[3]”賦值,居然編譯通過了,運行也沒有問題。這就是利用了指針對於非法內存的訪問來實現的,而且這樣也可以滿足我的需求,我這個數組即沒有大小限制的而且也絲毫不浪費內存,這不就是我想要的可變數組嗎?
但別以為這是什么好東西,恰恰相反在編程開發的時候,我們應該要去杜絕這種情況的發生。這段代碼在不同的電腦下面或者在不同的程序區域下面運行都有可能不一樣,你可以嘗試把它封裝成一個獨立函數,然后試着在另外一篇小規模的代碼上面調用試試看,可能在程序的開始調用它,程序就會報段錯誤,也有可能會在程序的尾部調用它,程序就報段錯誤了。也有可能整個程序執行完到退出,也沒有報段錯誤。什么時候報段錯誤,完全取決於CPU在執行你這個程序的過程中什么時候訪問到你正在使用的那塊非法內存。
你的代碼一運行就報錯不可怕,最可怕的就是這種隨機性報錯的,你無法准確定位到你的錯誤位置。想象一下,你代碼原本運行的好好的,你突然想給它來個優化,就好像把原本的int形數據改為char形的節省一下空間,或者改一下別的無關痛癢的細節,改完之后吧,你一編譯運行,報錯了,你就找啊找啊找啊,就是找不到在哪里有錯誤。
然后你就很不甘心的把代碼又改回去,但是你忽略了一個細節,你原本是先定義一個char類型的數據“a”然后再定義一個int類型的數據“b”的,但是你為了好看,你在優化的時候把它們改成了先定義“b”再定義“a”了。但是你在改回去的過程中覺得不可能是這里的問題啊,就沒管了,結果編譯運行后,還是報錯。
這時候你對着整篇代碼從頭看到尾,又從尾看到頭,你看了好幾遍,但就是不知道到底是哪里出了問題,明明跟原來的代碼一摸一樣啊,怎么就還是報錯呢?這時候打死你也不會相信,就是因為你改變了那兩個變量的定義順序了,使得整個程序在內存中的儲存結構發生了改變,這就造成了程序在運行的過程中訪問到了原本不會訪問的內存,而那塊內存正是你用的非法內存。
所以當時我在把這個思路運用到代碼里面的時候,就出現了很多奇奇怪怪的問題,程序有時候行,有時候不行。有的時候甚至會出現同一個代碼在我的電腦上面運行還可以一放到別人的電腦上面就有不行了這種情況,更誇張的是有時候上午運行可以,下午運行又報錯了。搞了半天,放棄了。直到后來知道了野指針的概念之后,才漸漸知道為什么會出現那種情況。對於野指針的分析,以后有機會我可能還會單獨寫一篇文章來說明吧,也可能不會。雖然野指針還是挺重要的一個概念的,不過也挺簡單,好像也沒什么好寫的。
好了,就寫到這里吧,確實沒有什么知識點好說的,只是單純的想要分享一下以前的一些有趣的事情而已。而且如果你剛好需要做一道編程題叫做”請用算法編程實現十進制數轉XX進制數“的時候,這篇代碼剛好可以套用嘛!不過不建議抄作業啦!參考參考就可以了,畢竟真沒有什么難度的這題,注釋代碼里面都寫明白了,應該也不會存在看不懂的現象,過些天我會嘗試性的把這個例子改為用“儲存原數組地址來實現可變數組”的方法來實現,然后再更新上來。
最后附上這個例子的精簡版代碼:
#include <stdio.h>
#include <string.h>
int Judge(int n);
int Detection(void);
int main(void)
{
char x[16] = {'0','1','2','3','4','5','6','7','8',
'9','a','b','c','d','e','f'};//十進制與十六進制對應的數組
printf("請輸入一個十進制的數:");
int n = Detection();//輸入數據檢查再賦值
int k = Judge(n);//檢測數組需要的長度
char y[k];//創建數組儲存十六進制數
memset(y,'\0',sizeof(y));//清空數組
int i,j;
for(j=0; n > 0; j++)//把轉換好的十六進制數依次輸入數組
{
i = (n % 16);
y[j] = x[i];
n /= 16;
}
printf("你輸入的數轉換成十六進制為:0x");
while(j >= 0)//把儲存了十六進制數的數組倒着輸出
printf("%c", y[--j]);
printf("\n");
return 0;
}
int Judge(int n)//這個函數的作用是用來判斷需要定義的數組大小的
{
int k;
for(k=0; n>0; ++k)
n /= 16;
return k;
}
int Detection(void)//這個是第一篇博客里面的那個代碼的封裝版,用來保證輸入的數為合法
{
int n;
while(1)
{
if(scanf("%d", &n) != 1 || getchar() != '\n')
{
printf("你輸入的數據有誤,請再輸一遍:");
while(getchar() != '\n');
}else
break;
}
return n;
}
原博客始發於CSDN,在如今博客界的轉載抄襲泛濫的環境下,原創不易,點個贊再走唄。以下是博客首頁的鏈接。
