0

I am trying to convert an array of characters to binary. I've spent an hour now perusing SO posts, but few do quite what I need and none of them has worked for me.

void formatCallSign(unsigned char *callsign) {
   unsigned char new[7];
   printf("original is %s",callsign);
   for (int j = 0; j < 6; j++) {
      printf("Hex of this is %x", callsign[j]); // this correctly prints out the original hex value
      char binary[9];
      binary[0] = ( (callsign[j] & (1 << 7)) ? '1' : '0' );
      binary[1] = ( (callsign[j] & (1 << 6)) ? '1' : '0' );
      binary[2] = ( (callsign[j] & (1 << 5)) ? '1' : '0' );
      binary[3] = ( (callsign[j] & (1 << 4)) ? '1' : '0' );
      binary[4] = ( (callsign[j] & (1 << 3)) ? '1' : '0' );
      binary[5] = ( (callsign[j] & (1 << 2)) ? '1' : '0' );
      binary[6] = ( (callsign[j] & (1 << 1)) ? '1' : '0' );
      binary[7] = ( (callsign[j] & (1 << 0)) ? '1' : '0' );
      printf("Binary of %c is: %x\r\n", callsign[j], binary); // print hex representation of character
   }
   }

Essentially, the input to this function is a 7-character string (char array). I was also using a nested for loop, but the compiler was generating infinite loop errors that made no sense, so I rewrote it like this.

I need to then get the binary representation of that character and shift do a left bitwise shift by 1 (not always but 95% of the time, I can add the logic later). After the bitshift, I need the hex value.

Getting the binary value has proved elusive. In a prior project, I was never able to do it properly, and could only get the hex value. In this case, I can also get the hax value in the second printf statement, but I can't do a bitwise shift with the hex value, as I need to shift the individual bits.

Yet, when I run the program, it always says Binary of [char] is: 9A. Always 9A, which is not the hex rep. of any of the characters in the string I pass into it.

My thought is to use sprintf and pass in the array and use %x to get the hex representation of it. Right now, it prints to the console, but I would actually store that in the new array, which is unused in this snippet right now. Is it possible to do this somehow? The code to get the binary doesn't seem to work (based on the second part of this answer).

(To clarify, my 2nd and 3rd printf statements in this snippet should output the same thing. Only the 2nd one works. The reason for drawing it out like that is so I can do bitwise operations and then use a sprintf to put it back into a coherent single value and get the hex from that.)

Many questions address this topic, but few have been helpful because I am not interested in printing the binary out to the console. Rather, I need to store the binary in a variable, do bitwise operations with it, then get a hex representation of the final binary. Right now, I can successfully get a hex representation of the original character, but that is not helpful.

My only other idea is to manually define the binary values of every hex number and then use that as a "lookup table", but this seems inefficient.

To test this, all else that is required is this in main:

unsigned char dest[8]   = "CQ     ";
formatCallSign(&dest);
InterLinked
  • 1,247
  • 2
  • 18
  • 50
  • The last `printf` statement is wrong. You are not supposed to pass an array (`binary`) for a `%x` format specification. To see what is in the array, print each array element separately. (Since it is an array of `char`, you can abuse C semantics and print it with `%s`. First, set the last element to 0 (`binary[8] = 0;`) and then pass it to `printf` as you do now, but change `%x` to `%s`. Also, remove the `\r`.) – Eric Postpischil Jul 31 '19 at 17:36
  • In the future, when you ask what is wrong with code, include a [mcve] so that other people can compile and work with your code easily. – Eric Postpischil Jul 31 '19 at 17:38
  • Turn on warnings in your compiler. It should have warned you that `binary` is the wrong argument for `%x`. – Eric Postpischil Jul 31 '19 at 17:38
  • @EricPostpischil I think I follow, working on revisions now. But why remove the `\r`? Otherwise there is a new line but no carriage return, which leads to messy output. `\r` is required for carriage return – InterLinked Jul 31 '19 at 17:43
  • @EricPostpischil I'm in a similar dilemma now, instead of a perpetual `9A`, I get a perpetual `a3`. You said you can't pass in an array to sprintf - yet then how do you build up a string (inevitably a char[]) and then pass it in? I followed this answer: https://stackoverflow.com/a/2674333 – InterLinked Jul 31 '19 at 17:52
  • Per the 2018 C standard, `\n` is the *new line* character and “Moves the active position to the initial position of the next line” (5.2.2 2). A `\r` should be unnecessary. If the system you are using does not both move the cursor to the next line and to the start of that line, it is not conforming to the C standard. – Eric Postpischil Jul 31 '19 at 17:56
  • @EricPostpischil Interesting. I was initially using just `\n` and could not figure out why things were messed up. I wondered if perhaps `\n` was sufficient in Unix but not Windows (CF/LF/CF+LF encoding). I added the `\r` and it fixed my issue, so I always use both now – InterLinked Jul 31 '19 at 17:59

1 Answers1

1

There are two problems here. I'll cover the easy, superficial one first. Your code is almost correctly converting characters to their binary representation. It's creating that representation as a string. (This probably isn't what you want; more on that in a bit.) But then it tries to print the string using printf's %x format. Now, %x is for printing numbers, not strings, and due to the sometimes-surprising way C treats arrays and pointers, if you have an array char binary[9] and you try to print it using %x, what you get is the address of the array, not the contents of the array. I think that's why you got the same value every time.

You also forgot to null-terminate the constructed binary string.

Here's a corrected version of your formatCallSign function. (I've cleaned up a few other things at the same time.)

