Что char * const argv []?

Я изучаю Linux-программу на С++, когда вижу

int execve(const char *path,
           char *const argv[],
           char *const envp[]);

Я не понимаю, что такое char *const argv[]. Я знаю, что char *const foo является указателем const на char. И const char *foo является указателем на const char. Но что char *const argv[]?

Является ли это массивом константных указателей на char или массивом указателей на const char?

И теперь у меня есть vector<string>, как преобразовать его в char *const argv[]?

Ответ 1

Является ли это массивом константных указателей на char или массивом указателей на const char?

Чтение типов справа налево:
const всегда связывается влево (если он не находится слева)

char *const argv[]

                ^^ Array of
      ^^^^^        const
     ^             pointer to
^^^^               char

Массив от "const pointer" до char

Таким образом, указатели в массиве не могут быть изменены.
Но то, на что они указывают, может быть изменено.

Примечание: Как указал Стив ниже. Потому что это используется как параметр. Он фактически распадается на "указатель на "const pointer" до char" или "char * const *".

И у меня есть вектор <string> теперь, как преобразовать его в char * const argv []?

int execve(const char *path,
       char *const argv[],
       char *const envp[]);

int main()
{
    std::vector<std::string>   data;  /// fill
    std::vector<char*>         argv;
    std::vector<char*>         envp;

    for(std::vector<std::string>::iterator loop = data.begin(); loop != data.end(); ++loop)
    {
        argv.push_back(&(*loop)[0]);
    }

    // I also bet that argv and envp need to be NULL terminated
    argv.push_back(NULL);
    envp.push_back(NULL);
    execve("MyAppName", &argv[0], &envp[0]);
}

Ответ 2

cdecl.org говорит:

char *const argv[]

объявить argv как массив указателя const на char

Ответ 3

// says: argv is a constant pointer pointing to a char*
int main(int c, char *const argv[]);

Вы не сможете изменить "argv" внутри функции.

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

В области параметров и массивов есть несколько запутанных правил, которые должны быть ясными перед продолжением. Во-первых, то, что вы объявляете в списке параметров, обрабатывается специальным образом. Существуют такие ситуации, когда вещи не имеют смысла в качестве параметра функции в C. Это

  • Функции как параметры
  • Массивы как параметры

Массивы как параметры

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

В C по этим причинам значения массива не существуют. Если вы хотите получить значение массива, то вместо этого вы будете указателем на первый элемент этого массива. И здесь на самом деле уже лежит решение. Вместо того, чтобы рисовать параметр массива недействительным, C-компилятор преобразует тип соответствующего параметра в указатель. Помните это, это очень важно. Параметр не будет массивом, но вместо этого он будет указателем на соответствующий тип элемента.

Теперь, если вы попытаетесь передать массив, то вместо этого передается указатель на первый элемент массива.

Экскурсия: функции как параметры

Для завершения и потому, что я думаю, что это поможет вам лучше понять суть вопроса, давайте посмотрим, что такое состояние дел, когда вы пытаетесь использовать функцию в качестве параметра. Действительно, сначала это не будет иметь никакого смысла. Как параметр может быть функцией? Конечно, мы хотим переменную в этом месте! Итак, что делает компилятор, когда это происходит, опять же, чтобы преобразовать функцию в указатель функции. Попытка передать функцию будет передавать указатель на эту соответствующую функцию. Итак, следующие же (аналогичные примеру массива):

void f(void g(void));
void f(void (*g)(void));

Обратите внимание, что нужны скобки вокруг *g. В противном случае указана функция, возвращающая void* вместо указателя на функцию, возвращающую void.

Вернуться к массивам

Теперь я сказал в начале, что массивы могут иметь неполный тип - что происходит, если вы еще не указали размер. Поскольку мы уже поняли, что параметр массива не существует, но вместо этого любой параметр массива является указателем, размер массива не имеет значения. Это означает, что компилятор переведет все следующие, и все будет одним и тем же:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

Конечно, не имеет смысла вставлять в него какие-либо размеры, и это просто выброшено. По этой причине C99 придумал новое значение для этих чисел и позволяет другим вещам появляться между скобками:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

Последние две строки говорят, что вы не сможете изменить "argv" внутри функции - он стал указателем const. Тем не менее, только несколько компиляторов C поддерживают эти функции C99. Но эти функции дают понять, что "массив" на самом деле не один. Это указатель.

Слово предупреждения

Обратите внимание, что все сказанное выше верно только тогда, когда у вас есть массив как параметр функции. Если вы работаете с локальными массивами, массив не будет указателем. Он будет вести себя как указатель, потому что, как объяснялось ранее, массив будет преобразован в указатель, когда его значение будет считано. Но его не следует путать с указателями.

Один классический пример:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;