【问题标题】:Print statement changing char array output打印语句更改字符数组输出
【发布时间】:2016-04-30 12:26:20
【问题描述】:

我正在尝试将一些文本(逐个字符)转换为其二进制表示。出于某种原因,打印语句printf("Hold is %d or %c: ", hold, hold); 正在改变我的函数的输出,我不知道如何解释它。任何帮助将不胜感激。测试文件只是一个包含Hello, World! 的文本文件。

有了它:

Hold is 72 or H: 01001000
Hold is 101 or e: 01100101
Hold is 108 or l: 01101100
Hold is 108 or l: 01101100
Hold is 111 or o: 01101111
Hold is 44 or ,: 00101100
Hold is 32 or  : 00100000
Hold is 87 or W: 01010111
Hold is 111 or o: 01101111
Hold is 114 or r: 01110010
Hold is 108 or l: 01101100
Hold is 100 or d: 01100100
Hold is 33 or !: 00100001

没有它:

1000 �
0101 �
1100 �
1100 �
1111 �
1100 �
0000 �
0111 �
1111 �
0010 �
1100 �
0100 �
0001 �

代码

#include <stdio.h>
#include <string.h>

void decimal_to_binary(unsigned long num, FILE *out) {    
    int i = 255, a = 0;
    char binarr[255];
    for (i = 0; i < 255; i++) { binarr[i] = '0'; }
    if (num != 0) {
        while (num != 0) {
            if (num % 2 == 0) {
                binarr[i] = '0';
                i--;
            } else {
                binarr[i] = '1';
                i--;
            }
            num /= 2;
        }
    } else {
        fprintf(out, "00000000");
    }
    fprintf(out, "%s ", binarr + strlen(binarr) - 8);
    printf("%s\n", binarr + strlen(binarr) - 8);
    memset(binarr, 0, sizeof(binarr));    
}

int main(int argc, char *argv[]) {
    int hold;
    FILE *in = fopen(argv[1], "r");
    FILE *out = fopen(argv[2], "w+");

    while (!feof(in)) {
        hold = fgetc(in);
        if (hold > 0 && hold != 10){
            printf("Hold is %d or %c: ", hold, hold);
            decimal_to_binary(hold, out);
        }
    }
    fclose(in);
    fclose(out);
    return 0;
}

【问题讨论】:

  • 您需要了解c 中的字符串 是什么,您不能在binarr 数组上使用strlen(),因为它不是null 终止的。
  • 字符串是字符数组。因此,当您仅声明一个 char 数组时,不会自动出现空终止符。你说的是这个吗?
  • 是的,c 中的字符串需要这个空终止符。
  • 所以我像binarr[255]='\0'; 这样打了一个空终止符,现在呢?仍然没有得到好的输出。
  • binarr[255] 是数组末尾的一个元素。此外,它应该在最后一个字符之后而不是数组的末尾。

标签: c arrays char printf


【解决方案1】:

您的decimal_to_binary 函数不正确:

  • 您的索引超出了binarr 数组的末尾。
  • 您不要空终止此数组以将其传递给printf

这是一个更简单且更正的版本:

void decimal_to_binary(unsigned long num, FILE *out) {
    int i = 256, a = 0;
    char binarr[257];
    memset(binarr, '0', sizeof(binarr) - 1);
    binarr[i] = '\0';
    while (num != 0) {
        --i;
        if (num % 2) {
            binarr[i] = '1';
        }
        num /= 2;
    }
    if (i > 256 - 8) // print at least 8 bits
        i = 256 - 8;
    fprintf(out, "%s ", binarr + i);
    printf("%s\n", binarr + i);
}

你的函数main也有问题:

  • 您使用feof(in) 测试文件结尾。这是不正确的,您应该检查 hold 是否为 EOF
  • '\n' 的值硬编码为 10 是不好的做法。

这是一个正确的版本:

int main(int argc, char *argv[]) {
    int hold;
    FILE *in = fopen(argv[1], "r");
    FILE *out = fopen(argv[2], "w+");

    while ((hold = fgetc(in)) != EOF) {
        if (hold != '\n') {
            printf("Hold is %d or %c: ", hold, hold);
            decimal_to_binary(hold, out);
        }
    }
    fclose(in);
    fclose(out);
    return 0;
}

