Статические, определяемые и константные в C

Я читал, что статические переменные используются внутри функции, когда вы не хотите, чтобы значение переменной изменялось/инициализировалось каждый раз при вызове функции. Но как насчет определения статической переменной в основной программе перед «основным», например.

#include <stdio.h>

static double m = 30000;

int main(void)
{
value = m * 2 + 3;
}

Здесь переменная m имеет постоянное значение, которое не будет изменено в дальнейшем в основной программе. В том же направлении мысли, какая разница, чтобы иметь их вместо использования статического определения:

const double m = 30000;

or

#define m 30000  //m or M  

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


person yCalleecharan    schedule 09.04.2010    source источник
comment
Спасибо всем за интересные ответы. Поэтому я думаю, что в моем случае лучше всего иметь static const double = 30000 .   -  person yCalleecharan    schedule 10.04.2010


Ответы (9)


static double m = 30000; 

double foo(double x, double y) {
    return x/m + y;
}

Это ничего тебе не даст. Для выполнения вычислений необходимо создать копию m. Также, если вы делаете:

double bar( double x, double y) {
     m += x + y;
     return  m;
}

Тогда все вызовы bar изменят m. Статические переменные вне функций (или классов) на самом деле являются глобальными переменными с файловой областью. Другие файлы не могут получить к ним доступ через extern

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

const double m = 30000;

Это лучше и во многих случаях лучше. Если компилятор видит эту глобальную константу, а затем видит ссылку на m, то он знает, что вместо того, чтобы генерировать код для загрузки значения из того места, где оно когда-либо было (что, вероятно, сначала требует загрузки буквального адреса в регистр) в регистр или позицию стека для выполнения вычислений он может просто сделать регистр равным 30000 или иногда генерировать инструкцию с закодированным 30000 прямо там.

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

Я не уверен, что это стандарт, но иногда вы можете сделать extern const double m = 30000;, и компилятор будет использовать 30000 для оптимизации и предположить, что в другом файле действительно есть копия m, которая будет храниться в исполняемом файле. Вы также можете сделать static const double m = 30000;, и компилятор может предположить, что никто другой не будет ожидать, что копия m будет сохранена в объектном коде, сгенерированном из этого исходного файла.

Делает

#define m 30000

является более рискованным. Вы не получите предупреждение или ошибку, если ранее был объявлен другой m как переменная, константа или функция. Кроме того, для макросов препроцессора, подобных этому, легко испортить. Например:

#define BASE_ADDRESS 48
#define MY_OFFSET  9
#define MY_ADDRESS  BASE_ADDRESS+MY_OFFSET
...
  return MY_ADDRESS*4;

Да, это глупый пример, но как это выглядит после того, как препроцессор закончит с ним

...
  return 48+9*4;

Который

 return 48+(9*4);

И это не то, что вы, вероятно, хотели.

Еще одно место, где макросы плохи, — это когда у вас есть большие константы, такие как строки. Строки требуют, чтобы к ним можно было обращаться по указателю, и их труднее оптимизировать, чем целые числа и литералы с плавающей запятой или постоянные числа. Вы могли бы легко создать очень большую программу, если бы у вас было много таких вещей, как:

#define JIM "Jim"
#define JOHN "John"

а затем использовали JIM и JOHN во всех ваших программах, потому что компилятор может не увидеть, что вам действительно нужны строки «Джом» и «Джон» только один раз в программе.

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

person nategoose    schedule 09.04.2010
comment
Спасибо за длинное объяснение. Итак, если у меня есть все коды в одном файле, то static const double m = 30000 - лучший ответ, который я полагаю. - person yCalleecharan; 10.04.2010
comment
Все компиляторы распознают два строковых литерала как идентичные и сохранят их только один раз. - person Tomas; 11.05.2010
comment
В некоторых случаях вам нужно использовать define вместо const. static const uint8_t ARRAY_SIZE = 16U; uint8_t array[ARRAY_SIZE] не работает, потому что ARRAY_SIZE не является явным постоянным значением. То же самое с const uint8_t SECONDS_PER_MINUTE = 60U; const uint16_t SECONDS_PER_HOUR = 60U * SECONDS_PER_MINUTE;, не работает. Это большой позор. - person Gauthier; 01.03.2011

static означает, что переменная будет иметь статическую продолжительность хранения и локальную видимость. В данном случае он используется для части «локальной видимости» — т. е. это означает, что m виден только внутри этой единицы перевода (по сути, этого файла после его предварительной обработки).

