2

I have a very simple function to convert a 3 char string representing a bit string to a decimal number:

int bin3_to_dec(char *bin) {
  int result;

  result=0;
  printf("string: %s\n", bin);
  printf("c0: %c\n", bin[0]);
  printf("c1: %c\n", bin[1]);
  printf("c2: %c\n", bin[2]);

  if ((strcmp(&bin[0], "1") == 0))
    result += 4;
  if ((strcmp(&bin[1], "1") == 0))
    result += 2;
  if ((strcmp(&bin[2], "1") == 0))
    result += 1;
  printf("result: %d\n", result);
  return result;
}

When I run the program and feed this function the string 111 it should calculate 7. Instead it outputs this:

string: 111
c0: 1
c1: 1
c2: 1
result: 1

Why is it not calculating the correct value? Why is only the third condition successfully passing?

ErwinM
  • 5,051
  • 2
  • 23
  • 35

7 Answers7

5

Your string bin equal "111" really consists of four chars - that is '1', '1', '1', '\0' where the 4th char has the value zero which terminates (i.e. ends) the string.

So &bin[0] is the string "111"

and &bin[1] is the string "11"

and &bin[2] is the string "1"

So what your code is actually doing is the same as:

  if ((strcmp("111", "1") == 0))
    result += 4;
  if ((strcmp("11", "1") == 0))
    result += 2;
  if ((strcmp("1", "1") == 0))
    result += 1;

Only the last compare results in true so resultbecomes 1

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
2

&bin[0] is actually a pointer to a character array starting from 0th index which is 111. So, your first comparison fails. Similarly for second. But in your third comparison, &bin[2] is a pointer to character array starting from 2nd index which is 1 and hence it add 1 to result. So to make your code work:

you can check if(bin[0] == '1') // Here you compare the character at bin[0] and it is equal to 1 and so here the condition gets fulfilled.

Priyansh Goel
  • 2,660
  • 1
  • 13
  • 37
  • Aha...why is it calculating 1 then though and not 0? – ErwinM Jul 10 '16 at 15:53
  • He is passing the address of the first character (`&bin[0]`), so indeed it is a string. – Cody Gray - on strike Jul 10 '16 at 15:53
  • @CodyGray : I didn't see that. Thanks for correcting. I have updated my answer. – Priyansh Goel Jul 10 '16 at 15:57
  • @CodyGray: `&bin[0]` is not a string! Itg is simply a pointer to `char`. Treating as a **pointer** to a string is just convention of some functions (including `strcmp`). C does not have a string type. – too honest for this site Jul 10 '16 at 16:02
  • @Olaf : Yes. I know. I was just trying to simplify it in simple terms. – Priyansh Goel Jul 10 '16 at 16:03
  • @PriyanshGoel: That's alway a bad idea. This site is used by beginners mostly who take it literally and learn such missconceptions. If you have to simplify, always add the correct version. – too honest for this site Jul 10 '16 at 16:04
  • The convention is exactly what I was referring to, wherein a pointer to an array of characters that ends with a NUL-terminator is said to be a C-style string. No one can possibly get confused about what is meant, @Olaf, since as you point out, C doesn't have a native string type. – Cody Gray - on strike Jul 10 '16 at 16:05
  • @Olaf : Updated my answer as per your suggestions :) – Priyansh Goel Jul 10 '16 at 16:06
  • 1
    @CodyGray: There is a difference betwee library semantics and language semantics. This should be strongly emphasised. It is a common source of confusion by beginners. – too honest for this site Jul 10 '16 at 16:11
  • I don't understand what there is for beginners to get confused about, @Olaf. I could see how this would potentially be confusing in C++, where you couldn't be sure if someone was talking about the actual string type or a C-style string. But there is absolutely no confusion in C. I also think making the distinction here between library semantics and language semantics is arbitrary and unfair. This aspect of the library can reasonably be considered part of the language. It's not like someone went back later and hacked this into the language. K&R talked about strings this way. – Cody Gray - on strike Jul 11 '16 at 04:30
  • @CodyGray: K&R is far from being standard C and is a very bad reference for terms nowadays. Maybe you use more recent material, i.e. something which is not >17 years outdated (presuming the 2nd Revision, the first is >27 years) now. Best would be the standard. – too honest for this site Jul 11 '16 at 07:36
  • @olaf You are missing the point by several miles. You invented an arbitrary distinction between "library semantics" and "language semantics". That distinction is not relevant here. According to the language semantics, a "string" in C is a pointer to an array of NUL-terminated characters. Yes, K&R is out of date; that is not the point. The point is, they invented the language and talked about strings this way. It wasn't someone else who came along later and invented some new library semantics. Calling it inaccurate just makes you look like a nutcase. – Cody Gray - on strike Jul 11 '16 at 07:47
  • What exactly do you want me to say? Each time I want to talk about a C string, you think I should type "a pointer to an array of NUL-terminated characters"? Sorry, not going to happen. When you talk about things, you assume a certain baseline understanding. I don't explain what a pointer is every time I use the word, either. If you don't understand the semantics, Google it. – Cody Gray - on strike Jul 11 '16 at 07:49
  • @CodyGray: In your personal world this might be true. but 1) A **pointer** is never a C-string. That would be **an array**, which is a distinct type and not a pointer! 2) It very well is a convention that the of the library. The fact that string literals arte stored like that is the only support the language itself gives. And that is not even complete. You finally should get it right and not spread such nonsense. – too honest for this site Jul 11 '16 at 10:52
