0

I'm trying to convert some text (character by character) to its binary representation. For some reason the print statement printf("Hold is %d or %c: ", hold, hold); is changing the output of my function and I have no idea how to explain it. Any help would be greatly appreciated. The test file is just a text file with Hello, World! inside of it.

With it:

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

Without it:

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

Code

#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;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Chirality
  • 745
  • 8
  • 22
  • You need to read about what *strings* are in [tag:c], you can't use `strlen()` on your `binarr` array because it's not `null` terminated. – Iharob Al Asimi Jan 24 '16 at 02:25
  • Strings are arrays of characters. So when you just declare a char array there isn't automatically a null terminator. Is that what you're saying? – Chirality Jan 24 '16 at 02:32
  • Yes and a string in [tag:c] requires this null terminator. – Iharob Al Asimi Jan 24 '16 at 02:34
  • So i slapped on a null terminator like so `binarr[255]='\0';` and now what? Still not getting good output. – Chirality Jan 24 '16 at 02:35
  • `binarr[255]` is one element after the end of the array. Also, it should be after the last character not the end of the array. – Iharob Al Asimi Jan 24 '16 at 02:36
  • Well the reason I'm putting it at the end of the array is because the characters are put on in reverse order so from back to front and then I'm printing the last 8 characters of the array. – Chirality Jan 24 '16 at 02:40
  • Then use `fwrite()` instead of `fprintf()`. – Iharob Al Asimi Jan 24 '16 at 02:41
  • Note that [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong). It may not be the immediate cause of your problem, but it will give you trouble in future. – Jonathan Leffler Jan 24 '16 at 02:56

3 Answers3

1

Your decimal_to_binary function is incorrect:

  • you index beyond the end of the binarr array.
  • you do not null terminate this array to pass it to printf.

Here is a simpler and corrected version:

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);
}

Your function main has problems too:

  • you test for end of file with feof(in). This is incorrect, you should instead check if hold is EOF.
  • hard coding the value of '\n' as 10 is bad practice.

Here is a correct version:

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;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thanks for the respone, but I was able to fix it as you posted. Why exactly is hardcoding to avoid a newline character bad practice? I haven't heard that one before. – Chirality Jan 24 '16 at 02:54
  • @Revolt: It is bad practice to hard code the numeric values of character literals. It makes you code harder to read and also less portable, although most systems now use ASCII. For example `'a'` is not `97` in EBCDIC, and `'a'` is definitely more readable than `97` for most humans. – chqrlie Jan 24 '16 at 02:58
  • Ah that makes sense from a best programming practices standpoint. I'm used to writing code for specific systems where they all use ASCII. – Chirality Jan 24 '16 at 03:04
  • @Revolt: the readability rationale still holds. This is a very common rule in programming style guides. – chqrlie Jan 24 '16 at 03:05
1

I decreased the extremely large array, made sure to terminate the string with a null character, zeroed the array, then printed it using fprintf. This solved the issue.

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));
}
Chirality
  • 745
  • 8
  • 22
  • This would be enough for 8 bit bytes, but you should define `num` as such to avoid overflow if called with a larger number in a later life. – chqrlie Jan 24 '16 at 02:55
  • Also half the code in this function is still useless `;-)` – chqrlie Jan 24 '16 at 03:00
  • It does one character at a time, so 8 bits is fine. It's not used for anything other than that. If renaming it to "character_to_binary" pleases you I can do that <3 – Chirality Jan 24 '16 at 03:05
  • The name matters less than the type of `num`, but it is indeed a good idea to rename it. The function might be pasted to another project and will fail when passed larger numbers. People often reuse code from answers. Functions posted in answers should not contain bear traps like this. – chqrlie Jan 24 '16 at 03:09
1

Your program has undefined behavior for couple of reasons.

  1. You don't have a null terminated string. Calling strlen on such a string is cause for undefined behavior.
  2. You are modifying binarr using an out of bounds index. That is also cause for undefined behavior.

I have my annotations to your function decimal_to_binary that point out where those errors are.

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));
}

The fixes are simple.

  1. Terminate string with the null character.

    for (i=0; i<255; i++){ binarr[i] = '0'; }
    i--;
    binarr[i] = '\0';
    
  2. Use the right index when modifying binarr in the while loop.

    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;
    }
    
R Sahu
  • 204,454
  • 14
  • 159
  • 270