【问题标题】:strcmp and void pointer in CC中的strcmp和void指针
【发布时间】:2015-07-23 08:30:21
【问题描述】:

我写了这个简单的字符串比较代码

    #include<stdio.h>

    void strCmp(char *,char *);

    int main() {
        char* str1 = "hello";
        char* str2 = "hello";
        strCmp(str1,str2);
        return 0;
    }
    void strCmp(char *vp1,char *vp2) {
        int r = strcmp(vp1,vp2);
        printf("result %d",r);
    }

然后为了检查如果我传递 void* 而不是 char* 会发生什么,我重写了如下代码:

    #include<stdio.h>

    void strCmp(void *,void *);

    int main() {
        char* str1 = "hello";
        char* str2 = "hello";
        strCmp(str1,str2);
        return 0;
    }
    void strCmp(void *vp1,void *vp2) {
        int r = strcmp(vp1,vp2);
        printf("result %d",r);
    }

上面的代码被编译并按预期工作!如果我更疯狂地去做

    #include<stdio.h>

    void strCmp(void **,void **);

    int main() {
        char** str1 = "helloxx";
        char** str2 = "hellox";
        strCmp(str1,str2);
        return 0;
    }
    void strCmp(void **vp1,void **vp2) {
        int r = strcmp(vp1,vp2);
        printf("result %d",r);
    }

没有失败,结果很好。我想知道 strcmp 的实现是什么,并将 https://stackoverflow.com/a/10927187/2304258 中的代码作为自定义函数复制粘贴到我的代码中,但是当您使用 void* 而不进行强制转换时,它会失败并出现您期望的常见错误消息。

您能解释一下上述实现是如何工作的吗?

【问题讨论】:

  • 这不能在 C++ 中编译。请不要标记不相关的语言。
  • 查看您的具体stdio.h。似乎strcmp 那里有int(const void*, const void*) 签名。
  • 这就是为什么你不应该忽略编译器警告。
  • 你的第三段代码是非法的。请以一致性模式调用您的编译器。
  • 代码编译不干净。缺少代码:'#include ' 最后一个示例由于语法错误而无法编译

标签: c pointers strcmp


【解决方案1】:

你的问题有两点。

首先是它真正起作用的原因。 第二个是您的程序是否保证始终有效。

第一点的答案是它可以工作,因为strcmp 只需要两个指针作为参数。这些指针的类型为char *,它们应该包含指向以空字符结尾的字符(字节)序列的地址。您的不同实现采用了这样两个指针,然后将它们转换为各种类型(在第一种情况下为void *,在第二种情况下为char **),然后将它们转换回char *,然后调用strcmp。它有效,因为在几乎所有真正的 C 实现中,将一种指针类型转换为另一种类型不会改变指针的值。因此,对于哪个指针以及您投射和重铸指针的次数都没有关系。请注意,在 C++ 中情况有所不同,因为强制转换经常改变实际地址(指针的值)。

第二点是你的程序是否符合 C 标准。在这种情况下,第一个是正确的,因为 C 标准说您始终可以将任何指针转换为 void *,并且当您将其转换回原始类型时,您将返回原始指针。但是,这不适用于转换为其他类型,因此不能保证在将 char * 转换为 char ** 并返回到 char * 后,您会得到原始指针。 C 语言不保证您的最后一个程序能够正常工作。

更准确地说,这里有两个来自 C99 标准的相关引用:

6.3.2.3 指针

1.) 指向 void 的指针可以转换为指向任何不完整或对象的指针或从指针转换为指向任何不完整或对象的指针 类型。指向任何不完整或对象类型的指针都可以转换为指向 void 的指针 又回来了;结果应与原始指针比较。

...

7.) 指向对象或不完整类型的指针可以转换为指向不同类型的指针 对象或不完整类型。如果生成的指针未正确对齐 57) 指向类型,行为未定义。否则,当再次转换回来时, 结果应与原始指针比较。当指向对象的指针转换为指向字符类型的指针时,结果指向的最低地址字节 物体。结果的连续递增,直到对象的大小,产生指针 到对象的剩余字节。

【讨论】:

    【解决方案2】:

    嗯,首先,你真的应该这样做

    #include <string.h>
    

    其次,强烈建议您使用 strncmp()。最后,这就是 C 的工作方式(与 C++ 不同)。它将执行从 void* 到任何其他指针的隐式转换(这就是例如 malloc() 正常工作的原因)。另见How to interpret section 6.3.2.3 part 7 of the C11 standard?

    至于为什么来自 libc 的代码不适合你,我不知道。它确实对我有用(在我修复了缺少的类型声明之后)。

    【讨论】:

    • 请注意,此代码不使用特殊规则来转换void*。它只是将不兼容的指针类型分配给彼此,这是值得警告的。
    【解决方案3】:

    之所以有效,是因为 C 几乎总是假定程序员知道它在做什么。

    即使 str1 和 str2 被声明为 char**,它们实际上也包含指向 char 的指针

    C 自动将指向任何类型的指针转​​换为指向void 的指针

    所以最后,你将char * 传递给strcmp,它就可以完成它的工作了。

    但是当我编译它时,我收到了警告(即使在添加 #include &lt;string.h&gt; 以对 strcmp 进行正确声明之后):

    foo.c(7) : warning C4047: 'initialisation' : 'char **' diffère de 'char [8]' dans les niveaux d'indirection
    foo.c(8) : warning C4047: 'initialisation' : 'char **' diffère de 'char [7]' dans les niveaux d'indirection
    foo.c(13) : warning C4047: 'fonction' : 'const char *' diffère de 'void **' dans les niveaux d'indirection
    foo.c(13) : warning C4024: 'strcmp' : types différents pour le paramètre formel et réel 1
    foo.c(13) : warning C4047: 'fonction' : 'const char *' diffère de 'void **' dans les niveaux d'indirection
    foo.c(13) : warning C4024: 'strcmp' : types différents pour le paramètre formel et réel 2
    

    在英语中应该或多或少:

    foo.c(7) : warning C4047: 'init' : 'char **' and 'char [8]' have different indexation level
    foo.c(8) : warning C4047: 'initialisation' : 'char **' and 'char [7]'  have different indexation level
    foo.c(13) : warning C4047: 'function' : 'const char *' and 'void **' have different indexation level
    foo.c(13) : warning C4024: 'strcmp' : different types for formal and actual parameter 1
    foo.c(13) : warning C4047: 'function' : 'const char *' and  'void **' dhave different indexation level
    foo.c(13) : warning C4024: 'strcmp' : different types for formal and actual parameter 2
    

    不应忽略这些警告,因为它们会显示代码中的实际问题。

    【讨论】:

    • 英文是“function”
    猜你喜欢
    • 2012-07-01
    • 1970-01-01
    • 2019-12-05
    • 2011-04-01
    • 1970-01-01
    • 2010-09-29
    相关资源
    最近更新 更多