简体   繁体   中英

strcmp and void pointer in C

I wrote this simple code for string comparison

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

And then to check what will happen if I pass void* instead of char*, I rewrote the code like this:

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

The above code gets compiled and works as expected! If I go crazy further and do

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

No failure and the results are fine. I wondered what is the implementation of strcmp and copy pasted the code from https://stackoverflow.com/a/10927187/2304258 as a custom function in my code but it fails with the usual error messages you expect when you use a void* without casting.

Can you please explain how the above implementations are working?

There are two points in your question.

The first is why it actually works. The second is whether your program is guaranteed to always work.

The answer to the first point is that it works because strcmp simply expects two pointers as parameters. Those pointers are of type char * , they are supposed to contain addresses to null terminated sequences of chars (bytes). Your different implementations took such two pointers, then cast them to various types (to void * in the first case, char ** in the second), then cast them back to char * and then call the strcmp . It works, because in practically all real C implementations, the cast of one pointer type to another does not change the value of the pointer. So it does not matter to which pointer and how many times you cast and recast the pointer. Note that in C++ the situation is different, because the cast often changes the actual address (value of the pointer).

The second point is whether your programs are correct according to the C standard. In this case the first one is correct, because C standard says that you can always cast any pointer to void * and when you cast it back to the original type, you get back the original pointer. However, this is not true for casting to other types, so you are not guaranteed that after casting char * to char ** and back to char * you get the original pointer. The C language does not guarantee that your last program will work.

To be more precise, here are two relevant quotes from C99 standard:

6.3.2.3 Pointers

1.) A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

...

7.) A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned57) for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

Well, first of all, you really ought to

#include <string.h>

Secondly, it's highly recommended that you use strncmp(). Finally, this is really just how C works (which is different from C++). It will do an implicit cast from void* to any other pointer (which is why eg malloc() just works). See also How to interpret section 6.3.2.3 part 7 of the C11 standard? .

As for why the code from libc didn't work for you, I don't know. It certainly worked for me (after I fixed the missing type declaration).

It works because C almost always assumes that programmers knows what it does.

Even if str1 and str2 are declared as char** they actually contain a pointer to char

C automatically converts from and to pointer to any type to pointer to void

So in the end, you pass char * to strcmp and it can do its job.

But when I compiled it I got warnings (even after adding #include <string.h> to have a correct declaration for 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

In english it should be more or less:

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

Those warning should not be ignored because they to show real problems in the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM