Почему я не могу преобразовать «char**» в «const char* const*» в C?

StackOverflow https://stackoverflow.com/questions/78125

Вопрос

Следующий фрагмент кода (правильно) выдает предупреждение на C и ошибку на C++ (с использованием gcc и g++ соответственно, протестировано с версиями 3.4.5 и 4.2.1;MSVC, похоже, не волнует):

char **a;
const char** b = a;

Я могу это понять и принять.
Решение этой проблемы в C++ состоит в том, чтобы изменить b на const char * const *, что запрещает переназначение указателей и не позволяет обойти const-корректность (Часто задаваемые вопросы по C++).

char **a;
const char* const* b = a;

Однако в чистом C исправленная версия (с использованием const char *const *) все равно выдает предупреждение, и я не понимаю почему.Есть ли способ обойти это без использования приведения?

Чтобы уточнить:
1) Почему это генерирует предупреждение на C?Он должен быть полностью константно-безопасным, и компилятор C++, похоже, распознает его как таковой.
2) Как правильно принять этот char** в качестве параметра, заявив (и заставив компилятор обеспечить это), что я не буду изменять символы, на которые он указывает?Например, если бы я хотел написать функцию:

void f(const char* const* in) {
  // Only reads the data from in, does not write to it
}

И я хотел вызвать его для char**, какой тип параметра будет правильным?

Редактировать:Спасибо тем, кто ответил, особенно тем, кто ответил на вопрос и/или проследил за моими ответами.

Я принял ответ, что то, что я хочу сделать, невозможно сделать без приведения, независимо от того, возможно ли это или нет.

Это было полезно?

Решение

У меня была такая же проблема несколько лет назад, и она меня бесконечно раздражала.

Правила в C сформулированы проще (т.е.они не перечисляют исключения, такие как преобразование char** к const char*const*).Следовательно, это просто запрещено.В стандарт C++ включено больше правил, разрешающих подобные случаи.

В конце концов, это просто проблема стандарта C.Я надеюсь, что следующий стандарт (или технический отчет) решит эту проблему.

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

Чтобы считаться совместимым, указатель источника должен быть константным на непосредственно переднем уровне косвенности.Итак, это даст вам предупреждение в GCC:

char **a;
const char* const* b = a;

Но это не будет:

const char **a;
const char* const* b = a;

Альтернативно, вы можете использовать его:

char **a;
const char* const* b = (const char **)a;

Вам понадобится тот же приведение для вызова функции f(), как вы упомянули.Насколько мне известно, в этом случае невозможно выполнить неявное преобразование (кроме C++).

> Однако в чистом C это все равно выдает предупреждение, и я не понимаю, почему

Вы уже определили проблему — этот код не является константно-корректным.«Правильная константа» означает, что, за исключением приведения const_cast и C-стиля, удаляющего const, вы никогда не сможете изменить константный объект с помощью этих константных указателей или ссылок.

Значение const-корректности — const в значительной степени предназначен для обнаружения ошибок программиста.Если вы объявляете что-то как константное, вы заявляете, что не считаете, что это следует изменять - или, по крайней мере, те, у кого есть доступ только к константной версии, не должны иметь возможности изменять это.Учитывать:

void foo(const int*);

Как заявлено, у foo нет разрешение чтобы изменить целое число, на которое указывает его аргумент.

Если вы не уверены, почему опубликованный вами код не является константно-корректным, рассмотрите следующий код, который лишь немного отличается от кода HappyDude:

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

Неконстантные типы могут преобразовываться в константные типы только определенными способами, чтобы предотвратить любой обход 'const' в типе данных без явного приведения.

Объекты, изначально объявленные как const, являются особенными: компилятор может предположить, что они никогда не изменяются.Однако если «b» можно присвоить значение «a» без приведения, вы можете случайно попытаться изменить константную переменную.Это не только нарушит проверку, которую вы просили компилятор выполнить, и запретите вам изменять значение этих переменных, но также позволит вам нарушить оптимизацию компилятора!

В некоторых компиляторах будет напечатано «42», в некоторых — «43», а в других программа выйдет из строя.

Редактировать-добавить:

СчастливыйЧувак:Ваш комментарий в точку.Либо язык C, либо используемый вами компилятор C обрабатывают const char * const * принципиально иначе, чем язык C++.Возможно, стоит рассмотреть возможность отключения предупреждения компилятора только для этой строки исходного кода.

Редактировать-удалить: удалена опечатка

Это раздражает, но если вы хотите добавить еще один уровень перенаправления, вы часто можете сделать следующее, чтобы перейти к указатель-указатель:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

У него немного другое значение, но обычно он работоспособен и не использует приведение типов.

Я не могу получить ошибку при неявном приведении char** к const char * const *, по крайней мере, в MSVC 14 (VS2k5) и g++ 3.3.3.GCC 3.3.3 выдает предупреждение, в правильности которого я не совсем уверен.

тест.с:

#include <stdlib.h> 
#include <stdio.h>
void foo(const char * const * bar)
{
    printf("bar %s null\n", bar ? "is not" : "is");
}

int main(int argc, char **argv) 
{
    char **x = NULL; 
    const char* const*y = x;
    foo(x);
    foo(y);
    return 0; 
}

Вывод при компиляции в виде кода C:cl/TC/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Вывод при компиляции в виде кода C++:cl/TP/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter

Вывод с помощью gcc:gcc -Wall test.c

test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type

Вывод с помощью g++:g++ -Wall test.C

нет вывода

Я почти уверен, что ключевое слово const не означает, что данные не могут быть изменены/постоянны, а только то, что данные будут обрабатываться как доступные только для чтения.Учти это:

const volatile int *const serial_port = SERIAL_PORT;

который является действительным кодом.Как могут сосуществовать Летучие и Константные?Простой.Летучий сообщает компилятору всегда читать память при использовании данных, а const сообщает компилятору создавать ошибку при попытке записи в память с использованием указателя Serial_port.

Помогает ли const оптимизатору компилятора?Нет.Нисколько.Поскольку константность может быть добавлена ​​к данным и удалена из них посредством приведения, компилятор не может выяснить, действительно ли константные данные являются постоянными (поскольку приведение может быть выполнено в другой единице трансляции).В C++ также есть ключевое слово mutable, которое еще больше усложняет ситуацию.

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

Что происходит при попытке записи в память, которая на самом деле доступна только для чтения (например, ПЗУ), вероятно, вообще не определено в стандарте.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top