Разница в передаче двойного указателя на функцию в C и C ++ - PullRequest
0 голосов
/ 04 июня 2019

В parseFunc (), если я вызываю loadFunc (), передавая & tIpArray, он прекрасно компилируется при использовании C в качестве компиляции (в onlinegdb.com), но жалуется, как показано ниже, в более поздней версии компилятора C (gcc-bin / 4.9. 4 / gcc) на работе.

Ошибка на работе похожа на ту, что я вижу при компиляции при использовании языка C ++ в onlinegdb. Может кто-нибудь сказать мне, почему компилятору C 4.9.4 gcc не нравится & и как правильно это сделать?

/******************************************************************************

Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby, 
C#, VB, Perl, Swift, Prolog, Javascript, Pascal, HTML, CSS, JS
Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>

typedef struct _MyStruct
{
    char string[72];
}MyStruct;

int loadFunc(MyStruct** ipData, int counter)
{
    MyStruct **currentVal = ipData;

    MyStruct *ipPtr = NULL;

    printf("%p\n", currentVal);

    for(int i=0; i<counter; i++)
    {
        ipPtr = currentVal[i];
        printf("Line-%d - %s - %p\n", i+1, ipPtr->string, ipPtr);
    }
}

int parseFunc()
{
        MyStruct*   tIPArray[3];

        for(int i=0; i< 3; i++)
        {
            tIPArray[i] = 0;
            tIPArray[i] = (MyStruct*) calloc(1, sizeof(MyStruct));
            snprintf(tIPArray[i]->string, 72, "Test-String-%d", i+101);         
        }        

        for(int i=0; i<3; i++)
        {
            printf("Line-%d %p-%p- %s\n", i+1, &tIPArray[i], tIPArray[i], tIPArray[i]->string);
        }

        // call the load/ print loadFunc
        loadFunc(&tIPArray, 3);
}

int main()
{
    printf("Hello World\n");
    parseFunc();
    return 0;
}

Ниже приведена ошибка, которую я получаю на работе

error: passing argument 1 of 'loadFunc' from incompatible pointer type [-Werror]
   loadFunc(&tIPArray, 3);
note: expected 'struct MyStruct **' but argument is of type 'struct MyStruct * (*)[(sizetype)((int)((short unsigned int)getMaxAddressObjects() / 4u))]'
 int loadFunc(MyStruct **ipData, int counter)

Ниже приведена ошибка при построении этого в onlinegdb.com в виде файла C ++

main.cpp: In function ‘int parseFunc()’:
main.cpp:49:38: error: cannot convert ‘MyStruct * (*)[3] {aka MyStruct * (*)[3]}’ to ‘MyStruct ** {aka MyStruct **}’ for argument ‘1’ to ‘int loadFunc(MyStruct **, int)’
     loadFunc(&tIPArray, 3);

Ответы [ 2 ]

2 голосов
/ 04 июня 2019

Возможно, это будет понятнее, если мы используем typedef MyStruct* StrPtr;.Тогда ваш пример становится следующим:

void foo(StrPtr* bar);

StrPtr array[4];

Сначала давайте посмотрим на этот вызов:

foo(array);

Массив имеет тип StrPtr[4], а не StrPtr*.В идеале это вызовет foo(StrPtr bar[4]), но такой функции нет.Следующим лучшим совпадением является то, что массивы могут распадаться на указатели на свои элементы = StrPtr*, и, к счастью, есть функция foo(StrPtr* bar), поэтому вызов действителен.

Теперь, каков тип этого выражения?

&array;

Ну, опять же, массив имеет тип StrPtr[4], так что это должен быть указатель на этот тип = StrPtr(*)[4].Тот же самый "странный" синтаксис, что и для указателей на функции.

Наконец, примите этот вызов:

foo(&array);

Теперь мы знаем, что для этого нужно вызвать foo(StrPtr(*bar)[4]), и снова такой функции нет,Так что же может сделать компилятор?&array это не массив, это указатель, и указатели не могут распадаться ни на что.Хм, а что теперь?Ну, в C любой указатель может быть передан другому независимо от типа.Разыменование такого указателя - другое дело.Таким образом, этот вызов действителен и вызывает foo(StrPtr* bar), так как другого кандидата нет.Успех?Нет, любой достойный компилятор должен предупреждать об этом, даже лучше превратить эти предупреждения в ошибки, используя -Wincompatible-pointer-types, или даже запретить все предупреждения с помощью -Werror, как вы это сделали.

Таким образом, вывод состоит в том, что правильный вызовfoo(array);.

2 голосов
/ 04 июня 2019

Эта декларация ...

        MyStruct*   tIPArray[3];

объявляет tIPArray как массив из 3 MyStruct *. Следовательно, &tIPArray - указатель на такой массив, его тип - MyStruct *(*)[3], как говорится в сообщении об ошибке. Это не тот же тип, что и MyStruct **, который является ожидаемым типом аргумента функции.

Вместо этого вы можете передать явный указатель на первый элемент ...

        loadFunc(&tIPArray[0], 3);

... но было бы более идиоматичным просто опустить &, так как обычное преобразование массива в указатель («распад» массива) производит указатель правильного типа и значения:

        loadFunc(tIPArray, 3);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...