Почему указатель не может быть назначен массиву в C? - PullRequest
0 голосов
/ 12 марта 2012

У меня есть следующий код:

int *pa;

int a[3] = {1, 2, 3};

Почему pa = a в порядке, но a = pa не разрешено?

Ответы [ 3 ]

6 голосов
/ 12 марта 2012

Основное отличие состоит в том, что тип a все еще является массивом, но он просто распадается в указатель, когда вы делаете pa=a;.pa теперь будет указывать на первый элемент массива, а не на весь массив.Когда вы делаете a=pa, это не имеет никакого смысла, поскольку вы пытаетесь указать тип данных, который содержит 3 целых числа, для типа, который может указывать только на одно целое число.

0 голосов
/ 12 марта 2012

Ну, простой ответ в том, что определение языка просто не позволяет этого - это выбор дизайна.

Глава и стих :

6.5.16 Операторы присваивания
...
Ограничения

2 Оператор присваивания должен иметь изменяемое lvalue в качестве своего левого операнда.

А что такое изменяемое значение l?

6.3.2.1 Значения, массивы и обозначения функций

1 Значение lvalue равновыражение с типом объекта или неполным типом, отличным от void; 53) , если lvalue не определяет объект при его оценке, поведение не определено.Когда говорят, что объект имеет определенный тип, тип определяется значением l, используемым для обозначения объекта. A модифицируемое lvalue - это lvalue, которое не имеет типа массива , не имеет неполного типа, не имеет типа с константой, и если это структура или объединение,не имеет какого-либо члена (включая, рекурсивно, любого члена или элемента всех содержащихся агрегатов или объединений) с квалифицированным константным типом.
...
53) Имя '' lvalue '' происходит отВыражение присваивания E1 = E2, в котором левый операнд E1 должен быть (модифицируемым) l-значением.Возможно, это лучше рассматривать как представление объекта «значение локатора».То, что иногда называют «rvalue», в этом международном стандарте описывается как «значение выражения».

Добавлено выделение.

Выражения массивов в C обрабатываются иначе, чем большинство других выражений.Причина этого объясняется в статье Деннис Ритчи писал о развитии языка Си:

NB существовал настолько кратко, что полное его описание не было написано.Он предоставил типы int и char, их массивы и указатели на них, объявленные в стиле, типизированном

    int i, j;
    char c, d;
    int iarray[10];
    int ipointer[];
    char carray[10];
    char cpointer[];

Семантика массивов осталась точно такой же, как в B и BCPL: объявления iarray и carray создают ячейки, динамически инициализированные со значением, указывающим на первую последовательность из 10 целых чисел и символов соответственно.Объявления для ipointer и cpointer опускают размер, чтобы утверждать, что никакое хранилище не должно выделяться автоматически.Внутри процедур интерпретация указателей в языке была идентична интерпретации переменных массива: объявление указателя создавало ячейку, отличающуюся от объявления массива только тем, что программист должен был назначить референт вместо того, чтобы компилятор выделял пространство иинициализировать ячейку.

Значения, хранящиеся в ячейках, привязанных к массиву и именам указателей, были машинными адресами, измеренными в байтах, соответствующей области хранения.Следовательно, косвенное обращение к указателю подразумевает отсутствие затрат времени выполнения для масштабирования указателя от смещения до байта.С другой стороны, машинный код для подписки массива и арифметики указателей теперь зависел от типа массива или указателя: для вычисления iarray[i] или ipointer+i подразумевалось масштабирование сложения i по размеру указанного объекта.

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

    struct {
        int    inumber;
        char   name[14];
    };
Я хотел, чтобы структура не merely для характеристики абстрактного объекта, а также для описания набора битов, которые могут быть прочитаны из каталога.Где компилятор может скрыть указатель на name, которого требует семантика?Даже если бы структуры рассматривались более абстрактно, а пространство для указателей могло бы быть каким-то образом скрыто, как я мог бы решить техническую проблему правильной инициализации этих указателей при выделении сложного объекта, возможно, такого, который указывал структуры, содержащие массивы, содержащие структуры на произвольную глубину?

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

Это изобретение позволило использовать большинствосуществующий код B, чтобы продолжать работать, несмотря на основной сдвиг в семантике языка.Несколько программ, которые присвоили новые значения имени массива для настройки его происхождения - возможно, в B и BCPL, бессмысленно в C - были легко восстановлены.Что еще более важно, новый язык сохранил последовательное и работоспособное (если необычное) объяснение семантики массивов, одновременно открывая путь к более полной структуре типов.

Это хорошая статья, которую стоит прочитать, если вас интересует "почему" языка C.

0 голосов
/ 12 марта 2012

Примечание: это чисто концептуально, это не настоящая причина, по которой это происходит.

Мне нравится думать о назначении указателя, как ООП и Наследование.

Представьте себе int * - это общий объект. Теперь представьте себе int [] как объект, который наследуется от int *.

Как видите, вы можете понижаться с int [] до int *, но не наверх.

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