Последний вопрос первый:
Это нормально:
const void *ptr = ...;
const char * const ptr3 = *(const char **) ptr1;
^^^^^^^^^^^^ ~~~~~ ^^^^^^^^^^^^
| |
+----------------------------+
Здесь я могу упростить это с помощью typedef:
typedef const char * string;
const void *ptr = ...;
const string ptr3 = *(string *) ptr;
~~~~~ ^^^^^^ ^^^^^^
| |
+----------------+
const
в локальной переменной (подчеркнутая ~~~~~
) на самом деле не требуется: она указывает, что локальная переменная ptr3
равна const
, а не что данные указывает на постоянную.
Следующий вопрос: Почему (или почему нет) не *(const char * const *) ptr1
?
Ну, тип *(const char * const *) ptr1
равен const char * const
или const string
, если вы используете typedef. Но он используется только в качестве значения. Нет разницы между константным и неконстантным значением. Например, посмотрите на следующий код:
void *ptr = ...;
int x = *(const int *) ptr;
int y = *(int *) ptr;
Очевидно, x
и y
получают одинаковое значение. Таким образом, нет смысла в добавлении const
здесь, это просто дополнительная печать.
Но: некоторые компиляторы выдают предупреждение ...
const void *ptr = ...;
string ptr2 = *(string *) ptr;
Поскольку вы приводите указатель на const void
на указатель на неконстантный string
, некоторые компиляторы могут выдавать предупреждение о том, что вы отбрасываете квалификаторы. Компиляторы C ++ даже не должны пропускать такой код, поскольку они потребуют const_cast
для преобразования из const void *
в string *
(a.k.a const char *const *
).
Однако: обратное преобразование в порядке. Можно передавать указатели на неконстантные объекты на strcmp
. Тот факт, что strcmp
использует указатели для сопоставления данных, указывает на то, что strcmp
сам не изменяет данные.
Есть много способов написать хорошую функцию. Вот несколько примеров.
return strcmp(*(char **) p1, *(char **) p2);
return strcmp(*(const char **) p1, *(const char **) p2);
return strcmp(*(char * const *) p1, *(char * const *) p2);
return strcmp(*(const char * const *) p1, *(const char * const *) p2);
// The following are not technically portable, but are portable in practice
return strcmp(*(void **) p1, *(void **) p2);
return strcmp(*(const void **) p1, *(const void **) p2);
return strcmp(*(void * const *) p1, *(void * const *) p2);
return strcmp(*(const void * const *) p1, *(const void * const *) p2);
Так как вы в любом случае приводите переменные, компилятор допускает скольжение множества возможных ошибок, но некоторые компиляторы могут выдавать предупреждения. Используйте любую версию, соответствующую вашему стилю кодирования.
Окончательный совет: Ключевое слово auto
устарело. В наши дни это ничего не значит - это спецификатор класса хранения по умолчанию. Не используйте auto
.