Я читал Head First C и в настоящее время застрял в понимании int main(int argc, char *argv[])
и optind
переменной getopt()
. Меня беспокоит та же программа, что и в Как присваивается "optind" в C?, к сожалению, я до сих пор не могу понять, что происходит, даже после прочтения этого вопроса и справочных страниц. Мой код выглядит следующим образом:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char *delivery = "";
int thick = 0;
int count = 0;
char ch;
printf("optind before loop is %i\n", optind);
while ( (ch = getopt(argc, argv, "d:t")) != EOF)
{
printf("optind before switch is %i\n", optind);
switch(ch)
{
case 'd':
delivery = optarg;
printf("argc in case d is %i\n", argc);
printf("argv in case d is %c\n", *argv);
printf("optind in case d is %i\n", optind);
break;
case 't':
thick = 1;
printf("argc in case t is %i\n", argc);
printf("argv in case t is %c\n", *argv);
printf("optind in case t is %i\n", optind);
break;
default:
fprintf(stderr, "Unknown option: '%s'\n", optarg);
return 1;
}
printf("after switch optind is %i\n", optind);
printf("----------------\n");
}
printf("after loop argc is %i\n", argc);
printf("after loop argv is %c\n", *argv);
printf("after loop optind is %i\n", optind);
printf("----------------\n");
argc -= optind;
argv += optind;
printf("final argc is %i\n", argc);
printf("final argv is %c\n", *argv);
printf("final optind is %i\n", optind);
if (thick) puts("Thick crust.");
if (delivery[0]) printf("To be delivered %s.\n", delivery);
puts("Ingredients:");
for (count = 0 ; count < argc; count++)
{
puts(argv[count]);
}
return 0;
}
Я запустил программу с помощью ./order_pizza bacon -d now -t shrimp
и получил следующий результат:
optind before loop is 1
optind before switch is 4
argc in case d is 6
argv in case d is O
optind in case d is 4
after switch optind is 4
----------------
argc in case t is 6
argv in case t is O
optind in case t is 5
after switch optind is 5
----------------
after loop argc is 6
after loop argv is O
after loop optind is 4
----------------
final argc is 2
final argv is ]
final optind is 4
Thick crust.
To be delivered now.
Ingredients:
bacon
pineapple
Код работает нормально, но я не до конца понимаю его внутренности. Вот мои проблемы:
char *argv[]
в main
— это массив указателей, в котором каждый указатель указывает на первый символ соответствующей строки аргумента, а argv
на самом деле argv[0]
верно? А argv[0] — это адрес первого символа ./order_pizza
, то есть .
. Но почему мой вывод говорит, что argv
был первым O
, а затем ]
(символы, которые даже не существуют в моем вводе)?
Согласно man-странице getopt(), «getopt()
переставляет содержимое argv
при сканировании, так что в конечном итоге все неопции оказываются в конце». Означает ли это, что после сканирования argv
становится {"./order_pizza","-d","now","-t","bacon","shrimp"}
? Будет ли порядок параметров и аргументов оставаться прежним (например, можно ли вместо этого преобразовать переставленный массив в {"./order_pizza","-t","now","-d","shrimp","bacon"}
)?
После цикла while
optind
равно 4, поэтому argv += optind
должно быть эквивалентно argv[0] = argv[0] + 4;
, что для меня выглядит как добавление 4 к адресу .
(первый символ ./order_pizza
). Учитывая, что массив теперь переставлен в {"./order_pizza","-d","now","-t","bacon","shrimp"}
, не должны ли мы вместо этого попытаться сделать argv[0] = argv[4];
или argv = arg[optind]
?
Вектор аргумента начинается с элемента ./order_pizza
. На странице руководства говорится: «Если больше нет символов параметров, getopt () возвращает -1. Тогда optind — это индекс в argv первого элемента argv, который не является параметром». Поскольку после обработки вектор представляет собой {"./order_pizza","-d","now","-t","bacon","shrimp"}
, почему optind
равно 4 (индекс bacon
) вместо 0 (индекс ./order_pizza
). Я знаю, что нам нужно первое, но разве ./order_pizza
не считается элементом argv?
Head First C утверждает, что «optind хранит количество строк, прочитанных из командной строки, чтобы обойти параметры», что, похоже, противоречит справочной странице getopt(), в которой указано, что optind
— это порядковый номер. Это недосмотр авторов книги, или я что-то упустил и авторы действительно правы?
Ответы на старый вопрос об этой программе мне очень помогли, но у меня все еще есть проблемы, перечисленные выше. Спасибо!
Эти printfs()
вызывают неопределенное поведение. %c
ожидает char
, а не char *
.
OT, но есть некоторые другие проблемы с вашим кодом. Учитывая char ch;
сравнение (ch = getopt(argc, argv, "d:t")) != EOF
неверно. Во-первых, getopt()
возвращает int
, а не char
, а EOF
не может быть представлено как значение char
. (Во всяком случае, при условии sizeof(char)<sizeof(int)
). Кроме того, getopt() возвращает -1, а не EOF
.
(продолжение) EOF не равно -1: "EOF
которое расширяется до целочисленного константного выражения с типом int
и отрицательным значением" Использование неправильных типов и значений - отличный способ ввести тонкие ошибки, которые может быть очень сложно исправить. найти причину.
char *argv[]
в main это массив указателей,
На самом деле это указатель на первый char *
в таком массиве. Причуда C заключается в том, что синтаксис массива можно использовать в списке параметров функции для указания указателя. Было бы эквивалентно — только в списке параметров функции — объявить argv
как char **argv
. И некоторые люди делают.
в котором каждый указатель указывает на первый символ соответствующего аргумента нить,
Да.
и
argv
на самом делеargv[0]
верно?
Нет. argv[0]
имеет на один уровень косвенности меньше, чем argv
. Они не могут относиться к одному и тому же объекту. Для любого действительного указателя p
p[0]
эквивалентен *p
. Это включает argv
: argv[0]
эквивалентно *argv
, а не самому argv
.
А
argv[0]
это адрес первого символа./order_pizza
, то есть.
.
Да, когда вы запускаете программу с помощью показанной команды.
Но почему мой вывод говорит, что
argv
был первымO
, затем]
(символы, которые даже не существуют в моем вводе)?
Это не говорит об этом. Ваша программа ведет себя неопределенно, когда пытается напечатать *argv
, char *
(см. выше) с помощью printf
спецификатора преобразования %c
, который предназначен для значений типа char
.
Хороший компилятор C предупредит вас об этом несоответствии. Если у вас нет предупреждения, то либо включите уровень предупреждения, либо получите лучший компилятор.
- Согласно справочной странице getopt(), «
getopt()
переставляет содержимое argv при сканировании, так что в конечном итоге все неопции оказываются в конце».
Обратите внимание, что это характеристика поведения по умолчанию реализации GNU getopt()
, которая отличается в этом отношении от поведения, указанного POSIX (то есть просто остановить обработку опции, когда найден первый аргумент, не являющийся опцией).
Означает ли это, что после сканирования
argv
становится{"./order_pizza","-d","now","-t","bacon","shrimp"}
?
В вашей примерной команде "./order_pizza"
— это имя команды, «-d
» и "now"
— это параметр и его параметр-аргумент, а "-t"
— еще один параметр (который не принимает аргумент). Остальные элементы argv
, "bacon"
и "shrimp"
являются необязательными аргументами, и именно они переставляются до конца.
Будет ли порядок параметров и аргументов оставаться прежним (например, можно ли вместо этого преобразовать переставленный массив в
{"./order_pizza","-t","now","-d","shrimp","bacon"}
)?
Документы не указывают, но я ожидаю, что относительный порядок необязательных аргументов будет сохранен. То есть я ожидаю первого порядка, который вы предлагаете, а не альтернативного порядка, который вы предлагаете. Если бы getopt()
переупорядочил аргументы, не являющиеся параметрами, то это было бы неподходящим для множества распространенных идиом команд. Например, было бы очень плохо, если бы обработка getopt()
преобразовала mv a b -f
в mv -f b a
. Однако такой аргумент — это не то же самое, что документация.
- После цикла while
optind
равно 4, поэтомуargv += optind
должно быть эквивалентноargv[0] = argv[0] + 4;
,
Точно нет. argv
— это не то же самое, что argv[0]
(когда-либо). argv += optind
эквивалентно argv = &argv[optind]
. То есть он обновляет argv
, чтобы он указывал на (указатель на) первый аргумент без опций.
- Вектор аргумента начинается с элемента
./order_pizza
. На странице руководства говорится: «Если больше нет символов параметров, getopt () возвращает -1. Тогда optind — это индекс в argv первого элемента argv, который не является параметром». Поскольку после обработки вектор представляет собой{"./order_pizza","-d","now","-t","bacon","shrimp"}
, почемуoptind
равно 4 (индексbacon
) вместо 0 (индекс./order_pizza
).
Текст немного вводит в заблуждение. Вместо «первый элемент argv, который не является параметром», было бы точнее сказать «первый аргумент, не являющийся параметром, или завершающий нулевой указатель, если нет аргументов, не являющихся параметрами». Это также согласуется с остальным описанием того, как getopt()
поддерживает значение optind
. И делать это имеет смысл, хотя использовать это в качестве основного критерия небезопасно.
Я знаю, что нам нужно первое, но разве
./order_pizza
не считается элементом argv?
Да, ./order_pizza
— это элемент argv, но это не то, что здесь имеется в виду в руководстве.
- Head First C заявляет, что «optind хранит количество строк, считанных из командной строки, чтобы обойти параметры», что кажется конфликт с справочной страницей getopt(), в которой указано, что
optind
является индексом число. Это недосмотр авторов книги, или я пропустил что-то и авторы действительно правы?
Книга и страница руководства не конфликтуют, хотя книга немного слабее применительно к версии GNU getopt()
. Предположим, что команда была дана как
./order_pizza -d now -t bacon shrimp
После того, как getopt()
обработает все параметры, что определяется возвратом -1, значение optind
будет равно 4. Это индекс первого аргумента, не являющегося параметром, "bacon"
, в argv
. Это также количество предшествующих строк в argv
. Вот что значит книга.
На самом деле книга точно подходит для POSIX-совместимого getopt()
, но не совсем корректна для поведения по умолчанию версии GNU getopt()
. Это связано с тем, что GNU getopt()
имеет поведение, которое мы уже обсуждали, просматривая весь список аргументов в поисках опций вместо того, чтобы останавливаться на первом аргументе без опций. Тем не менее, идея, которую он пытается передать, та же самая: после того, как getopt()
обработает все варианты, optind
сообщает вам, где в argv
начинаются аргументы, не являющиеся параметрами.
Спасибо большое, вы прояснили мою голову! Теперь я знаю, что забыл, что все переменные имеют свои адреса, поэтому на самом деле *argv
== argv[0]
и argv
== &argv[0]
, что означает, что значение argv
является адресом argv[0]
, но не сам элемент argv[0]
, а значение of argv[0]
— это адрес первого символа первой строки. Еще раз спасибо!
В сторону: стандарт C указывает, что
EOF
является отрицательным, а не обязательно -1, несмотря на то, что большинство реализаций определяют его как. Так что тестируйте против -1, а неEOF
.