了解memcmp的返回值[已关闭]

pcww981p  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(130)

**已关闭。**此问题为not reproducible or was caused by typos。目前不接受回答。

这个问题是由错字或无法再重现的问题引起的。虽然类似的问题在这里可能是on-topic,但这个问题的解决方式不太可能帮助未来的读者。
23天前关闭
Improve this question
我写了一个函数来模拟memcmp,在测试过程中我意识到memcmp有一些不寻常的地方。库函数的源代码应该是:

#include <ansidecl.h>
#include <stddef.h>

int
memcmp(const void *str1, const void *str2, size_t count)
{
  register const unsigned char *s1 = (const unsigned char *)str1;
  register const unsigned char *s2 = (const unsigned char *)str2;

  while (count-- > 0)
    {
      if (*s1++ != *s2++)
        return s1[-1] < s2[-1] ? -1 : 1;
    }
  return 0;
}

字符串
我的是

#include <stddef.h>

int ft_memcmp(const void *s1, const void *s2, size_t n)
{
    size_t  i;

    i = 0;
    if (n == 0)
        return (0);
    while(((char *)s1)[i] == ((char *)s2)[i])
    {
        if ((((char *)s1)[i] == '\0') || (i == (n - 1)))
        {
            return (0);
        }
        i++;
    }
    if (((char *)s1)[i] < ((char *)s2)[i])
        return (-1);
    else
        return (1);
}


意外的问题是,根据提供的计数,memcmp函数开始返回的不是1-1,而是与字符的实际差值,即s1[-1] - s2[-2]
有谁知道这是怎么回事吗?
这是我运行的测试,

int main() {
    char *test_strings1[] = { "fdjkDKDJFLDkjdfkjdf", "-456", "ALO marciano!!!", "xc42:", "     7894545989828547", "  +99", "abc123", "12abc", "" };

    char *test_strings2[] = { "fdjkDKDJFLDSkjdfkjdf", "-456", "ALO_ALO marciano!!!", "xc42", "   789454598982854752", "  +99", "abc123", "12abc", "" };

    for (int count = 0; count < 50; count++)
        for (int string = 0; string < 9; string++) {
            int ft = ft_memcmp(test_strings1[string], test_strings2[string], count);
            int lib = memcmp(test_strings1[string], test_strings2[string], count);
       
            if (ft != lib) {
                printf("******Wrong!!!  lib  %i  ft  %i  count = %i  string = %i*********\n", lib,  ft, count, string);
            }
        }
    return 0;
}


GCC版本是

gcc --version
Ubuntu clang version 12.0.0-3ubuntu1~20.04.5
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin


我的系统是Ubuntu 20.04.5 LTS,英特尔®酷睿™ i5- 7360 U CPU@2.30 GHz × 4,梅萨英特尔®虹膜(R)加图形640(Kaby Lake GT3 e)(KBL GT3)
这是我得到的输出,这让我很惊讶。在所有有差异的字符串(索引2,3和4)上,如果count大于7memcmp的行为就会改变(在这个版本的输出中,我的自定义函数返回的是差异,而不是1,-1和0)

Wrong!!!  lib  -1  ft  -63  count =  4  string =  2
Wrong!!!  lib  -1  ft  -23  count =  4  string =  4
Wrong!!!  lib  -1  ft  -63  count =  5  string =  2
Wrong!!!  lib  +1  ft  +58  count =  5  string =  3
Wrong!!!  lib  -1  ft  -23  count =  5  string =  4
Wrong!!!  lib  -1  ft  -63  count =  6  string =  2
Wrong!!!  lib  +1  ft  +58  count =  6  string =  3
Wrong!!!  lib  -1  ft  -23  count =  6  string =  4
Wrong!!!  lib  -1  ft  -63  count =  7  string =  2
Wrong!!!  lib  +1  ft  +58  count =  7  string =  3
Wrong!!!  lib  -1  ft  -23  count =  7  string =  4

