Объявление const с аргументами массива? - PullRequest
2 голосов
/ 06 августа 2020

Итак, я читал книгу Язык C Керниган Ритч ie и на странице 39, Глава 2: Типы, операторы и выражения автор пишет:

Объявление const также может использоваться с аргументами массива, чтобы указать, что функция не изменяет этот массив:

int strlen(const char[]);

Результат определяется реализацией, если делается попытка изменить константу.

Я не понимаю, что это значит. Был бы признателен, если бы кто-нибудь мог упростить то, что он имел в виду, этим.

Ответы [ 5 ]

1 голос
/ 06 августа 2020

«Реализация определена» просто означает, что то, что должно произойти, зависит от реализации. Отличие от «неопределенного поведения» состоит в том, что когда оно «определено реализацией», его необходимо задокументировать. Подробнее об этом читайте здесь: Неопределенное, неопределенное и определяемое реализацией поведение

Но вы можете изменить вещи с помощью константного указателя, если вы приведете его к неконстантному. Это напечатает 42;

void foo(const int *x)
{
    *(int *)x = 42;
}

int main(void)
{
    int n = 69;
    foo(&n);
    printf("%d\n", &n);
}

Я написал соответствующий ответ о const, который вы можете прочитать здесь: { ссылка }

0 голосов
/ 07 августа 2020

Итог

Керниган и Ритч ie ошибаются; попытка изменения const объектов не определена, не определяется реализацией.

Эти правила применяются только к объектам, изначально определенным с помощью const.

const в параметрах функции является рекомендательным, а не принудительно. Это возможно и определено стандартом C для функции, чтобы изменить объект, на который указывает параметр const, если этот объект не был определен с помощью const.

Подробности

Процитированный отрывок неверен. Попытка изменить объект, определенный с помощью const, имеет неопределенное поведение, а не поведение, определяемое реализацией. И это относится только к объектам, определенным с помощью const, а не к объектам, переданным через const -квалифицированные указатели, если эти объекты не были изначально определены с помощью const.

C 2018 6.7.3 7 говорит:

Если предпринята попытка изменить объект, определенный с типом, квалифицированным константой, с помощью lvalue с типом, не квалифицированным константой, поведение будет неопределенным.

Та же формулировка встречается в C 1990 6.5.3.

«Не определено» означает, что стандарт C не предъявляет никаких требований к поведению (C 2018 3.4.3). Это отличается от «определяется реализацией», что означает, что реализация C должна документировать, как делается выбор среди возможностей (C 2018 3.4.1).

Обратите внимание, что это правило применяется только к объектам определяется с const. 6.7 5 говорит нам, что для идентификаторов объектов определение - это объявление, которое заставляет память зарезервировать для объекта. Если мы объявим int x; внутри функции, это приведет к тому, что память будет зарезервирована для x, так что это определение. Однако оператор int strlen(const char[]); просто объявляет функцию и ее тип параметра. Фактический параметр не объявляется, потому что для него нет имени. Если мы рассмотрим фактическое определение функции, например:

int strlen(const char s[])
{
   …
}

, тогда это определение функции включает объявление параметра s. И он определяет s; хранилище для самого параметра будет зарезервировано при выполнении функции. Однако этот s - всего лишь указатель на некоторый объект, адрес которого вызывающий передает. Так что это не определение этого объекта.

До сих пор мы знаем, что правило в 6.7.3 7 говорит нам, что изменение объекта, определенного с помощью const, имеет неопределенное поведение. Существуют ли какие-либо другие правила относительно функции, изменяющей объект, который она получила через указатель с const? Есть. Левый операнд оператора присваивания должен быть изменяемым. C 2018 6.5.16 2 говорит:

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

lvalue, квалифицированное с помощью const, не возможность изменения, согласно C 2018 6.3.2.1 1. Этот абзац является ограничением в стандарте C, что означает, что для диагностики нарушений требуется реализация C. (Итак, опять же, это поведение не определяется реализацией. Реализация C должна выдавать сообщение.) Операторы ++ и --, как до, так и после, имеют схожие ограничения.

Таким образом, функция с параметром const char s[] не может напрямую изменять *s или s[i], по крайней мере, без получения сообщения c диагностики. Однако программе разрешено удалить const в операторе преобразования, если он изначально не присутствовал. C 2018 6.3.2.3 2 говорит, что мы можем добавить const:

Для любого квалификатора q , указатель на не- q - квалифицированный тип может быть преобразован в указатель на q -квалифицированную версию типа; значения, хранящиеся в исходном и преобразованном указателях, должны сравниваться равными.

