Static, определить и const в C - PullRequest
18 голосов
/ 10 апреля 2010

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

#include <stdio.h>

static double m = 30000;

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

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

const double m = 30000;

или

#define m 30000  //m or M  

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

Ответы [ 9 ]

14 голосов
/ 10 апреля 2010
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;
}

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

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

const double m = 30000;

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

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

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

Doing

#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 во всех ваших программах, потому что компилятор может не увидеть, что вам действительно нужны только строки "Jom" и "John" в программе.

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

7 голосов
/ 10 апреля 2010

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

5 голосов
/ 10 апреля 2010

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

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

5 голосов
/ 10 апреля 2010

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

, например

static const double m = 5;

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

3 голосов
/ 10 апреля 2010

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

static const double m = 30000; 

или

#define m 30000

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

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

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

2 голосов
/ 10 апреля 2010

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

2 голосов
/ 10 апреля 2010

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

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

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

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

/* 
  # 1
  # 2
*/

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

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

1 голос
/ 10 апреля 2010

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

для (int m = 0; m

Вас ожидает неприятный сюрприз ...

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

1 голос
/ 10 апреля 2010

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

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

...