【讨论】:

  • 感谢您的回复,但我能够按照您发布的内容进行修复。为什么硬编码以避免换行符是不好的做法?我以前没听过。
  • @Revolt:硬编码字符文字的数值是不好的做法。尽管现在大多数系统都使用 ASCII,但它使您的代码更难阅读且可移植性也较差。例如 'a' 在 EBCDIC 中不是 97,对于大多数人来说,'a' 绝对比 97 更具可读性。
  • 啊,从最佳编程实践的角度来看,这是有道理的。我习惯于为都使用 ASCII 的特定系统编写代码。
  • @Revolt:可读性原理仍然成立。这是编程风格指南中非常常见的规则。
【解决方案2】:

由于几个原因,您的程序具有未定义的行为。

  1. 您没有以空字符结尾的字符串。在这样的字符串上调用 strlen 会导致未定义的行为。
  2. 您正在使用越界索引修改binarr。这也是导致未定义行为的原因。

我对您的函数 decimal_to_binary 进行了注释,指出了这些错误的位置。

void decimal_to_binary(unsigned long num, FILE *out){

    int i = 255, a = 0;
    char binarr[255];
    for (i=0; i<255; i++){ binarr[i] = '0'; }

    // All the elements of binarr are set to '0'.
    // It's not a null terminated string.

    if (num != 0) {
        while (num!=0){

           // The value of i is 255 when this loop is 
           // entered the first time.
           // Setting the value of binarr[255] is cause for
           // undefined behavior.

            if (num%2 == 0){

                binarr[i] = '0';
                i--;
            }
            else { binarr[i] = '1'; i--; }
            num /= 2;
        }
    } else { fprintf(out, "00000000"); }
    fprintf(out, "%s ", binarr + strlen(binarr) - 8);
    printf("%s\n", binarr + strlen(binarr) - 8);
    memset(binarr, 0, sizeof(binarr));
}

修复很简单。

  1. 以空字符结束字符串。

    for (i=0; i<255; i++){ binarr[i] = '0'; }
    i--;
    binarr[i] = '\0';
    
  2. while循环中修改binarr时使用正确的索引。

    while (num!=0){
    
        // Decrement the index before you assign to the next element.
        // When the loop is entered the first time, i = 254, which
        // is used to null terminate binarray.
        // The next '1' or '0' needs to be placed at i = 253.
        i--;
    
        if (num%2 == 0){
    
            binarr[i] = '0';
        }
        else {
           binarr[i] = '1';
        }
        num /= 2;
    }
    

【讨论】:

    【解决方案3】:

    我减少了非常大的数组,确保以空字符终止字符串,将数组归零,然后使用 fprintf 打印它。这解决了问题。

    void decimal_to_binary(unsigned long num, FILE *out){
    
        int i = 7, a = 0;
        char binarr[9];
        binarr[8]='\0';
        for (a=7; a>=0; a--){ binarr[a] = '0'; }
        if (num != 0) {
            while (num!=0){
                if (num%2 == 0){
                    binarr[i] = '0';
                    i--;
                }
                else { binarr[i] = '1'; i--; }
                num /= 2;
            }
        } else { fprintf(out, "00000000"); }
        fprintf(out, "%s ", binarr);
        memset(binarr, 0, sizeof(binarr));
    }
    

    【讨论】:

    • 这对于 8 位字节来说已经足够了,但是您应该这样定义 num 以避免在以后使用更大的数字调用时溢出。
    • 这个函数里还有一半的代码还是没用;-)
    • 一次只处理一个字符,所以 8 位就可以了。它不用于除此之外的任何事情。如果将其重命名为“character_to_binary”让您满意,我可以这样做
    • 名称比num的类型重要,但重命名确实是个好主意。该函数可能会被粘贴到另一个项目,并且在传递更大的数字时会失败。人们经常重复使用答案中的代码。答案中发布的函数不应包含这样的陷阱。