7gyucuyw

7gyucuyw1#

来自C标准(7.24.4.1 memcmp函数)

返回

3**memcmp函数返回一个大于、等于或小于零的整数,**相应地,s1指向的对象大于、等于或小于s2指向的对象。

yhqotfr8

yhqotfr82#

C标准(C2x)规定:

7.26.4比较功能

比较函数memcmpstrcmpstrncmp返回的非零值的符号由第一对字符(均解释为unsigned char)的值之间的差的符号确定,这两个字符在被比较的对象中不同。
[...]
memcmp函数返回一个大于、等于或小于零的整数,相应地,s1指向的对象大于、等于或小于s2指向的对象。
memcmp的发布代码只是一个可能的实现。C标准没有为不同内容的块指定确切的返回值,只是返回值的符号。因此返回s1[-1] - s2[-1]与返回s1[-1] < s2[-1] ? -1 : 11 - 2 * (s1[-1] < s2[-1])一样符合要求
还请注意,您的实现存在问题:

  • 测试((char *)s1)[i] == '\0')是没有意义的,因为memcmp不会在任何空终止符上停止。
  • 将字节值作为char值进行比较是不正确的:C标准规定必须使用unsigned char值进行比较。

以下是修改后的版本:

#include <stddef.h>

int ft_memcmp(const void *s1, const void *s2, size_t n)
{
    size_t  i;

    i = 0;
    if (n == 0)
        return (0);

    while (((unsigned char *)s1)[i] == ((unsigned char *)s2)[i])
    {
        i++;
        if (i == n)
        {
            return (0);
        }
    }
    if (((unsigned char *)s1)[i] < ((unsigned char *)s2)[i])
        return (-1);
    else
        return (1);
}

字符串
main函数不正确:

  • 它必须只检查零值,如果ftlib都不为零,则必须检查它们的符号是否匹配。
  • 传递给两个函数的count不应该超过对象的大小。这解释了为什么对于相同的字符串,当count大于7时,会得到不一致的结果:前8个字节或相同字符串之外的内存内容不同。阅读字符串末尾之外的字节(即:空终止符之后)无论如何都有未定义的行为。

以下是修改后的版本:

int main(void) {
    const char *test_strings1[] = {
        "fdjkDKDJFLDkjdfkjdf", "-456", "ALO marciano!!!", "xc42:",
        "     7894545989828547", "  +99", "abc123", "12abc", ""
    };
    const char *test_strings2[] = {
        "fdjkDKDJFLDSkjdfkjdf", "-456", "ALO_ALO marciano!!!", "xc42",
        "   789454598982854752", "  +99", "abc123", "12abc", ""
    };
    int test_count = sizeof(test_strings1) / sizeof(*test_strings1);

    for (int i = 0; i < test_count; i++) {
        size_t size1 = strlen(test_strings1[i]) + 1;
        size_t size2 = strlen(test_strings2[i]) + 1;
        for (size_t count = 0; count <= size1 && count <= size2; count++) {
            int ft = ft_memcmp(test_strings1[i], test_strings2[i], count);
            int lib = memcmp(test_strings1[i], test_strings2[i], count);
       
            if ((ft < 0 && lib >= 0) 
            ||  (ft == 0 && lib != 0)
            ||  (ft > 0 && lib <= 0)) {
                printf("*** Wrong!!!  lib  %i  ft  %i  count = %zu  string = %i ***\n",
                       lib, ft, count, i);
            }
        }
    }
    return 0;
}

az31mfrm

az31mfrm3#

原始函数(非常简化)是:

int mymemcmp(const void *s1, const void *s2, const size_t n)
{
    const unsigned char *a = s1;
    const unsigned char *b = s2;
    int result = 0;

    for(size_t index = 0; index < n; index ++)
    {
        if(a[index] != b[index])
        {
            result = a[index] - b[index];
            break;
        }
    }
    return result;
}

字符串

相关问题