2016-02-27 186 views
-3

編輯問題

我明白了我在原始問題中給出的代碼中的錯誤,並且我得到的字符是垃圾字符。雖然,我仍然在C約垃圾字符的幾個問題:C中的垃圾字符

  • 爲什麼不能字符enter image description here被複制?

  • 垃圾人物有一些模式嗎?這意味着你能預測一個空字符串會出現什麼樣的字符,一個空的整數什麼時候會出現,等等。

  • 當一個變量被聲明時,爲什麼它有一個垃圾字符,而不是空白?有沒有將它與垃圾字符一起存儲的特定原因?

  • 對於沒有空終止的字符串,是否會在每個操作系統上打印相同的垃圾字符?如果是,哪一個?

  • 每個操作系統上都有相同的垃圾字符嗎?或者它們不同?

  • 有沒有辦法在C/C++的stdout緩衝區中打印這些字符?

  • 如果您在字符enter image description here中仔細看,那裏有一些字符和數字。他們代表什麼?

  • 有沒有可以用C/C++打印的垃圾字符列表?



原始的問題

原題的題目:用C神祕字符輸出

我以K & [R碰到過這樣的代碼:

int scanline (char str [], int lim)              /* Line will be read in 'str []', while lim is the maximum characters to be read */ 
{ 
    int c, len, j;                  /* 'len' will have the length of the read string */ 

    j = 0;                    /* Initializing 'j' */ 
    for (len = 0; (c = getchar()) != EOF && c != '\n'; ++len)       /* Reading a character one by one, till the user enters '\n', and checking for failure of 'getchar' */ 
    { 
     if (len < (lim -2))                /* Checking that string entered has not gone beyond it's boundaries. '-2' for '\n' and '\0' */ 
     { 
      str [j] = c;                 /* Copying read character into 'string [j]' */ 
      ++ j;                  /* Incrementing 'j' by 1 */ 
     } 
    } 
    if (c == '\n')                  /* Checking if user has finished inputting the line */ 
    { 
     str [j] = c;                 /* Copying newline into string */ 
     ++j; 
     ++ len; 
    } 

    return len;                   /* Returning number of characters read */ 
} 

進入K & R,它被稱爲getline,但我所做的更改,添加的評論,並且因此將其定義爲scanline。爲了驗證這一點,我做了一個演示程序:

#include <mocl/cancel.h> 

int main (int argc, char **argv) 
{ 
    int len; 
    char str [50]; 
    len = scanline (str, 50); 
    printf ("len = %d\n str = %s\n", len, str); 
    return 0; 
} 

所需的頭和功能是在我自己的圖書館,cancel.h。然後當我編譯我的程序時,它成功了。雖然,當我跑的可執行文件,我得到了意想不到的輸出(我打不了它,因爲我得到它,當我複製,它只是被貼爲「M」字符):

enter image description here

神祕字符enter image description here當我複製時,複製爲信m。此外,當我運行我的程序有不同的輸入,我得到不同的神祕輸出:

enter image description here

在另一種情況下,我得到完美的輸出,只是一個空行打印:

enter image description here

我也遇到過this問題,其中用戶獲得相同的符號。


我做了什麼至今?

我搜索了很多,我找不到任何線索有關enter image description here這個角色,但如果你仔細看,第二圖像中,我得到更多的字符,當我輸入「你好,這是阿希什」。其中之一是斜線,一個是enter image description here。但我得到另一個字符enter image description here。我得到了this鏈接,它顯示瞭如何重現它,並解釋它,雖然我不明白。當你運行那裏給出的代碼時,你會得到很多字符,其中一個是enter image description here。雖然,即使該文章的作者也無法複製,並且沒有發佈。因此,這裏的輸出:

enter image description here

這是實際的輸出,因爲這是不明確的,這裏有一個切出的版本:

enter image description here

所以基本上我才知道,這兩個人物enter image description hereenter image description here是字符串中的擴展字符。在那時,我確實找到了scanline中導致問題的原因。

線條

if (c == '\n')                  /* Checking if user has finished inputting the line */ 
{ 
    str [j] = c;                 /* Copying newline into string */ 
    ++j; 
    ++ len; 
} 

是造成問題,因爲你複製一個換行符到字符串。它的工作,但我不知道爲什麼,因爲這只是一個猜測。我搜查了但仍然找不到原因。


我的問題

  • 如何消除這些線條使程序正常工作?

  • 什麼是字符enter image description hereenter image description here?他們應該做什麼,他們是如何出現在這裏的?

  • 還有更多這樣的人物嗎?

  • 爲什麼不能複製這些字符?

  • 它是未定義的行爲?

+4

'str'不是空終止。 – BLUEPIXY

+1

downvoter可以解釋嗎?我想改善我的帖子。 –

+0

一些工具集會使DEBUG編譯並填充本來不會被填充的內存(因爲您的代碼沒有說),因此您和它的調試庫在測試_some_執行路徑時可以輕鬆捕獲_some_錯誤。 –

回答

3

有一些混亂關於這裏術語亂碼。它指的是駐留在未以某種明確定義的方式分配的變量中的任何字節。如果字符A碰巧出現在(例如)由malloc返回的內存塊或未初始化的變量char中,則字符A可能是垃圾字符。

這與不可打印字符不同,它們是打印爲字符時沒有明確表示的任何字符。例如,ASCII碼0-31和127(十六進制的0-1F和7F)是控制字符,因此不可打印。還有一些特定的終端可能不知道如何渲染它們的多字節字符。

進入你的具體問題:

爲什麼不能字符(圖像)被複制?

作爲一個不可打印的字符,其屏幕表示沒有很好的定義。所以試圖從終端複製並粘貼它會產生意想不到的結果。

做垃圾人物有一些模式?這意味着你可以預測一個空字符串可以出現什麼字符,空 整數什麼會來,等等。

垃圾字符的本質是它們的內容是未定義的。試圖預測未初始化的數據將包含什麼是徒勞的努力。用兩種不同編譯器(或具有不同優化設置的相同編譯器)編譯的同一段代碼對於任何未初始化的數據可以具有完全不同的內容。

該標準沒有說明應該去哪些值,所以實現可以自由處理它,但是他們想要。他們可以選擇在這些內存地址上留下任何值,他們可以選擇將0寫入所有地址,他們可以選擇依次寫入值0,1,2,3等。換句話說,內容是undefined

當聲明一個變量時,爲什麼它有一個垃圾字符 而不是空白?有沒有將它與 一個垃圾字符存儲的具體原因?

全局變量和靜態局部變量初始化爲所有字節爲零,這是標準規定的。這是編譯時很容易完成的事情。另一方面,局部變量駐留在堆棧上。所以它們的值就是函數被調用時堆棧中發生的任何事情。

這裏有一個有趣的例子:

void f1() 
{ 
    char str[10]; 
    strcpy(str, "hello"); 
} 

int main() 
{ 
    f1(); 
    f1(); 
    return 0; 
} 

下面是一個具體的實現威力做:

首次f1被調用時,局部變量str未初始化。然後strcpy被稱爲哪個副本中的字符串「你好」。這佔用變量的前6個字節(5爲字符串,1爲空終止符)。剩下的4個字節仍然是垃圾。當此函數返回時,變量str所在的內存可以自由用於其他用途。

現在f1在第一次呼叫後立即再次被呼叫。由於沒有調用其他函數,因此調用f1的堆棧恰好與上次調用位置相同。因此,如果您此時要檢查str,您會發現它包含h,e,l,l,o以及前六個字節的空字節(即字符串「hello」)。但是,這個字符串是垃圾。它並沒有專門存儲在那裏。如果在第二次調用f1之前調用某個其他函數,那麼很可能這些值將是而不是在那裏。

同樣,垃圾表示內容未定義。編譯器沒有明確地將「垃圾」(或不可打印的字符)放入變量中。

對於非空字符串的字符串,是否會在每個操作系統上打印相同的垃圾文字 ?如果是,哪一個?

這裏的那些你混淆垃圾不可打印的地方之一。在你的具體情況下,垃圾字符恰好是不可打印的,但它不一定是。再舉一例:

void f3() 
{ 
    char str1[5], str2[5]; 

    strcpy(str1, "hello"); 
    strcpy(str2, "test"); 
    printf("str1=%s\n", str1); 
} 

讓我們假設編譯器決定放置str2內存str1後立即(儘管它不具有)。第一次調用strcpy將把字符串「hello」寫入str1,但是這個變量沒有足夠的空間來終止空字節。所以它被寫入內存中的下一個字節,這恰好是str2的第一個字節。然後當strcpy的下一個調用運行時,它將字符串「test」放在str2中,但這樣做會覆蓋寫入str1時所放置的空終止字節。

然後當printf被調用,你會得到這樣的輸出:

str1=hellotest 

打印時str1printf查找空結束,但沒有的str1一個內。所以它一直在閱讀,直到它。在這種情況下,恰好在它之後有另一個字符串,所以它也會打印出來,直到它找到正確存儲在該字符串中的空終止符。

但是,這一行爲是undefined。此功能看起來很小的變化可能會導致str2首先出現在內存中。編譯器可以自由地按照它的意願去做,所以沒有辦法預測會發生什麼。

每個操作系統上是否有相同的垃圾字符?或者他們 不同?

我相信你實際上是指在這種情況下不可打印的字符。這實際上取決於所討論的操作系統和/或終端的字符集。例如,漢字由多個字節表示。如果你的終端不能打印中文字符,你會看到一些類似於你看到的每個字節的代碼。但如果可以的話,它會以明確的方式顯示它。

有沒有辦法在C/ C++中的標準輸出緩衝區上打印這些字符?

不作爲字符。但是,您可以打印出它們的數字表示。例如:

void f4() 
{ 
    char c; 
    printf("c=%02hhX\n", (unsigned char)c); 
} 

c內容是不確定的,但上面會打印任何值恰好有十六進制格式。

如果您仔細看到字符(圖片), 中有一些字符和數字。他們代表 什麼?

一些終端將通過打印包含字符的Unicode codepoint使讀者可以知道它是什麼框中顯示不可打印的字符。

Unicode是文本的標準,其中每個字符分配有數字代碼點。除了ASCII範圍內的典型字符集外,Unicode還定義了其他字符,如重音字母,希臘字母,希伯來字母,西裏爾字母,中文和日文等其他字母以及各種符號。由於Unicode定義了數千個字符,因此需要多個字節來表示它們。 Unicode最常見的編碼是UTF-8,它允許將常規ASCII字符編碼爲一個字節,並根據需要將其他字符編碼爲兩個或多個字節。

在這種情況下,問題代碼點是007F。這是DELETE控制字符,通常在按下Delete鍵時生成。由於這是一個控制字符,因此您的終端將其顯示爲一個帶有Unicode字符的字符框,而不是試圖「打印」它。

是否有可以用C/ C++打印的垃圾字符列表?

同樣,假設你真的是不可打印字符這裏,有更多的工作要做與同時顯示字符終端,與語言。通常,控制字符是不可打印的,而某些多字節字符可能會或可能不會正確顯示,具體取決於終端的字體/字符集。

+0

這很好,但是在你的'f3'函數中,你輸入'printf(「str1 =%s \ n」);'不通過任何第二個參數,並且'hellotest'按照你的打印方式?我認爲這是一個錯字。 –

+0

我也無法理解第二個最後一個問題:「如果仔細觀察角色(圖片),它裏面有一些字符和數字,它們代表什麼?」。什麼是_Unicode Codepoint_?另外,什麼是_DELETE控制字符? –

+0

@AshishAhuja缺少的參數是一個錯字。 'str1'應該已經被傳入。這已被修復。我還添加了對Unicode的描述以及更多詳細信息的鏈接和DELETE字符的描述。 – dbush

0

對於初學者來說,該函數返回不正確的值len。我們假設lim等於2

在這種情況下,在循環會有什麼陣列中被寫入,由於條件

if (len < (lim -2)) 

後然而環路len的第一次迭代將增加。

for (len = 0; (c = getchar()) != EOF && c != '\n'; ++len) 
                ^^^^^ 

在第二次迭代再次會有什麼在陣列diue寫入相同的條件

if (len < (lim -2)) 

len將有所增加。

for (len = 0; (c = getchar()) != EOF && c != '\n'; ++len) 
                ^^^^^ 

因此沒有將被寫入陣列中,但LEN將升高,直到例如換行字符會遇到。

所以該功能無效。此外,它假定該函數將附加讀取字符串與終止零。但是這不是在函數中完成的。所以你可能不會以字符串形式輸出字符數組。

該函數可以寫成下面的方式

int scanline(char str [], int lim) 
{ 
    int len = 0; 
    int c;  

    while (len < lim - 1 && (c = getchar()) != EOF && c != '\n') 
    { 
     str[len++] = c; 
    } 

    if (len < lim - 1 && c == '\n') str[len++] = c; 

    if (len < lim) str[len++] = '\0'; 

    return len; 
}