Каковы последствия использования static const вместо #define?

gcc жалуется на это:

#include <stdio.h>
static const int YY = 1024;
extern int main(int argc, char*argv[])
{  
  static char x[YY];
}

$gcc -c test1.c test1.c: Функция main': test1.c:5: error: storage size of x 'не является постоянной test1.c: 5: ошибка: размер переменной `x 'слишком велик

Удалите "статический" из определения x, и все будет хорошо.

Я не совсем понимаю, что происходит здесь: конечно, YY постоянна?

Я всегда считал, что подход "static const" предпочтительнее "#define". Есть ли способ использования "static const" в этой ситуации?

Ответ 1

В C переменная const не является "реальной" константой времени компиляции... это действительно просто нормальная переменная, которую вам не разрешено изменять. Из-за этого вы не можете использовать переменную const int для указания размера массива.

Теперь gcc имеет расширение, которое позволяет указать размер массива во время выполнения, если массив создан в стеке. Вот почему, когда вы оставляете static из определения x, код компилируется. Однако это все равно не будет законным в стандарте C.

Решение: используйте #define.

Изменить: обратите внимание, что это точка, в которой отличаются C и С++. В С++ a const int является реальной константой времени компиляции и может использоваться для указания размера массивов и т.п.

Ответ 2

Вы можете использовать "enum" или "define" для объявления размера:

#define          XX   1024 
static int const YY = 1024;
           enum{ ZZ = 1024 };

extern int main(void){

  static char x[XX]; // no error
  *(int*)&XX = 123;  // error: lvalue required as unary ‘&’ operand


  static char y[YY]; // error: storage size of ‘y’ isn’t constant
  *(int*)&YY = 123;  // no error, the value of a const may change


  static char z[ZZ]; // no error
  *(int*)&ZZ = 123;  // error: lvalue required as unary ‘&’ operand
}

Ответ 3

Потому что вы объявили x как "статический", что делает его глобальной. Его просто известно только функции main(), в которой она объявлена. Объявляя YY вне любой функции, вы сделали ее глобальной. "static" также делает его глобальным, но известен только этому файлу.

Если вы объявили YY как "const int YY = 1024", компилятор может рассматривать его как #define, но с типом. Это зависит от компилятора.

В этот момент 2 вещи могут быть неправильными.

1:

Все глобальные переменные инициализируются во время выполнения, до вызова main().

Поскольку оба x и YY являются глобальными, они оба инициализируются тогда.

Таким образом, инициализация времени выполнения глобального x должна распределять пространство по значению в YY. Если компилятор не обрабатывает YY, как #define с типом, он должен принять решение о времени выполнения во время компиляции. Это может предполагать наибольшее возможное значение для int, которое действительно было бы слишком большим. (Или, возможно, отрицательный, поскольку вы оставили его под подпиской.)

Может быть интересно посмотреть, что произойдет, если вы только измените YY на короткий, предпочтительно беззнаковый короткий. Тогда его макс будет 64K.

2:

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

Поскольку вы объявили x как размер YY, вы установили его для переноса символов YY из глобального пространства. Каждый char в нем по существу был бы глобальным. Если глобальное пространство в вашей системе ограничено, то 1024 символов может быть слишком большим.

Если вы объявили x как указатель на char, тогда он будет принимать байты sizeof (char *). (4 байта - это размер указателя на большинстве систем.) При этом вам нужно будет установить указатель на адрес правильного пространства malloc'd.

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

также:

Я не помню, есть ли какая-либо гарантия того, что глобалы будут инициализированы в любом порядке. Если нет, то x может быть инициализирован до YY. Если это произойдет, тогда YY будет просто содержать случайное содержимое ОЗУ.

Ответ 4

Чтобы выполнить ответ от Martin B, вы можете сделать это:

#include <stdio.h>

#define XSIZE 1024

static const int YY = XSIZE;

int main(int argc, char*argv[])
{  
    static char x[XSIZE];
}

Ответ 5

/* SHOULDN'T THIS WORK IN C++? */
// .h

class C {
public:
   static const int kSIZE;
   int a[kSIZE]; // error: array bound is not an integer constant
};


// .cc

const int C::kSIZE = 1;


/*  WORKS FINE */    
// .h

class C {
public:
   enum eCONST { kSIZE = 1 };
   int a[kSIZE];
};