Назначение указателя на больший массив указателю на меньший VLA

Я заметил, что компиляторы C (gcc, clang, tinycc) позволяют мне без предупреждения назначать указатель на больший массив указателю на меньший VLA:

#include <stdio.h>
#if !__TINYC__
void take_vla(int N, char const X[1][N]) { printf("%zd\n", sizeof(*X)); }
#endif
int main()
{
    static char bigarray[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    //VLA
    int n = 3;
    char const (*subarray2)[n]=&bigarray;
    //char const (*subarray3)[(int){3}]=&bigarray; //VLA but clang doesn't see it as such (a bug, I guess)
#if !__TINYC__
    take_vla(3,&bigarray);
    take_vla(3,&"abcdefg");
#endif

    #if 0
        char const (*subarray1)[3]=&bigarray; //-Wincompatible-pointer-types
    #endif
}

Это соответствует C и почему?

В C указатели - это просто числа для ЦП, нет дополнительной информации о том, на что они указывают, и язык также не определяет никаких метаданных, поэтому не имеет значения размер того, на что он указывает. , просто тип тот же (и дело в компиляторе, а не в процессоре).

Skizz 12.12.2018 14:09

В вашем объявлении subarray2n используется только для вычислений адресов и не приводит к выделению памяти, например в subarray2++ будет добавлен размер символов n. Можно считать, что компилятор, когда это возможно, может выдать предупреждение, когда назначенный указатель указывает на объект, размер которого не совпадает с размером n, однако это будет проблемой времени выполнения, а не проблемой времени компиляции.

Paul Ogilvie 12.12.2018 14:13

@Skizz Да, но с системой типов C никогда не бывает так просто, не так ли. Попробуйте изменить последний #if 0 на #if 1, и вы получите предупреждение об этом назначении (более крупный простой старый массив на меньший простой старый массив). Не имеет значения, если «указатели - это просто числа», но для компилятора это каким-то образом имеет значение.

PSkocik 12.12.2018 14:16

может быть, потому что мог - это предупреждения или массивы проверки границ, что вы обманываете с этим?

Jean-François Fabre 12.12.2018 14:18

@PaulOgilvie Это обоснованное соображение, что динамический int n может быть неизвестен статически (например, он может исходить из аргумента функции), и тогда компилятор не может проверить, но мне интересно, могут ли когда-либо быть какие-либо проблемы даже с не -vla версия. char (*p)[n] = &(char [N/*N>n*/]){0}; /*... use p*/ не выглядит совместимым в соответствии со строгими правилами псевдонима, но подмассивы являются подобъектами более крупных массивов, и вы никогда не сможете получить к ним доступ целиком (только к отдельным элементам), поэтому, возможно, char (*p)[n] = (void*)&(char [N/*N>n*/]){0}; /*... use p*/ никогда не должен приводить к каким-либо проблемам.

PSkocik 12.12.2018 14:26

@PSkocik: если я не ошибаюсь (и это всегда возможно!), Этот код в последнем #if включает в себя назначение между двумя разными типами: массив указателя на char и указатель на указатель на char, поэтому компилятор может легко обнаружить это и выдать предупреждения.

Skizz 12.12.2018 14:49

@Skizz: В C указатели представляют собой нет просто числа для ЦП, потому что они имеют информацию о типе (которая влияет на арифметику указателей в C), и потому что они абстракции.

Eric Postpischil 12.12.2018 15:04

Код не строго соответствует C, потому что ограничения для операторов присваивания (C 2018 6.5.16.1 1) говорят, что два указателя должны быть указателями на версии совместимых типов, и правила, которые могут сделать указатели на два типа массивов совместимыми (6.2.7 3) не предусмотрены для массивов разной заданной длины. («Указанная» в этом случае включает длину, определенную путем вычисления выражения размера.)

Eric Postpischil 12.12.2018 15:17

@EricPostpischil Я думаю, что даже для массивов, не относящихся к VL, int (*p)[3]=(char (*)[3])&(int[4]){0}; /*... use p in any way without casting*/ никогда не должен приводить к каким-либо строгим проблемам с псевдонимом, потому что, хотя указанные типы несовместимы, для нарушения строгих правил псевдонима вам придется получить доступ к объекту через L -значение несовместимо с его эффективным типом, но в C. нет такой вещи, как L-значения, типизированные для массивов. Массивы всегда будут распадаться до указателя на int, а int действительно там. Что вы думаете?

PSkocik 12.12.2018 15:17

@EricPostpischil: Я говорил о коде, который ЦП видит во время выполнения, ЦП не знает, на каком языке был создан код, поэтому это просто числа. В языке C только компилятор (и с некоторыми метаданными - отладчик) связывает тип с адресом. Эта ссылка потеряна в конечном исполняемом файле. Некоторые языки (например, Java, C#) поддерживают эту связь между типом и адресом, поэтому возможны проверки типов во время выполнения. Арифметика указателя - это то, что компилятор генерирует на основе информации о типе, а для ЦП он просто складывает два числа вместе.

Skizz 12.12.2018 16:33

@Skizz: (а) Если вы говорили о том, что говорит ЦП, то это не «в C.» (б) Это верно не для всех процессоров. Некоторые процессоры используют архитектуру сегментированных адресов, поэтому адреса - это не просто числа. Есть и другие приукрашивания и вариации схем адресации.

Eric Postpischil 12.12.2018 17:16

@Skizz проблема с типами псевдонимов заключается в том, что он позволяет компилятору принять restrict и на его основе сгенерировать другой код. Если вы выполните int f(int *X, short *Y){ *X=2; *Y=3; return *X; }, он может сгенерировать код, который возвращает 2 в качестве промежуточного, потому что, поскольку X и Y не могут быть псевдонимами, * Y = 3 не может изменить X. На первый взгляд похоже, что то же самое может произойти с назначением подмассива / литье, но я не думаю, что это может быть основано на букве стандарта, потому что стандарт определяет строгий псевдоним с точки зрения доступа к lvalue, массивы lvalue не существуют из-за распада массива-2-указателя

PSkocik 12.12.2018 17:23

@EricPostpischil: Ах, я вижу, где произошла путаница, извините. Да уж, люди, которые читают слишком много стандартов, так разборчивы в грамматике! (Шутить)

Skizz 13.12.2018 09:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
13
136
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

const char[3] несовместим с char[37].

Также «указатель на квалифицированный тип» несовместим с «указателем на тип» - не путайте это с «квалифицированным указателем на тип». (К сожалению, корректность констант не работает с указателями на массивы.)

Соответствующей частью является правило простого присвоения C17 6.5.16.1:

  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

Смотрим на разные компиляторы:

  • gcc в "режиме GNU" бесполезен для проверки соответствия C. Вы должны компилировать с -std=cxx -pedantic-errors. После чего gcc работает нормально: gcc -std=c17 -pedantic-errors:

    error: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]

  • icc дает ту же диагностику, что и gcc, работает нормально.

  • В то время как clang -std=c17 -pedantic-errors не сообщает об ошибках, он явно не соответствует стандарту C.

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