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

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

const int foo(); 

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

Я чувствую, что это похоже на то, что явная инициализация глобальных переменных равна нулю для ясности, хотя стандарт 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 * потому что это объявление указателя на постоянные символы, а не постоянный указатель.

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

 int x = foo(); 

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

 int x = 42; 

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

Значения не могут быть const ( или restrict или volatile ). Только объекты могут иметь classификаторы типов.


Контраст с

 const char *foo(); 

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

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

Ключевое слово 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;  // Трудно читать - нужно знать приоритет
     int a = b + ((c * d) / e) + f;  // Простота чтения ясных вычислений
    

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

      virtual int MyFunc () 

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

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

Interesting Posts

Компиляция workbench mysql на Fedora 21

Как удалить виртуальный компакт-диск MyBook «WD SmartWare» с моего рабочего стола?

Настройка моей мостовой сети на виртуальной машине CentOS

Держите iphone активным во время запуска программы

Заполнение отсутствующих значений по группам в data.table

Множественное наследование в java

найти недостаток вариант -printf, теперь что?

VirtualBox 4.3.12, Windows 7 в качестве гостевой и хост-операционной системы: хост -> гостевой буфер не работает

Изменение отображения по умолчанию для строки «не анализировано» в Elasticsearch

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

Как я могу обмениваться сеансом между несколькими поддоменами в ASP.NET?

Как совместить библиотеку с моей банкой?

Как конвертировать ppi в dpi для изображений Android?

Как добавить параметры в android http POST?

Преобразование ‘ArrayList в’ String ‘в Java

Давайте будем гением компьютера.