В стандарте C (5.1.2.2.1 запуска Программы) говорится:
Функция зашла в запуск программы, назван основным. [...]
Это должен быть defined с типом возврата интервала и без параметров:int main(void) { /* ... */ }
или с двумя параметрами [...]:
int main(int argc, char *argv[]) { /* ... */ }
И позже говорит:
Значение argc должно быть неотрицательным.
argc
будьте определены как unsigned int
, argc
предположительно, означающее 'количество аргумента'?argc
используйтесь в качестве индекса для argv
?Таким образом, я начал задаваться вопросом, говорится ли в стандарте C что-то о типе индекса массива. Это подписывается?
6.5.2.1 Индексирование массива:
Одно из выражений должно иметь тип ‘‘указателем на тип объекта’’, другое выражение должно иметь целый тип, и результат имеет тип ''тип''.
Это ничего не говорит о своем со знаком (или я не нашел его). Довольно распространено видеть, что коды используют индексы массива отрицательных сторон (array[-1]
) но разве это не неопределенное поведение?
Причина использования int в main () историческая - так было всегда, задолго до стандартизации языка. Требование к индексу массива состоит в том, чтобы он находился в границах массива (или, в некоторых случаях, один за концом) - все остальное не определено, поэтому подпись не имеет значения.
1) Argc - это количество аргументов, но, честно говоря, как вы можете добавить аргумент перед именем программы, которое argv [0]
. Представьте себе программу с именем foo
, вы не можете просто сказать args1 foo args2
, поскольку это бессмысленно, несмотря на то, что argc
является типом int
со знаком. ], т.е. нет такой вещи, как argv [-1]
, которая даст вам 'args1' ...
2) Причина, по которой argc на самом деле не является индексом вектора аргументов (отсюда ' argv '), поскольку среда выполнения вставляет имя исполняемой программы в нулевое смещение, то есть argv [0]
, следовательно, argc
будет отключен на 1.
3) Индексы массивов, с точки зрения манипулирования указателями, при условии , что вы находитесь в границах блока памяти, в котором находится указатель, использование индексов массива как отрицательных допустимо, поскольку индексы массива являются ярлык для указателей, и не только это, они коммутативны, например
char v[100]; char *p = &v[0]; You can do this: p[55] = 'a'; Which is the same as *(p + 55) = 'a'; You can even do this: p = &v[55]; p[-10] = 'b' /* This will stuff 'b' into 45'th offset! */ Which is the same as *(p - 10) = 'b';
Также, если вы используете и управляете массивами таким образом, чтобы они выходили за границы - это неопределенное поведение и будет зависеть от реализации времени выполнения, как его обрабатывать, возможно, ошибка сегментации или программа сбой ....
4) В средах * nix у некоторых будет третий параметр, предоставленный в main char ** endvp
, опять же, это редко используется в мире Microsoft DOS / Windows. В некоторых реализациях среды выполнения * nix по доисторическим причинам можно было передавать переменные среды через среду выполнения.
В общем случае в Си "принцип наименьшего удивления" подразумевает, что предпочтительнее делать переменную знаковой, если нет веских причин для того, чтобы она была беззнаковой. Это связано с тем, что правила приведения типов могут привести к неожиданным результатам, когда вы смешиваете знаковые и беззнаковые значения: например, если argc
была беззнаковой, то это простое сравнение привело бы к неожиданным результатам:
if (argc > -1)
(Значение -1
повышается до unsigned int
, поэтому его значение преобразуется в UINT_MAX
, которое почти наверняка больше, чем argc
).
1) О типе main () argc: IMHO стандарт продолжает очень старую традицию (более 30 лет!), И теперь ... просто слишком поздно что-то менять (ПРИМЕЧАНИЕ: в большинстве систем ни компилятор, ни компилятор, ни компоновщик, ни ЦП не будут жаловаться, если "argc" определен как "беззнаковый", но вы не соответствуете стандарту!)
2) В большинстве реализаций argv [argc] является допустимым и принимает значение NULL. В самом деле, альтернативный способ найти конец списка аргументов - перебрать argv с 0, заканчиваясь, когда argv [i] равен NULL.
3) Арифметические операции с массивами / указателями с отрицательными числами допустимы, если диапазон адресов от (p-n) до p принадлежит одному и тому же объекту памяти. I.E. вы можете иметь
char array[100];
char *p;
p = &array[50];
p += -30; /* Now p points to array[20]. */
Такое использование арифметики указателей допустимо, потому что полученный указатель все еще остается внутри исходного объекта памяти («массива»). В большинстве систем арифметика с указателями может использоваться для навигации по памяти в нарушение этого правила, но это НЕ переносимо, поскольку полностью зависит от системы.