Должны ли бесполезные квалификаторы типов в типах возврата, для ясности?

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

Вопрос

Наш инструмент статического анализа жалуется на «бесполезный квалификатор типа при возвращении типа», когда у нас есть прототипы в файлах заголовков, таких как:

const int foo();

Мы определили это таким образом, потому что функция возвращает константу, которая никогда не изменится, думая, что API казался более ясным с const на месте.

Я чувствую, что это похоже на явную инициализацию глобальных переменных до нуля для ясности, даже если стандарт C уже утверждает, что все глобалы будут инициализированы до нуля, если не явно инициализированы. В конце концов, это действительно не имеет значения. (Но инструмент статического анализа не жалуется на это.)

У меня вопрос, есть ли какая -то причина, по которой это может вызвать проблему? Должны ли мы игнорировать ошибки, сгенерированные инструментом, или мы должны успокоить инструмент по возможной стоимости менее четкого и последовательного API? (Он возвращает другие const char* Константы, с которыми у инструмента нет проблем.)

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

Решение

Обычно ваш код лучше описать как можно точнее, что происходит. Вы получаете это предупреждение, потому что const в const int foo(); в основном бессмысленно. API кажется более ясным, если вы не знаете, что const Ключевое слово означает. Не перегружайте подобное значение; static как бы ни был достаточно плохо, и нет никаких оснований добавлять потенциал для большей путаницы.

const char * означает что -то отличное от const int делает, поэтому ваш инструмент не жалуется на это. Первый является указателем на постоянную строку, что означает, что любой код, вызывающий функцию, возвращающую эту тип, не должна пытаться изменить содержимое строки (например, она может быть в ПЗУ). В последнем случае система не имеет возможности обеспечить соблюдение того, что вы не внесете изменения в возвращенные int, поэтому квалификатор бессмыслен. Более ближе параллельно с типами возврата будет:

const int foo();
char * const foo2();

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

На самом деле, я только что сделал небольшую программу тестирования, и GCC даже явно предупреждает об этой проблеме:

test.c:6: warning: type qualifiers ignored on function return type

Так что не только ваша статическая программа анализа жалуется.

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

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

#define CONST_RETURN

CONST_RETURN int foo();

У тебя нет проблем с const char * Потому что это объявляет указатель на постоянный chars, а не постоянный указатель.

Игнорируя const на данный момент, foo() возвращает значение. Ты можешь сделать

int x = foo();

и назначить значение, возвращаемое foo() к переменной x, почти так же, как вы можете сделать

int x = 42;

назначить значение 42 переменной x.
Но вы не можете изменить 42 ... или значение, возвращенное foo(). Анкет Говоря, что значение возвращается из foo() нельзя изменить, применив const ключевое слово типа foo() ничего не делает.

Ценности не может быть const (или же restrict, или же volatile) Только объекты могут иметь типовые квалификаторы.


Контрастировать с

const char *foo();

В таком случае, foo() Возвращает указатель на объект. Объект, на который указан возвращенное значение, может быть квалифицирован const.

Int возвращается копия. Анкет Это может быть копия Const, но когда он назначен чем -то другим, что -то в силу того факта, что оно было назначено, не может быть по определению констант.

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

const int foo() сильно отличается от const char* foo(). const char* foo() Возвращает массив (обычно строку), содержимое которого не разрешается меняться. Подумайте о разнице между:

 const char* a = "Hello World";

а также

const int b = 1;

a все еще является переменной и может быть назначена другим строкам, которые не могут измениться, тогда как b не является переменной. Так

const char* foo();
const char* a = "Hello World\n";
a = foo();

разрешено, но

const int bar();
const int b = 0;
b = bar();

не допускается, даже с const декларация bar().

Да. Я бы посоветовал написать код «явно», потому что он дает понять кому -либо (включая себя) при чтении кода то, что вы имели в виду. Вы пишете код для Другие программисты для чтения, чтобы не угодить прихоти компилятора и инструментов статического анализа!

(Тем не менее, вы должны быть осторожны, чтобы любой такой «ненужный код» не приводит к созданию другого кода!)

Некоторые примеры явного кодирования улучшают читаемость/обслуживание:

  • Я помещаю скобки вокруг частей арифметических выражений, чтобы явно указать, что я хочу произойти. Это дает понять любому читателю, что я имел в виду, и спасает меня о необходимости беспокоиться (или делать ошибки с) правилами приоритета:

    int a = b + c * d / e + f;      // Hard to read- need to know precedence
    int a = b + ((c * d) / e) + f;  // Easy to read- clear explicit calculations
    

  • В C ++, если вы переопределяете виртуальную функцию, то в полученном классе вы можете объявить ее без упоминания «виртуального» вообще. Любой, кто читает код, не может сказать, что это виртуальная функция, которая может вводить в заблуждение! Однако вы можете безопасно использовать виртуальное ключевое слово:

    virtual int MyFunc()
    И это дает понять всем, кто читает ваш заголовок класса, что этот метод является виртуальным. (Эта «ошибка синтаксиса C ++» зафиксирована в C#, требуя использования ключевого слова «переопределить» в этом случае - больше доказательств, если кто -то нуждается в нем, что упустить «ненужный виртуальный» - действительно плохая идея) - это действительно плохая идея)

Это оба четкие примеры, когда добавление «ненужного» кода сделает код более читабельным и менее подверженным ошибкам.

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