Приведение массива указателей из одного типа в другой

Почему следующая программа не работает?

void test(int*[]);
int main()
{
    char *a[2];
    test((int*[])a);
    
    return 0;
}

void test(int* c[]){

}

Это показывает

ошибка приведения к неполному типу 'int*[]' test((int*[])a)

Проблему можно устранить, используя:

test((int**)a);

Но что не так с предыдущим кодом?

Обновлено: в объявлении прототипа функции внутри круглых скобок используется то же самое, что и приведение.

(целое*[])

Так почему же это не неполный тип?

int * и char * — разные типы. Компилятор диагностирует это. Если вы попытаетесь скрыть ошибку с помощью приведения, поведение во время выполнения будет неопределенным.
M.M 03.07.2024 05:49

Это неправильно. Запускается следующая программа: void test(int*); int main() { char* a; тест(а); вернуть 0; } void test(int* c){ }

Cryogen 03.07.2024 05:52

Эта программа неправильно сформирована и должна генерировать диагностику. Кроме того, он принципиально отличается от этого. Там вы пытаетесь преобразовать char * в int *; в этом вопросе вы пытаетесь интерпретировать char * как int *

M.M 03.07.2024 06:00

Я не уверен, что приведение всего массива за раз правильно, попробуйте приводить каждый член отдельно.

Null 03.07.2024 06:16

«Что не так с предыдущим кодом?» --> int*[] — неполный тип, не подходящий для кастинга.

chux - Reinstate Monica 03.07.2024 06:18

Это неправильно. Запускается следующая программа — это бессмысленно. Тот факт, что код компилируется и выполняется, не означает, что он корректен — это просто означает, что синтаксис работает. Вы можете скомпилировать и запустить ужасный код, содержащий логические ошибки и перезапись памяти, и даже вам повезет, и он проработает минуту, час, неделю или год. Это не меняет того факта, что это неправильно.

Ken White 03.07.2024 06:36

Конечно. Но где синтаксическая ошибка? Вот что я хотел знать. В приведенном выше объявлении прототипа функции я использовал то же самое, что и приведение (int*[]). Так почему же это не неполный тип?

Cryogen 03.07.2024 06:49

В списке параметров функции типы массивов (на верхнем уровне) настраиваются на типы указателей.

M.M 03.07.2024 06:54

@Cryogen Нет синтаксической ошибки, есть ошибка ограничения, означающая, что вы нарушаете обязательные правила языка C.

Lundin 03.07.2024 09:11

Обработка массива char как массива int также является строгим нарушением псевдонимов и сама по себе вызывает неопределенное поведение, а также может вызывать неопределенное поведение, если int имеет более строгие требования к выравниванию, чем char.

Andrew Henle 03.07.2024 14:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в том, что int *[] — это неполный тип, экземпляр которого невозможно создать.

В объявлении функции int *[] в точности эквивалентен int **, поскольку фактический аргумент, передаваемый функции, в любом случае будет «указателем на указатель на int». Следовательно, в контексте объявления функции int *[] фактически относится к полному типу (указатель на указатель на int).

Однако в приведении (int *[])a сообщает компилятору создать временный тип int *[], а это недопустимо, поскольку это неполный тип.

Хотя мы не можем иметь значения неполного типа, мы можем иметь указатели на такие значения, поэтому компилируется следующее (только ради примера; это не рекомендуемая конструкция) — для более четкой записи используется typedef:

typedef int * incompleteIntArray[];

void test(int*[]);
int main()
{
    char *a[2];
    test(*(incompleteIntArray *)&a);
}

Дело в том, что хотя incompleteIntArray (он же int *[]) является неполным типом, указатель на этот тип им не является. Следовательно, указатель &a на char *[2] можно привести к указателю на int *[], и мы можем разыменовать его, чтобы создать аргумент функции. Однако это работает только потому, что массивы всегда передаются по ссылке, поэтому фактический аргумент — это указатель, а не экземпляр int *[].

Проблема не в этом. Проблема в том, что приведение типов массива не допускается.

Lundin 03.07.2024 08:59

@Lundin Я думаю, это вопрос точки зрения. Сообщение компилятора OP совершенно ясно, но мой компилятор также жалуется на приведение типа массива, как вы говорите. Независимо от проблемы с приведением типа массива, существует проблема неполного типа, на которой я решил сосредоточиться.

nielsen 03.07.2024 09:24

C просто не позволяет приводить типы массивов. Вероятно, по той же причине, что и не позволяет присваивать типы массивов. Просто язык указан.

C17 6.5.4 Операторы приведения:

Ограничения
Если имя типа не указывает недействительный тип, имя типа должно указывать атомарный, квалифицированный или неквалифицированный скалярный тип, а операнд должен иметь скалярный тип.

Скаляр означает «одну переменную», в отличие от «агрегата», который представляет собой массивы или структуры. Поэтому приведение структуры к другому типу структуры также не допускается.

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

Причина, по которой (int**)a компилируется, заключается в том, что параметр массива в функции всегда неявно подстраивается под указатель на ее первый элемент. Таким образом, в случае void test(int*[]); это на 100% эквивалентно void test(int**);, потому что первый элемент — это int*, а указатель на него будет int**.

Но то, что ваш код компилируется, не означает, что он правильный. C допускает всевозможные дикие и безумные преобразования указателей, но приведение символьного указателя к указателю к целочисленному указателю по-прежнему приводит к недопустимому преобразованию. Поскольку эти типы несовместимы, и разыменование результирующего указателя приведет к неопределенным ошибкам поведения.

Конкретное предупреждение о том, что int*[] является неполным типом, здесь менее актуально, поскольку в любом случае код совершенно неверен. Массив без указания размера является неполным типом. В случае функции это не имеет значения, поскольку массив в любом случае будет скорректирован, как упоминалось ранее. Но в другом месте кода, вероятно, не имеет смысла создавать экземпляр типа неполного массива, если C вообще это позволяет. А это не так, поскольку приведение типов массивов недопустимо.

Другие вопросы по теме