Разница между статическими константами char * и const char *
Может ли кто-нибудь объяснить, в чем разница между двумя фрагментами кода ниже? Они определенно скомпилируются для разных ассемблерных кодов, но я пытаюсь понять, как код может действовать по-другому. Я понимаю, что строковые литералы выбрасываются в постоянное запоминающее устройство и фактически статичны, но как это отличается от явного статичного ниже?
struct Obj1
{
void Foo()
{
const char* str( "hello" );
}
};
и
struct Obj2
{
void Foo()
{
static const char* str( "hello" );
}
};
Ответы
Ответ 1
С вашей статической версией будет только одна переменная, которая будет храниться где-то, и всякий раз, когда будет выполняться функция, будет использоваться одна и та же переменная. Даже для рекурсивных вызовов.
Нестатическая версия будет храниться в стеке для каждого вызова функции и уничтожается после каждого.
Теперь ваш пример немного сложный в отношении того, что на самом деле делает компилятор, поэтому сначала рассмотрим более простой случай:
void foo() {
static long i = 4;
--i;
printf("%l\n", i);
}
А потом главное что-то вроде этого:
int main() {
foo();
foo();
return 0;
}
напечатает
3
2
тогда как
void foo() {
long i = 4;
--i;
printf("%l\n", i);
}
он напечатает
3
3
Теперь с вашим примером у вас есть константа, поэтому значение не может быть изменено, поэтому компилятор может сыграть некоторые трюки, в то время как он часто не влияет на генерируемый код, но помогает компилятору обнаружить ошибки. И тогда у вас есть указатель, и помните, что статический эффект влияет на самого указателя, а не на значение, на которое он указывает. Таким образом, строка "привет" из вашего примера, скорее всего, будет помещена в сегмент .data вашего двоичного файла и только один раз и будет жить до тех пор, пока программа будет жить независимо от статической вещи.
Ответ 2
Локальная статическая переменная инициализируется в первый раз, ее определение встречается, но не разрушено, когда функция завершается. Таким образом, сохраняет свое значение между вызовами функции.
В случае const
это не все, что полезно - по крайней мере, до тех пор, пока построение постоянного значения является таким же пренебрежимо высоким, как присвоение адреса. (Если объект const
не является постоянным выражением, или выражение требует значительных ресурсов для создания - как в const Foo bar = foobar();
, где foobar()
может занимать значительное время - разница может стать важной.)
В тех случаях, когда это действительно важно, вы должны вернуть объект за ссылку или указатель: вы не можете вернуть ссылку или указатель на локальный объект, если это не локальный статический объект. (Спасибо Matthieu за указание на это.) Однако, когда вы хотите использовать это, вам нужно иметь в виду, что локальная статика по своей сути небезопасна.
Ответ 3
Я выяснил, что некоторые компиляторы рассматривают два по-разному.
Версия с const char *
будет копировать данные из места только для чтения в переменную в стеке.
Версия с static const char *
ссылается на данные в доступном для чтения месте (копирование не выполняется).
Я обнаружил эту разницу, пройдя через код сборки функции, используя отладчик. Я предлагаю вам либо распечатать код сборки, либо перейти на уровне языка ассемблера с помощью отладчика, чтобы найти точную истину.
Ответ 4
Несмотря на техническую разницу, с точки зрения использования и эффекта, ваши два примера идентичны.
Более подробно использование ключевого слова static
применяется к указателю на строковый литерал, а не только на строковый литерал. Указатель в примере 1 будет помещен в стек, указатель в примере два будет помещен со статическими переменными.
Я был бы удивлен, если бы они оба не оптимизировались к одной и той же вещи.