C中的垃圾字符

2022-04-04 00:00:00 string special-characters c newline c++

已编辑的问题

我明白了我在原始问题中给出的代码中的错误,而我得到的字符是垃圾字符。不过,关于C:

中的垃圾字符,我仍然有几个问题
  • 为什么无法复制字符?

  • 垃圾字符有模式吗?这意味着您可以预测空字符串会出现什么字符,空整数会出现什么,以此类推。

  • 声明变量时,为什么它有垃圾字符而不是空白?是否有特定原因将其与垃圾字符一起存储?

  • 对于不是以空结尾的字符串,是否会在每个操作系统上打印相同的垃圾字符?如果是,是哪一个?

  • 每个操作系统上都有相同的垃圾字符吗?或者它们是不同的吗?

  • 有没有办法在C/C++的stdout缓冲区中打印这些字符?

  • 如果你仔细看这个字,里面有一些字符和数字。它们代表什么吗?

  • 是否有可以用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 != '
'; ++len)                          /* Reading a character one by one, till the user enters '
', and checking for failure of 'getchar' */
    {
        if (len < (lim -2))                                                             /* Checking that string entered has not gone beyond it's boundaries. '-2' for '
' and '' */
        {
           str [j] = c;                                                                 /* Copying read character into 'string [j]' */
           ++ j;                                                                        /* Incrementing 'j' by 1 */
        }
    }
    if (c == '
')                                                                      /* 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
 str = %s
", len, str);
    return 0;
}
所需的头文件和函数位于我自己的库cancel.h中。然后,当我编写我的程序时,它是成功的。虽然,当我运行可执行文件时,我得到了意外的输出(我无法输入它,因为我得到一个字符,当我复制它时,它只是粘贴为‘m’):

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

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

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


我到目前为止做了什么?

我搜索了很多次,都找不到有关这个字符的任何线索,但如果您仔细查看,在第二张图像中,当我输入";Hi时,我得到了更多的字符。其中一个是斜杠,一个是。但我得到了另一个字符。我得到了this链接,上面显示了如何复制它,并解释了它,尽管我看不懂。当您运行那里给出的代码时,您会得到很多字符,其中一个是。尽管,即使是那篇文章的作者也无法复制它,也没有发布它。下面是输出:

这是实际输出,因为不清楚,下面是一个删节版本:

所以基本上我知道字符和都是字符串的扩展字符。在这一点上,我实际上找出了导致scanline中问题的原因。

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

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


我的问题

  • 删除这些行如何使程序正常工作?

  • 和是什么字符?他们应该做什么?他们在这里是什么样子?

  • 还有像这样的字符吗?

  • 为什么无法复制这些字符?

  • 它是未定义的行为吗?


解决方案

这里对术语垃圾字符有一些混淆。它指的是驻留在没有以某种明确定义的方式赋值的变量中的任何字节。如果字符A恰好出现在malloc返回的内存块或未初始化的char变量中,则它可能是垃圾字符。

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

进入您的特定问题:

为什么无法复制字符(图像)?

作为无法打印的字符,其屏幕表示没有定义良好。因此,尝试从终端复制并粘贴它会产生意外的结果。

垃圾字符是否有某种模式?意思是说你能 为空字符串预测可能出现的字符 整数将要发生的内容,依此类推。

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

该标准没有说明应该在那里放什么值,所以实现可以随意处理它。它们可以选择保留那些存储器地址中碰巧存在的任何值,可以选择将0写入所有地址,可以选择按顺序写入值0、1、2、3等。换句话说,内容未定义。

声明变量时,为什么它有垃圾字符 而不是一片空白?有没有特别的理由把它存放在一起 垃圾字符?

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

这里有一个有趣的例子:

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

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

以下是特定实现可能执行的操作:

第一次调用f1时,局部变量str未初始化。然后调用strcpy,它复制字符串"Hello"。这将占用变量的前6个字节(5个用于字符串,1个用于空终止符)。剩下的4个字节仍然是垃圾。当此函数返回时,变量str所在的内存可用于其他用途。

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

同样,垃圾表示内容未定义。编译器不会显式地将"垃圾"(或无法打印的字符)放入变量中。

对于不是以NULL结尾的字符串,将使用相同的垃圾 字符可以打印在每个操作系统上吗?如果是,是哪一个?

这里是您混淆垃圾和无法打印的地方之一。在您的特定情况下,垃圾字符碰巧是不可打印的,但它不一定是。下面是另一个例子:

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

    strcpy(str1, "hello");
    strcpy(str2, "test");
    printf("str1=%s
", str1);
}
让我们假设编译器决定将str2紧跟在str1之后放入内存中(尽管它不必这样做)。对strcpy的第一次调用会将字符串"Hello"写入str1,但该变量没有足够的空间容纳空的终止字节。因此它被写入内存中的下一个字节,也就是str2的第一个字节。然后,当对strcpy的下一次调用运行时,它将字符串"test"放入str2,但在这样做的过程中,它会覆盖写入str1时放在那里的空终止字节。

然后,当printf被调用时,您将得到以下输出:

str1=hellotest

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

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

每个操作系统上都有相同的垃圾字符吗?或者他们是 不同?

我相信在本例中您实际上指的是无法打印的字符。这实际上取决于相关操作系统和/或终端的字符集。例如,汉字用多个字节表示。如果您的终端不能打印中文字符,您将看到某种类型的代码,类似于您看到的每个字节的代码。但如果可以,它将以明确定义的方式显示它。

有没有办法用C/语言在标准输出缓冲区中打印这些字符 C++?

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

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

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

如果你仔细看这个角色(图像), 里面有一些字符和数字。他们是否代表了 什么事?

某些终端将通过打印包含字符Unicode codepoint的框来显示无法打印的字符,以便读者可以知道它是什么。

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

在本例中,有问题的代码点是007F。这是删除控制字符,通常在按Delete键时生成。由于这是一个控制字符,因此您的终端会将其显示为带有该字符的Unicode点的框,而不是尝试"打印"它。

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

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

相关文章