C中的垃圾字符

Garbage characters in C

已编辑问题

我理解我在原问题中给出的代码中的错误,我得到的字符是垃圾字符。虽然,我仍然对 C:

中的垃圾字符有一些疑问

原始问题

Title of original question: Mysterious character output in 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 '[=10=]' */
        {
           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;
}

所需的 headers 和函数在我自己的库中,cancel.h。然后当我编译我的程序时,它是成功的。虽然,当我 运行 可执行文件时,我得到了意外的输出(我无法输入它,因为我得到一个字符,当我复制时,它只是被粘贴为 'm'):

神秘字符是 ,当我复制时,它被复制为字母 m。另外,当我 运行 我的程序有不同的输入时,我得到不同的神秘输出:

在另一种情况下,我得到了完美的输出,只是打印了一个空行:

我也遇到过 this 问题,其中用户得到相同的符号。


到目前为止我做了什么?

我找了很多,也没找到任何关于 this character, but if you see carefully, in the second image, I get more characters when I enter "hi this is ashish". One of them is the slash, and one is . But I get another character . I got this link which was showed how to reproduce it, and explained it, although I could not understand. When you run the code given there, you get a lot of characters, and one of them is 的线索。虽然,连那篇文章的作者都无法复制,也没有发表。所以这是输出:

这是实际输出,不清楚,这里是截取的版本:

所以基本上我知道这两个字符 and 都是字符串的扩展字符。在这一点上,我实际上弄清楚了 scanline.

中导致问题的原因

线条

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

在您将换行符复制到字符串中时导致了问题。它有效,但我不确定为什么,因为这样做只是一个猜测。找了找也没找到原因


我的问题

对于初学者来说,函数 returns 的值 len 不正确。假设 lim 等于 2.

在这种情况下,由于条件

,在循环中数组中不会写入任何内容
if (len < (lim -2))

但是在循环的第一次迭代后 len 将增加。

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

在第二次迭代中,由于相同的条件,数组中将不再写入任何内容

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++] = '[=14=]';

    return len;
}

关于术语 垃圾字符,这里有些混淆。它指的是驻留在未以某种明确定义的方式分配的变量中的任何字节。如果字符 A 碰巧出现在(例如)由 malloc 返回的内存块或未初始化的 char 变量中,则它可能是垃圾字符。

这与 不可打印字符 不同,后者是作为字符打印时没有明确定义的任何字符。例如,ASCII 代码 0 - 31 和 127(0 - 1F 和 7F 十六进制)是控制字符,因此不可打印。还有一些多字节字符,特定终端可能不知道如何呈现它们。

进入您的具体问题:

Why can't the character (image) be copied?

作为不可打印的字符,它的屏幕表示没有明确定义。因此,尝试从终端复制和粘贴它会产生意想不到的结果。

Do garbage characters have some pattern? Meaning that can you predict for an empty string what character can come, for an empty integer what will come, and so on.

垃圾字符的本质是其内容未定义。试图预测未初始化的数据将包含什么是徒劳的。用两个不同的编译器(或具有不同优化设置的同一编译器)编译的同一段代码对于任何未初始化的数据可能具有完全不同的内容。

标准没有说明应该放什么值,所以实现可以随意处理它。他们可以选择保留这些内存地址中的任何值,他们可以选择将 0 写入所有地址,他们可以选择依次写入值 0、1、2、3 等。也就是说,内容是undefined.

When a variable is declared, why does it have a garbage character instead of being blank? Is there a specific reason of storing it with a garbage character?

全局变量和静态局部变量初始化为所有字节为零,这是标准规定的。这是在编译时很容易完成的事情。另一方面,局部变量驻留在堆栈中。因此,它们的值是调用函数时恰好在堆栈上的任何值。

这是一个有趣的例子:

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

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

这是特定实现 可能 做的事情:

第一次调用f1,局部变量str未初始化。然后调用strcpy which copies in the string "hello"。这占用了变量的前 6 个字节(5 个用于字符串,1 个用于空终止符)。剩下的 4 个字节仍然是垃圾。当这个功能 returns 时,变量 str 所在的内存可用于其他目的。

现在 f1 在第一次调用后立即再次调用。由于没有调用其他函数,因此调用 f1 的堆栈恰好位于与上次调用完全相同的位置。所以如果你此时去查看str,你会发现它包含了h, e, l, l, o,和前 6 个字节的空字节(即字符串 "hello")。但是,这个字符串是垃圾。它没有专门存放在那里。如果在第二次调用 f1 之前调用了其他函数,那么这些值很可能 而不是

同样,垃圾 表示内容未定义。编译器没有明确地将 "garbage"(或不可打印的字符)放入变量中。

For a string which is not null-terminated, will the same garbage character be printed on every OS? If yes, which one?

这是您混淆 垃圾不可打印 的地方之一。在您的特定情况下,垃圾字符恰好是不可打印的,但不一定是。这是另一个例子:

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

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

让我们假设编译器决定在内存中 str1 之后立即放置 str2(尽管它不必这样做)。第一次调用 strcpy 会将字符串 "hello" 写入 str1,但此变量没有足够的空终止字节空间。所以它被写入内存中的下一个字节,恰好是 str2 的第一个字节。然后,当下一次调用 strcpy 运行时,它会将字符串 "test" 放入 str2 中,但这样做会覆盖写入 str1 时放置在那里的空终止字节。

然后当 printf 被调用时,你会得到这样的输出:

str1=hellotest

打印str1时,printf查找空终止符,但str1里面没有。所以它一直读到它读完为止。在这种情况下,它后面恰好有另一个字符串,因此它也会打印该字符串,直到找到正确存储在该字符串中的空终止符。

但同样,此行为是 未定义。此函数中看似微小的更改可能会导致 str2 首先出现在内存中。编译器在这方面可以随心所欲,所以无法预测会发生什么。

Are there the same garbage characters on every OS? Or are they different?

我相信在这种情况下您实际上指的是 不可打印 字符。这实际上取决于所讨论的 OS and/or 终端的字符集。例如,汉字是用多个字节表示的。如果您的终端无法打印汉字,您将看到某种类型的代码,类似于您在每个字节中看到的代码。但是如果可以的话,它会以定义明确的方式显示它。

Is there a way to print these characters on the stdout buffer in C / C++?

不是字符。但是,您可以打印出它们的数字表示。例如:

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

c 的内容未定义,但上面的内容将以十六进制格式打印恰好存在的任何值。

If you see carefully in the character (image), there are some characters and numbers in it. Do they represent something?

一些终端会显示不可打印的字符,通过打印一个包含字符 Unicode codepoint 的框来让 reader 知道它是什么。

Unicode 是一种文本标准,其中每个字符都分配有一个数字 代码点 。除了 ASCII 范围内的典型字符集外,Unicode 还定义了其他字符,例如重音字母、其他字母表(如希腊语、希伯来语、西里尔字母、中文和日语)以及各种符号。因为 Unicode 定义了数千个字符,所以需要多个字节来表示它们。 Unicode 最常见的编码是 UTF-8,它允许常规 ASCII 字符用一个字节编码,其他字符根据需要用两个或更多字节编码。

在这种情况下,有问题的代码点是 007F。这是 DELETE 控制字符,通常在按下 Delete 键时生成。由于这是一个控制字符,您的终端将其显示为一个带有字符 Unicode 点的框,而不是尝试 "print" 它。

Is there a list of garbage characters which can be printed in C / C++?

同样,假设您在这里的意思是 不可打印的字符,这更多地与显示字符的终端有关。通常,控制字符是不可打印的,而某些多字节字符可能会或可能不会正确显示,具体取决于终端的字体/字符集。