, а затем C 2018 6.3.2.3 7 говорит, что после того, как мы это сделаем, мы можем преобразовать версию const обратно к исходному типу:

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта ... при повторном преобразовании результат будет равен исходному указателю.

Это означает, что если вызывающая процедура имеет:

int x = 3;
foo(&x);
printf("%d\n", x);

, а foo это:

void foo(const int *p)
{
    * (int *) p = 4;
}

, то это разрешено и определено стандартом C. . Функция foo удаляет const и изменяет объект, на который указывает, и будет напечатано «4».

Урок здесь в том, что const в параметрах функции является рекомендательным, а не C. Он служит двум целям:

  • const для параметра функции обычно указывает людям, что функция не будет изменять указанный объект через этот параметр. (Однако есть обстоятельства, не обсуждаемые здесь, когда это указание не выполняется.)
  • Компилятор будет применять правило, согласно которому указанный объект не может быть изменен с помощью типа const. Это предотвращает случайные ошибки, когда типографская ошибка может привести к нежелательному присвоению объекту const. Однако функции разрешено явно удалить const и затем попытаться изменить объект.
0 голосов
/ 06 августа 2020

Этот материал кажется немного устаревшим.

Стандартная библиотечная функция strlen в настоящее время возвращает size_t, но в любом случае:

int strlen(const char[]);, что то же самое, что int strlen(const char*); означает, что strlen может принимать либо char*, либо const char* без необходимости преобразования.

Если вы передаете указатель на неконстантную переменную, и функция пытается его изменить (отбрасывая const как в void modify(char const *X){ *(char*)X='x'; }) поведение выглядит так: ̶i̶m̶p̶l̶e̶m̶e̶n̶t̶a̶t̶i̶o̶n̶-̶d̶e̶f̶i̶n̶e̶d̶ undefined (это undefined , реализация не определена в более новых C версиях, это означает, что вы потеряете все гарантии поведения в

Unfined). программа.

Определенная реализация означает, что код будет делать что-то конкретное c (например, abort, segfault, ничего не делать) последовательным, предсказуемым образом (с учетом платформы) и что это не повлияет целостность остальной части программы.

В старых простых компиляторах stati c -lifetime const -переменная будет помещена в постоянную память, если платформа имеет постоянную память или записываемую память в противном случае. Тогда вы либо получите ошибку segfault при попытке изменить его, либо нет. Это будет поведение, определяемое реализацией.

Новые компиляторы могут дополнительно попытаться выполнить далеко идущую оптимизацию на основе аннотаций const, поэтому трудно сказать, какие эффекты может иметь попытка такой модификации. Тот факт, что такая попытка модификации является неопределенным поведением в современном C, позволяет компиляторам выполнять такие оптимизации (т. Е. Оптимизации, которые дают трудно предсказуемые результаты, если правило нарушается).

0 голосов
/ 06 августа 2020

const char[] как тип параметра функции фактически совпадает с указателем на const char (тип const char *). Обозначение массива было придумано для удобства при передаче указателя на массивы.

Связанные сообщения:

С объявлением const char p_a[] вы объявляете p_a как указатель на const char.

Квалификатор const, связанный с char, сообщает компилятору, что объект / массив char, на который указывает вызывающий объект, не должен

Любая попытка изменить этот объект / массив с помощью не- const указателя / lvalue вызывает неопределенное поведение:

«Если попытка изменить объект определенный с типом, квалифицированным константой, посредством использования lvalue с типом, не квалифицированным константой, поведение не определено. "

Источник: C18, 6.7.3 / 7

и компилятор обычно предупреждает вас об этом.

strlen не требуется, а также s h не может изменить строку, на которую в качестве аргумента передается указатель. Чтобы не дать возможности случайно изменить строку в вызывающей программе, указатель классифицируется как указатель на const char.

Квалификатор const добавляет дополнительный уровень безопасности.

0 голосов
/ 06 августа 2020

Объявление параметров функции как const указывает на то, что функция не должна изменять значения этих параметров.

Даже несмотря на то, что C передает аргументы по значению, указанные значения могут быть изменены. Объявив параметр функции как const, если функция попытается изменить указанное значение, компилятор выдаст ошибку.

Следующая функция изменит значение, на которое указывает x:

void foo(int *x) 
{
  *x = 100;
}

В следующей функции, помечая параметр как const, функция не сможет изменить значение, на которое указывает x.

void foo(const int *x) 
{
  *x = 100; // Compiler generates an error
}

In C, даже хотя при использовании квадратных скобок [] похоже, что вы передаете массив, на самом деле вы передаете указатель. Таким образом, void foo(const int *x) будет таким же, как void foo(const int x[])

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