person Jerry Coffin    schedule 09.04.2010

Когда вы пишете const double m=3000;, вы говорите компилятору создать символ m в объектном файле, к которому можно получить доступ из других файлов. Компилятор может встроить значение m в файл, где оно определено, но символ по-прежнему должен выделяться для целей отдельной компиляции.

Когда вы пишете #define m 3000, вы просто используете синтаксическое удобство для записи одной и той же константы в нескольких местах исходного файла.

person Pascal Cuoq    schedule 09.04.2010

static для объекта, объявленного вне функции, просто делает объект локальным для единицы перевода (т. е. к нему нельзя получить доступ из других .c файлов). Это не делает его постоянным. Для чего это было const. Они ортогональны, поэтому вы можете иметь одно или другое или оба.

e.g.

static const double m = 5;

#define объявляет макрос, который (в данном случае) можно использовать как постоянное значение. Объекта нет, поэтому const не применяется, поскольку нет объекта, который нужно изменить. Как следствие, вы также не можете взять адрес макроса.

person CB Bailey    schedule 09.04.2010
comment
Просто добавлю: const и static не настолько ортогональны в C++, как в C. В C++ переменная const, определенная вне функции, по умолчанию также является static. - person Jerry Coffin; 10.04.2010
comment
@Jerry: Конечно, но это вопрос C и вопрос для новичков, так что, вероятно, это деталь, с которой не стоит усложнять ответ. - person CB Bailey; 10.04.2010
comment
Ортогональный - подходящее слово? Или вы имели в виду взаимность? как ты сказал, мы можем либо одно, либо другое, либо оба. - person yCalleecharan; 10.04.2010
comment
@yCalleecharan: я имел в виду ортогональный. Имеет ли объект внутреннюю или внешнюю связь (т. е. то, что делает static), это не зависит от того, является ли он const. - person CB Bailey; 10.04.2010
comment
Спасибо. Когда я думал об ортогональных, я думал об ортогональных векторах, где 1 компонент не влияет на другой ортогональный компонент :). - person yCalleecharan; 10.04.2010

Если значение m должно оставаться неизменным навсегда, то, конечно, вы можете либо использовать

static const double m = 30000; 

or

#define m 30000

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

Также обратите внимание, что в языке C const объекты являются не константами, а скорее "постоянными переменными". Если вам нужна истинная константа (т. е. объект, который формирует константные выражения), вы должны использовать либо #define, либо константу перечисления.

Последнее обычно проблема только с целыми константами. В вашем случае double подход с [static] const может работать лучше всего.

person AnT    schedule 09.04.2010

... изменять/инициализировать каждый раз при вызове функции

Вы используете слова «изменить» и «инициализировать», как если бы они были одинаковыми, но это не так.

void f(void) {
  static int a = 0;
  a++; // changed!
  printf("%d\n", a);
}

int main(void) {
  f(); f();
}

/* 
  # 1
  # 2
*/

Когда в области файла (вне функций) static не означает «константа», как в «статическом значении», но это означает, что на идентификатор можно ссылаться только в этой единице перевода.

Так что ваш первый m без const еще можно поменять. Только const защищает от изменений. Но если вы опустите static, то, если вы свяжете библиотеку или другой объектный файл, который имеет тот же нестатический идентификатор в файловой области, вы получите конфликты во время компоновки.

person Johannes Schaub - litb    schedule 09.04.2010

#define является операцией препроцессора и приведет к замене всех вхождений m на 30000 до того, как произойдет фаза компиляции. Два других примера являются добросовестными переменными. Переменная static существует в единице перевода, в которой она объявлена, и может быть изменена. Переменная const доступна только для чтения.

person fbrereto    schedule 09.04.2010

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

Разница между использованием const и #define заключается в том, что первый позволяет компилятору проверять тип использования вами константы.

person rampion    schedule 09.04.2010

Основное отличие состоит в том, что с #define вы выходите из системы типов. Препроцессор не имеет понятия о безопасности типов, области действия и т. д. Так что, например. если вы позже попытаетесь написать цикл, например

for (int m = 0; m ‹ размер; m++) { ... }

ты готовишь неприятный сюрприз...

Также, если вы используете #define, при отладке кода вы увидите только значение 30000, а не имя m. Кажется, что в данном случае это не имеет большого значения, но при использовании осмысленных имен констант и переменных имеет значение.

person Péter Török    schedule 09.04.2010
comment
Спасибо. Интересно узнать этот совет по отладке. - person yCalleecharan; 10.04.2010