1
if (bin[0] == '1') result += 4;
if (bin[1] == '1') result += 2;
if (bin[2] == '1') result += 1;

please note that &bin[0] is the same as bin

bin[0] is the first element

&bin[0] is a pointer to the first element just like bin

Szabolcs Dombi
  • 5,493
  • 3
  • 39
  • 71
1

C does not detect the end of a string until it encounters a null (i.e. \0). When you pass "111" into your function, you are actually passing a pointer to a block of memory that looks like this: "111\0". Thus, when you pass the address of bin[0] into strcmp(), strcmp operates on the full string "111". When you pass the address of bin[1] into strcmp(), strcmp operates on the string "11". Only when you pass the address of bin[2] into strcmp() do you get the behavior you were expecting, because in that case the next character in memory is a null.

Matthew Nizol
  • 2,609
  • 1
  • 18
  • 22
1

Have you tried printing &bin[1] (for example) as a string, instead of the individual characters? Because that's how strcmp() is going to see them.

In you do that, strcmp(&bin[0], "1") is clearly always non-zero, because &bin[0] is the full input string and (in our example) "111" is not at all like "1". Strings run until the null-terminator character.

You can use direct character comparisons (bin[0] == '1'), copy the character to a null-terminated string of its own, or (destructively) work from right to left and insert the null character ('\0') after the character that interests you. But you can't compare the middle of a string as a single character.

John C
  • 1,931
  • 1
  • 22
  • 34
  • 1
    This is a place where `strncmp()` could be used to compare a fixed length sub-string in the middle of a bigger string, but it seems like overkill compared with simply comparing single characters when the sub-string is of length 1, as it would be here for this question. (But, in general, `strncmp(&src[off1], &dst[off2], len)` can compare two substrings of up to `len` characters quite happily.) – Jonathan Leffler Jul 10 '16 at 20:09
-1

As mentioned by others, you are confusing those strings. The three of them are strings, however, string in java is an array of chars. So when you mean the string "1" using &bin[0], you are actually comparing "111". Why? Pointer to an array of chars make a string with the chars in this array starting where your pointer shows and continues to the end.

So when you point at the first letter you get "111", when you point at the second letter you get "11" and when you point at the last character you get "1", thats why your sum is 1. You can try to pass as argument the string "1111", you can see that your result is 0 intead of 1.

hzzhyj
  • 3
  • 4
-1

Your code seems somehow baffled around a function call of strcmp(), which isn't needed and would consistently return non-zero for any comparison between the string literal "1" and any "sub-string" pointed to in your code (&bin[0], &bin[1]), except for the one-printable-member-string &bin[2], if also "1". Let's have a walk-through.

As you have properly written in your function prototype, the pointer to the first element of the character array is being passed by value to your called function and copied as its argument. This is the "mechanism" for the part of memory populated by the array pointed to, to become visible by the called function, if its "upper bound" is known.
There are two means of having its upper bound known:

  1. Passing the char array size as additional argument to the function, or
  2. Null-terminating the char array in the calling function, so the called function can interpret it as string, which is your choice. The called function can either use strlen() to determine string length (size), or step through it, incrementing the counter, until a null-character is reached, and read size from the counter.

If the calling function already receives the array as a null-terminated string of '0' and '1' characters, the second looks more practical.

Allowing for up to as many characters as allowed by the storage capacity of the return data type of the called function (in this case int) clarifies the problem and simplifies the code. The caller function should guard against overflow.

The called function should only compare every array member's ASCII value with '1' and convert if equal. For this strcmp isn't needed.

Please see the comments in this demonstration code, based on your post:

#include <stdio.h>
#include <string.h>
#define BPB 8   //bits per byte

int bin_to_dec(char *bin)            //called function 
{
  int result=0;  
  int l = (int)strlen(bin);
  for (int i = 0; i < l; i++){
      printf("c%d: %c\n", i, bin[i]);
      if(bin[i] == '1')               //compare value of the i-th element
          result += 1<<(l - i - 1);   //convert to power-of-two and add to the result
      }
  return result;
}

int main(int argc, char *argv[])      //calling function
{  
   size_t siz = BPB*sizeof (int); //size for each char to represent one bit
   char s[siz + 1];               //characters, each representing one bit + terminating '\0'

   if((argc < 2)||(argc > 2))     //fail-safe check for correct count of arguments
       return 1;
   size_t len = strlen(argv[1]) ; //get length of the input string
   if ( len > siz )               //check against too long input which would cause overflow
       return 2; 
   strncpy(s, argv[1], len);
   s[len] = '\0';                 //appending the terminating null-character
   for(int i = 0; i < (int)len; i++)
       if((s[i] < '0')||(s[i] > '1')) //fool-proof check against 'off-limit' input
           return 3;

   printf("decimal: %d\n", bin_to_dec(s));
   return 0;
}
user3078414
  • 1,942
  • 2
  • 16
  • 24