Разрешает ли ISO C использовать псевдонимы указателей argv [], передаваемых main ()? - PullRequest
0 голосов
/ 10 июня 2018

ISO C требует, чтобы размещенные реализации вызывали функцию с именем main.Если программа получает аргументы, они принимаются как массив char* указателей, второй аргумент в определении main int main(int argc, char* argv[]).

ISO C также требует, чтобы строки, на которые указывает argv массив может быть изменяемым.

Но могут ли элементы argv псевдонимы друг друга?Другими словами, могут ли существовать i, j такие, что

  • 0 >= i && i < argc
  • 0 >= j && j < argc
  • i != j
  • 0 < strlen(argv[i])
  • strlen(argv[i]) <= strlen(argv[j])
  • argv[i] псевдонимы argv[j]

при запуске программы?Если это так, запись через argv[i][0] также будет видна через строку псевдонимов argv[j].

Соответствующие пункты стандарта ISO C приведены ниже, но не позволяют мне окончательно ответить на основной вопрос.

§ 5.1.2.2.1 Запуск программы

Функция, вызываемая при запуске программы, называется main.Реализация не объявляет прототип для этой функции.Он должен быть определен с типом возврата int и без параметров:

int main(void) { /* ... */ }

или с двумя параметрами (именуемыми здесь argc и argv, хотя могут использоваться любые имена,поскольку они являются локальными для функции, в которой они объявлены):

int main(int argc, char *argv[]) { /* ... */ }

или эквивалент;10) или каким-либо другим способом, определяемым реализацией.

Если они объявлены, параметры функции main должны подчиняться следующим ограничениям:

  • Значение argc должен быть неотрицательным.
  • argv[argc] должен быть нулевым указателем.
  • Если значение argc больше нуля, элементы массива с argv[0] по argv[argc-1] включительно должнысодержат указатели на строки, которым перед запуском программы передаются значения, определяемые реализацией средой хоста.Намерение состоит в том, чтобы предоставить программе информацию, определенную до ее запуска, из другого места в размещенной среде.Если среда хоста не может предоставлять строки с буквами как в верхнем, так и в нижнем регистре, реализация должна обеспечить получение строк в нижнем регистре.
  • Если значение argc больше нуля, строкаобозначенный argv[0] представляет название программы;argv[0][0] должен быть нулевым символом, если имя программы недоступно из среды хоста.Если значение argc больше единицы, строки, указанные с argv[1] по argv[argc-1], представляют параметры программы.
  • Параметры argc и argv и строки, на которые указываетмассив argv должен изменяться программой и сохранять последние сохраненные значения между запуском программы и завершением программы.

Насколько я понимаю, ответ на главный вопрос: "да ", поскольку нигде это явно не запрещено и нигде стандарт не требует или не требует использования char* restrict* -квалифицированного argv, но ответ может включить интерпретацию " и сохранить их последние сохраненные значения междузапуск программы и завершение программы. ".

Практический смысл этого вопроса заключается в том, что если ответ на этот вопрос действительно" да ", переносимая программа, которая хочет изменить строки в argv, должнасначала выполните (эквивалент) POSIX strdup() для них.

Ответы [ 3 ]

0 голосов
/ 10 июня 2018

Насколько я понимаю, ответ на заглавие - «да», поскольку нигде это явно не запрещено и нигде стандарт не требует и не требует использования ограниченного аргумента argv, но ответ может включить интерпретацию.of "и сохраните их последние сохраненные значения между запуском программы и завершением программы.".

Я согласен, что стандарт явно не запрещает элементам аргумента vector быть псевдонимами друг друга.Я не думаю, что положения об изменяемости и сохранении стоимости противоречат этой позиции, но они действительно подсказывают мне, что комитет не рассматривал возможность алиасинга.

Практический смысл этого вопроса заключается в том, чтоесли ответ на этот вопрос действительно «да», переносимая программа, которая хочет изменить строки в argv, должна сначала выполнить (эквивалентно) POSIX strdup () для них.

Действительно,именно поэтому я думаю, что комитет даже не рассматривал возможность.Если бы они сделали это, то наверняка включили бы по крайней мере сноску для того же самого эффекта, иначе явно указали бы, что все строки аргументов различны.

