对 strcmp 感到困惑

Baffled by strcmp

我有一个非常简单的函数可以将表示位串的 3 个字符的字符串转换为十进制数:

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

当我 运行 程序并为该函数提供字符串 111 时,它应该计算 7。相反,它输出:

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

为什么计算不正确?为什么只有第三个条件成功通过?

&bin[0] 实际上是一个指向字符数组的指针,从第 0 个索引开始,即 111。因此,您的第一次比较失败了。第二个也是如此。但是在你的第三次比较中, &bin[2] 是一个指向字符数组的指针,从第二个索引开始,它是 1,因此它将结果加 1。所以为了让你的代码工作:

you can check if(bin[0] == '1') // 这里比较 bin[0] 处的字符,它等于 1,所以这里的条件得到满足。

if (bin[0] == '1') result += 4;
if (bin[1] == '1') result += 2;
if (bin[2] == '1') result += 1;

请注意 &bin[0] 与 bin

相同

bin[0]为第一个元素

&bin[0] 是指向第一个元素的指针,就像 bin

C 在遇到空值(即 \0)之前不会检测字符串的结尾。当您将“111”传递给您的函数时,您实际上传递了一个指向如下所示的内存块的指针:“111[=12=]”。因此,当您将 bin[0] 的地址传递给 strcmp() 时,strcmp 将对完整字符串“111”进行操作。当您将 bin[1] 的地址传递给 strcmp() 时,strcmp 对字符串“11”进行操作。只有当您将 bin[2] 的地址传递给 strcmp() 时,您才会得到预期的行为,因为在这种情况下,内存中的下一个字符为空。

您是否尝试过将 &bin[1](例如)打印为字符串而不是单个字符?因为这就是 strcmp() 看到他们的方式。

在你这样做时,strcmp(&bin[0], "1") 显然总是非零的,因为 &bin[0] 是完整的输入字符串并且(在我们的示例中)"111" 根本不像 "1"。字符串 运行 直到空终止符。

您可以使用直接字符比较 (bin[0] == '1'),将字符复制到它自己的以 null 结尾的字符串,或者(破坏性地)从右到左工作并插入空字符 ('[=17 =]') 在您感兴趣的字符之后。但是您不能将字符串的中间部分作为单个字符进行比较。

您的字符串 bin 等于“111”实际上由四个字符组成 - 即“1”、“1”、“1”、“\0”,其中第 4 个字符的值为零,终止(即结束)字符串。

所以 &bin[0] 是字符串 "111"

&bin[1]是字符串"11"

&bin[2]是字符串"1"

那么您的代码实际执行的操作与:

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

只有最后一次比较结果为真,所以 result 变成 1

正如其他人所提到的,您混淆了这些字符串。这三个都是字符串,但是 java 中的字符串是一个字符数组。因此,当您使用 &bin[0] 表示字符串“1”时,您实际上是在比较“111”。为什么?指向字符数组的指针构成一个字符串,该数组中的字符从指针显示的位置开始,一直持续到结尾。

因此,当您指向第一个字母时,您会得到“111”,当您指向第二个字母时,您会得到“11”,当您指向最后一个字符时,您会得到“1”,这就是为什么您的总和是1. 你可以尝试将字符串“1111”作为参数传递,你可以看到你的结果是 0 而不是 1.

您的代码似乎对 strcmp() 的函数调用感到困惑,这不是必需的,并且对于字符串文字 "1" 和之间的任何比较始终 return 非零代码中指向的任何 "sub-string"(&bin[0]&bin[1]),除了 one-printable-member-string &bin[2],如果还有 "1"。让我们来看看吧。

正如您在 函数原型 中正确编写的那样,指向 字符数组 第一个元素的指针是 通过值 传递给您的 调用的函数 并复制为它的参数。这是 "mechanism" 用于由指向的数组填充的内存部分,如果其 "upper bound" 已知,则由 调用的函数 可见。
有两种方法可以知道其上限:

  1. char 数组大小作为附加参数传递给函数,或
  2. 调用函数中以空值终止char数组,因此被调用函数可以将其解释为字符串,这是你的选择。 调用的函数 可以使用 strlen() 来确定字符串长度(大小),或者单步执行它,递增计数器,直到到达空字符,然后从中读取大小柜台.

如果 调用函数 已经收到数组作为 '0''1' 字符的空终止字符串,第二个看起来更实用。

根据 被调用函数 的 return 数据类型的存储容量允许最多允许的字符数(在本例中为 int ) 阐明了问题并简化了代码。 调用函数 应防止溢出。

调用的函数 应该只将每个数组成员的 ASCII 值与 '1' 进行比较,如果相等则进行转换。 为此 strcmp 不是必需的。

请参阅此演示代码中的注释,基于您的 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 '[=10=]'

   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] = '[=10=]';                 //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;
}