void formatCallSign(unsigned char *callsign) {
   printf("original is %s\n",callsign);
   for (int j = 0; j < strlen(callsign); j++) {
      printf("Hex of %c is %x\n", callsign[j], callsign[j]);
      char binary[9];
      binary[0] = ( (callsign[j] & (1 << 7)) ? '1' : '0' );
      binary[1] = ( (callsign[j] & (1 << 6)) ? '1' : '0' );
      binary[2] = ( (callsign[j] & (1 << 5)) ? '1' : '0' );
      binary[3] = ( (callsign[j] & (1 << 4)) ? '1' : '0' );
      binary[4] = ( (callsign[j] & (1 << 3)) ? '1' : '0' );
      binary[5] = ( (callsign[j] & (1 << 2)) ? '1' : '0' );
      binary[6] = ( (callsign[j] & (1 << 1)) ? '1' : '0' );
      binary[7] = ( (callsign[j] & (1 << 0)) ? '1' : '0' );
      binary[8] = '\0';
      printf("Binary of %c is: %s\r\n", callsign[j], binary);
   }
}

With this function, when I call formatCallSign("CQ"), it prints

original is CQ
Hex of C is 43
Binary of C is: 01000011
Hex of Q is 51
Binary of Q is: 01010001

And that's fine, it makes sense, those are the correct binary representations of C and Q in ASCII.

But it's also much more trouble than it's worth -- and in fact it's pretty useless. You said your ultimate goal is to shift bits in those characters, but converting everything to a string array binary is going to make that problem much harder. (And we haven't even gotten to the problem of converting the binary strings back to real 8-bit characters.)

As you probably know, just about everything you manipulate in a C program -- characters, integers, floating-point numbers, strings -- is, deep down inside, already in binary. For example, the character C is already represented internally as 01000011. It only looks like the character C if you print it out using %c, and it only looks like the hexadecimal number 43 if you print it out using %x. Deep down inside it's still 01000011, and that didn't change when you printed it out in some other representation.

And since characters are already represented internally as binary numbers, if you want to shift a character's binary representation left by 1, you can do that with a simple, straightforward application of the << operator, no explicit conversion to binary beforehand required. Here's an encryptCallSign function that does what I think you had in mind:

void encryptCallSign(unsigned char *callsign) {
    for(int j = 0; j < strlen(callsign); j++) {
        unsigned char newchar = callsign[j] << 1;
        printf("%X -> %X\n", callsign[j], newchar);
        callsign[j] = newchar;
    }
}

I tried calling it from a little test main function like this:

main()
{
    char callSign[] = "CQ";
    formatCallSign(callSign);
    printf("encrypting...\n");
    encryptCallSign(callSign);
    printf("encrypted:\n");
    formatCallSign(callSign);
}

And this is the output I got:

original is CQ
Hex of C is 43
Binary of C is: 01000011
Hex of Q is 51
Binary of Q is: 01010001
encrypting...
43 -> 86
51 -> A2
encrypted:
original is ¢
Hex of  is 86
Binary of  is: 10000110
Hex of ¢ is a2
Binary of ¢ is: 10100010

This looks a little funny because the character with value A2 is the ¢ sign (in ISO-8601-1 "Latin1", anyway), and the character with value 86 doesn't have a printing representation.

Simple as it is, the encryptCallSign function I've shown is about twice as long as it needs to be. If it weren't printing out its work as it goes along, we could simplify it down to

void encryptCallSign(unsigned char *callsign) {
    for(int j = 0; j < strlen(callsign); j++)
        callsign[j] = callsign[j] << 1;
}

(You could also use C's "op =" shortcut callsign[j] <<= 1.)

There are some other issues here -- for example, my compiler correctly complains that there's some fishy intermixing of char * and unsigned char * types going on -- but this should hopefully get you pointed in the right direction.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • Very odd. When I use your encryptCallSign (though I'm not encrypting anything, just shifting bits), I do get the correct translation *per character*. However, when I have the following code in `main`: `printf("before %x", dest); formatCallSign(&dest); printf("after %x", dest);` - I get "before 88... the correct individual characters... after 88". I think it's referencing the memory location as you said. Should I combine your answer with the other and use strtol or sprintf to explicitly cast the string to binary? – InterLinked Aug 01 '19 at 14:10
  • @InterLinked Again, `%x` is **not** the right way to print a string of characters. – Steve Summit Aug 01 '19 at 14:18
  • I'm not in a position to post tested code just now, but try `printf("%s\n", callSign)` both before and after to call to `encryptCallSign`, or, if you want to see the hexadecimal representation of the characters (rather than the raw characters), try something like `for(i = 0; i < strlen(callSign); i++) printf("%02x ", callSign[i]);`. And to make sure you understand what's going on here, compare the behavior of the very similar loop `for(i = 0; i < strlen(callSign); i++) printf("%c ", callSign[i]);`. – Steve Summit Aug 01 '19 at 14:21
  • That's close enough to what I need it do that it suffices, later on I actually need to build a super array containing the ordered hex values of all the callsigns of which I shift the bits, so having it in an array is an advantage after all. Thanks! – InterLinked Aug 01 '19 at 14:23