Я склонен думать, что эта деталь ускользнула от внимания комитетапотому что на практике реализации действительно предоставляют разные строки, и потому, что программы редко модифицируют свои строки аргументов (хотя изменение argv само по себе несколько более распространено).Если бы комитет согласился выпустить официальное толкование в этой области, то я не удивлюсь тому, что они выступят против возможности наложения алиасов.

Однако до тех пор, пока такое толкование не будет издано, вы правыэто строгое соответствие не позволяет вам полагаться априори на argv элементов, не являющихся псевдонимами.

0 голосов
/ 10 июня 2018

В качестве точки данных я скомпилировал и запустил следующие программы на нескольких системах.(Отказ от ответственности: эти программы предназначены для предоставления точки данных, но, как мы увидим, они не в конечном итоге отвечают на вопрос, как указано.)

p1.c:

#include <stdio.h>
#include <unistd.h>

int main()
{
    char test[] = "test";
    execl("./p2", "p2", test, test, NULL);
}

p2.c:

#include <stdio.h>

int main(int argc, char **argv)
{
    int i;
    for(i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n");
    argv[1][0] = 'b';
    for(i = 1; i < argc; i++) printf("%s ", argv[i]); printf("\n");
}

В каждом месте, где я пробовал (под MacOS и несколькими версиями Unix и Linux), напечатано

test test 
best test 

Поскольку вторая строка никогда не была "best best", это доказывает, что в тестируемых системах к моменту запуска второй программы строки больше не псевдонимы.

Конечно, этот тест делает not доказывает, что строки в argv никогда не могут быть псевдонимами ни при каких обстоятельствах ни при каких системах.Я думаю, что все это доказывает, что неудивительно, что каждая из протестированных операционных систем переписывает список аргументов, по крайней мере, один раз между временем p1 вызовов execl и временем фактического вызова p2.Другими словами, вектор аргумента, созданный вызывающей программой, не используется непосредственно в вызываемой программе, и в процессе его копирования он (опять-таки не удивительно) «нормализуется», что означает, что эффектылюбого псевдонима теряется.

(я говорю, что это не удивительно, потому что если вы подумаете о том, как на самом деле работает семейство системных вызовов exec, и как распределяется память процесса в Unix-подобных системахнет никакого способа, которым список аргументов вызывающей программы можно было бы использовать напрямую: он имеет , который должен быть скопирован, по крайней мере, один раз, в адресное пространство нового, исполняемого процесса процесса. Кроме того, любой очевидный и прямойметод копирования списка аргументов всегда и автоматически собирается «нормализовать» его таким образом, ядру придется проделать значительную, дополнительную, совершенно ненужную работу, чтобы обнаружить и сохранить любой псевдоним.)

Простоесли это имеет значение, я изменил первую программу следующим образом:

#include <stdio.h>
#include <unistd.h>

int main()
{
    char test[] = "test";
    char *argv[] = {"p2", test, test, NULL};
    execv("./p2", argv);
}

Results не изменились.


С учетом всего вышесказанного я согласен с тем, что эта проблема в стандартах кажется упущением или ошибкой.Я не знаю ни одного предложения, гарантирующего, что строки, на которые указывает argv, различны, а это означает, что параноидально написанная программа, вероятно, не может зависеть от такой гарантии, независимо от того, насколько это вероятно (как показывает этот ответ) любая разумная реализация, вероятно, сделает это таким образом.

0 голосов
/ 10 июня 2018

То, как он работает на распространенных * nix-платформах (включая Linux и Mac OS, предположительно, также FreeBSD), заключается в том, что argv представляет собой массив указателей на одну область памяти, содержащую строки аргументов одну за другой (разделенные тольконулевой терминатор).Использование execl() не меняет этого - даже если вызывающий объект передает один и тот же указатель несколько раз, исходная строка копируется несколько раз, без особого поведения для идентичных (то есть с псевдонимами) указателей (необычный случай без большой выгоды для оптимизации).

Однако C не требует этой реализации.Настоящий параноик, возможно, захочет скопировать каждую строку перед ее изменением, возможно, пропуская копии, если память ограничена и цикл по argv показывает, что ни один из указателей на самом деле не является псевдонимом (по крайней мере, среди тех, которые программа намеревается изменить).Это кажется чрезмерно параноидальным, если вы не разрабатываете программное обеспечение для полета или подобное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...