Можно ли изменить argv или мне нужно создать настроенную копию? - PullRequest
29 голосов
/ 08 июня 2009

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

Ответы [ 8 ]

67 голосов
/ 08 июня 2009

Стандарт C99 говорит об изменении argvargc):

Параметры argc и argv и строки, на которые указывает массив argv, должны изменяться программой и сохранять свои последние сохраненные значения между запуском и завершением программы.

24 голосов
/ 08 июня 2009

После того, как argv был передан в метод main, вы можете обращаться с ним как с любым другим массивом C - меняйте его по своему усмотрению, просто помните о том, что вы делаете с ним. Содержимое массива не влияет на код возврата или выполнение программы, кроме того, что вы явно делаете с ним в коде. Я не могу придумать причину, по которой было бы «нецелесообразно» относиться к этому особо.

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

6 голосов
/ 08 июня 2009

В последней версии стандарта C (N1256) говорится, что есть две допустимые формы функции main:

int main (void);
int main (int argc, char* argv[]);

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

Некоторые люди специально используют "const char *" для argv, чтобы запретить изменение аргументов. Если ваша основная функция определена таким образом, вам не разрешено изменять символы, на которые указывает argv[], о чем свидетельствует следующая программа:

pax> cat qq.c
#include <stdio.h>
int main (int c, const char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq qq.c
qq.c: In function `main':
qq.c:3: error: assignment of read-only location

Однако, если вы удалите "const", он будет работать нормально:

pax> cat qq2.c
#include <stdio.h>
int main (int c, char *v[]) {
    *v[1] = 'X';
    printf ("[%s]\n", v[1]);
    return 0;
}

pax> gcc -o qq2 qq2.c ; ./qq2
[Xello]

Я думаю, что это также относится и к C ++. Текущий проект гласит:

All implementations shall allow both of the following definitions of main:
    int main();
    int main(int argc, char* argv[]);

, но он не запрещает другие варианты, так что вы, вероятно, можете принять версию "const" также на C ++ (и, фактически, g ++ это делает).

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

4 голосов
/ 08 июня 2009

Эмпирически, такие функции, как GNU getopt (), переставляют список аргументов, не вызывая проблем. Как говорит @Tim, пока вы играете разумно, вы можете манипулировать массивом указателей и даже отдельными строками. Только не превышайте ни одной из неявных границ массива.

2 голосов
/ 08 июня 2009

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

1 голос
/ 18 мая 2018

Некоторые библиотеки делают это!

Метод инициализации, предоставляемый библиотекой glut opengl (GlutInit) сканирует аргументы, связанные с перенасыщением, и очищает их, перемещая последующие элементы в argv вперед (перемещая указатели, а не фактические строки) и убывающий argc

2,1

glutInit glutInit используется для инициализации библиотеки GLUT.

Использование

void glutInit (int * argcp, char ** argv);

argcp

Указатель на неизмененная переменная argc программы из main. По возвращении значение Указанный argcp будет обновлен, потому что glutInit извлекает любые параметры командной строки, предназначенные для библиотеки GLUT.

ARGV

программы неизмененная переменная argv из main. Как и argcp, данные для argv будут быть обновленным, потому что glutInit извлекает любые параметры командной строки понимается библиотекой GLUT.

1 голос
/ 08 июня 2009

Единственный раз, когда я бы сказал, что прямое манипулирование с argv - это плохая идея, это когда приложение меняет свое поведение в зависимости от содержимого argv [0].

Однако, изменение поведения программы в зависимости от argv [0] само по себе является очень плохой идеей, когда переносимость является проблемой.

Кроме этого, вы можете обращаться с ним так же, как с любым другим массивом. Как сказал Джонатан, GNU getopt () неразрушающе переставляет список аргументов, я видел другие реализации getopt (), которые вплоть до сериализации и даже хэширования аргументов (полезны, когда программа приближается к ARG_MAX).

Просто будьте осторожны с арифметикой указателя.

0 голосов
/ 10 февраля 2016

Исходное распределение argv оставляется на усмотрение компилятора / среды выполнения. Так что может небезопасно изменять его волей-неволей. Многие системы строят его в стеке, поэтому он автоматически освобождается при возвращении main. Другие строят его в куче и освобождают (или нет), когда main возвращается.

Можно безопасно изменить значение аргумента, если вы не пытаетесь его увеличить (ошибка переполнения буфера). Можно сменить порядок аргументов.

Чтобы удалить аргументы, которые вы предварительно обработали , что-то вроде этого будет работать:

(множество условий ошибок не проверено, "--special", кроме первого аргумента, который не проверен и т. Д. Это, в конце концов, просто демонстрационная концепция.)

int main(int argc, char** argv)
{
    bool doSpecial = false; // an assumption
    if (0 == strcmp(argv[1], "--special"))
    {
        doSpecial = true; // no longer an assumption
        // remove the "--special" argument
        //  but do copy the NULL at the end.
        for(int i=1; i<argc; ++i)
            argv[i]  = argv[i+1];
        --argc;
    }
    // all normal processing with "--special" removed.
    // the doSpecial flag is available if wanted.
    return 0;
}

Но посмотрите на это для полной манипуляции: (часть библиотеки libiberty , которая используется для манипулирования векторами в стиле argv)

http://www.opensource.apple.com/source/gcc/gcc-5666.3/libiberty/argv.c

Лицензирован GNU LGPL.

...