Строго типизированные указатели в C (гипотетически)

Вот вопрос, который я сегодня получил на экзамене:

В C предположим, что указатели строго типизированы (т.е. указатель на int не может использоваться для указания на char). Уменьшает ли это его выразительную силу? Если нет, почему и как вы компенсируете это ограничение? Если да, то как? И какие еще конструкции вы бы добавили, чтобы "уравнять" потерю выразительной мощности C?

Дополнительные сведения:

  • Благодаря уменьшенной выразительной мощности, я думаю, это означает: вы не сможете создавать определенные программы, которые вы могли ранее.
  • Строго типизированные указатели означают, что вы не можете сделать что-то вроде: int x = 5; int *p = &x; char *temp = (char*)p;
  • Это включает (void*) преобразования

Я также включил свой ответ ниже.

Ответ 1

Значит ли это также больше void*? Если да, то да: C выразительность будет ограничена, поскольку malloc будет невозможно реализовать. Вам нужно добавить новый, типизированный, свободный механизм распределения хранилища в духе С++ new.

(Или, нет: C все равно будет Turing-complete. Но я не думаю, что это означало здесь.)

Собственно, C не является даже Тьюрингом; см. комментарии ниже.

Ответ 2

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

Если вы удалили требование о том, чтобы указатели были представлены как char [sizeof (pointer type)], тогда формально указанный язык мог бы в принципе иметь дело с бесконечным количеством данных, и это было бы завершено Тьюрингом.

Ответ 3

Ну, это очень субъективный вопрос. Пара, что с тем, что я не знаю, что такое "выразительная сила";)

Все еще неспособность отличать между типами указателей является большим ограничением в моей голове. Похоже, что при использовании сопоставления Java массив char (исходящий от чего-то вроде сетевого сокета, например), к классу невероятно раздражает. Возможность просто бросить его и повторно интерпретировать память невероятно полезна и позволяет значительно оптимизировать обработку случайных блоков памяти.

Как бы вы обошли эти ограничения? Возможно, реализована функция "cast" или, возможно, только шаблонная функция memcpy, которая может повторно интерпретировать память, будет огромным бонусом для оптимизации, а для таких людей, как я, производительность. Возможно, это даже план позволить включить некоторый класс "id" в поток байтов, чтобы вы знали, что его можно интерпретировать как определенный класс.

Недостатком этой силы является то, что она позволяет полностью интерпретировать данные. Это может вызвать очень неприятные ошибки.

Ответ 4

Этот вопрос похож на вопрос о том, какие полезные/допустимые применения приведения указателей. Вот несколько:

Без указателей-указателей у вас должно быть несколько версий memcpy, memmove, malloc, так как все они требуют преобразования указателей, которые будут реализованы и использованы. В случае malloc выделение памяти для пользовательского struct становится невозможным.

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

Что касается того, какая функция позволит восстановить выразительную силу, система типов, которая распознает полиморфизм, чтобы вам не нужно было кодировать ее с помощью небезопасных указателей-указателей, было бы отличным шагом. "Языки семейства ML" уже давно используют такую ​​систему типов, и если вы знакомы с Java generics, они следуют той же линии мышления.

Ответ 5

Я не думаю, что это уменьшает его выразительную силу - вы все еще можете написать интерпретатор машины Тьюринга, что означает, что Тьюринг завершен. См. Например, этот код для гольфа.

Если вы имеете в виду выразительную силу с точки зрения удобства пользователя, то это определенно ограничивает C, потому что механизм распределения памяти (malloc и co.) должен быть изменен.

Ответ 6

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

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

Ответ 7

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

union alltypepointers  
{  
    char* c,  
    short* s,
    int* i,  
    float* f,
    double* d,
    ...
};  

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

int variable = 0x2345;
alltypepointers p;
p.i = &variable;
char *temp = p.c;
p.c++;
int newint=*p.i;

Итак, вы все еще можете делать все, что могли бы сделать раньше = > там нет снижения выразительной мощности.

Ответ 8

(someType *)((void *)somePtr)

эта конструкция позволяет конвертировать любой указатель в (someType *).