Одной из самых первых архитектур, на которые ориентировался С, были архитектуры с 36-битными или 18-битными словами (тип int
).Только слова были непосредственно адресуемы по адресам, таким как 0, 1, 2, используя родные указатели.Однако одно слово для одного символа потратило бы слишком много памяти, поэтому был добавлен 9-битный тип char
, с 2 или 4 символами в одном слове.Поскольку они не были бы адресуемы указателем слова, char *
был сделан из двух слов: одно указывало на слово, а другое сообщало, каким из байтов в слове следует манипулировать.
Конечно, сейчаспроблема в том, что char *
имеет ширину в два слова, тогда как int *
- это всего лишь одно, и это имеет значение при вызове функции без прототипа или с многоточием - тогда как (void*)0
будет иметь представление, совместимое с (char *)0
, это не будетбыть несовместимым с (int *)0
, поэтому требуется явное приведение.
Есть еще одна проблема с NULL
.Хотя GCC, похоже, гарантирует, что NULL
будет иметь тип void *
, стандарт C не гарантирует этого, поэтому даже использование NULL
в вызове функции, подобном execl
, который ожидает char *
s в качестве аргументов переменной, является неправильнымбез приведения, потому что реализация может определить
#define NULL 0
(sem_t*)-1
не является указателем NULL, это целое число -1
, преобразованное в указатель с определенным реализацией Результаты.В системах POSIX это (по необходимости) приведет к адресу, который никогда не может быть местоположением любого sem_t
.
На самом деле это очень плохое соглашение - использовать -1
, так как результирующий адрес, скорее всего, наиболее вероятен.не имеет правильного выравнивания для sem_t
, поэтому вся конструкция имеет неопределенное поведение сама по себе.