Индекс массива и аргументированность
Вопрос
Тот Самый Стандарт C (5.1.2.2.1 Запуск программы) говорит:
Функция, вызываемая при запуске программы , называется main. [...]
Он должен быть определен с возвращаемым типом int и без параметров:
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) О типе main() argc:ИМХО стандарт продолжает очень старую традицию (более 30 лет!), и сейчас...просто уже слишком поздно что-то менять (ПРИМЕЧАНИЕ:в большинстве систем ни компилятор, ни компоновщик, ни центральный процессор не будут жаловаться, если "argc" определено как "без знака", но вы выходите за рамки стандарта!)
2) В большинстве реализаций argv [argc] является законным и принимает значение NULL.Действительно, альтернативный способ найти конец списка аргументов - выполнить итерацию по argv от 0, завершающуюся, когда argv[i] равно NULL.
3) Арифметика массива / указателя с отрицательными числами допустима, поскольку диапазон адресов от (p-n) до p принадлежит одному и тому же объекту памяти.Т.Е.вы можете иметь
char array[100];
char *p;
p = &array[50];
p += -30; /* Now p points to array[20]. */
Такое использование арифметики указателей является законным, поскольку результирующий указатель по-прежнему остается внутри исходного объекта памяти ("массива").В большинстве систем арифметика указателей может использоваться для навигации в памяти в нарушение этого правила, но она НЕ переносима, поскольку полностью зависит от системы.
В общем случае в C "принцип наименьшего удивления" подразумевает, что предпочтительнее создавать переменную со знаком, если только нет веской причины для того, чтобы она была без знака.Это связано с тем, что правила повышения типа могут привести к неожиданным результатам при смешивании значений со знаком и без знака:например, если argc
если бы он не был подписан, то это простое сравнение привело бы к удивительным результатам:
if (argc > -1)
(Тот самый -1
повышается до unsigned int
, таким образом , его значение преобразуется в UINT_MAX
, что почти наверняка больше , чем argc
).
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, по доисторическим причинам, вы могли передавать переменные среды через время выполнения.