почему execv не может использовать неявное преобразование из char ** в char * const *? - PullRequest
0 голосов
/ 12 декабря 2018

Рассмотрим следующий код:

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

void foo(char * const arg[]) {
    printf("success\n");
}

int main() {
    char myargs[2][64] = { "/bin/ls", NULL };

    foo(myargs);
    execv(myargs[0], myargs);
    return 0;
}

И foo, и execv требуют аргумента char * const *, но пока мой foo работает (я получаю success на выходе) системаЗвонить execv не удается.

Я хотел бы знать, почему.Это как-то связано с реализацией execv?

Кроме того, если у меня есть переменная char** - как я могу отправить ее на execv?

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

И foo, и execv требуют char * const * аргумент,

Да.

но пока мой foo работает (я получаюуспех в выводе) системный вызов execv завершается неудачей.

Получение ожидаемого результата не доказывает, что ваш код правильный.Вызов проявляет неопределенное поведение, потому что его аргумент не соответствует типу параметра, но вполне вероятно, что это имеет небольшой практический эффект, потому что реализация foo() не использует параметр в любом случае.В более общем плане, ваш код может в принципе демонстрировать абсолютно любое поведение, потому что это то, что означает «неопределенный».

Я хотел бы знать, почему.Это как-то связано с реализацией execv?

С точки зрения стандарта, оба вызова демонстрируют одинаково неопределенное поведение.На практике, однако, мы знаем, что execv использует свои аргументы, поэтому для этого вызова было бы гораздо более удивительным, чтобы вызвать ожидаемое вами поведение, чем для вызова foo для получения ожидаемого поведения..

Основная проблема в том, что 2D-массивы - это массивы , а - не указатели .Таким образом, ваш двумерный массив myargs вообще не имеет правильного типа для аргумента любой из функций.

Кроме того, если у меня есть переменная char ** - как я могу отправить ее в execv??

В вашем коде нет такой переменной, но если она у вас есть, вы можете привести ее к соответствующему типу:

char *some_args[] = { "/bin/ls", NULL };
execv((char * const *) some_args);

На практике большинство компилятороввероятно, примет это, если вы опустите бросок тоже, хотя стандарт требует этого.Лучше всего было бы сначала объявить переменную с правильным типом:

char * const correct_args[] = { "/bin/ls", NULL };
execv(correct_args);

Обратите также внимание, что хотя массивы не являются указателями, они преобразуются в указатели в большинстве контекстов -- который я использую в примере кода - но только верхний уровень.Таким образом, массив массивов «распадается» на указатель на массив, а не на указатель на указатель.

0 голосов
/ 12 декабря 2018

Двумерный массив выглядит следующим образом:

char myargs[2][16];

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Я уменьшил размер с 64 до 16, чтобы не допустить раздражающего увеличения диаграммы.

С помощью инициализатора он можетвыглядит так:

char myargs[2][16] = { "/bin/ls", "" }

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| /| b| i| n| /| l| s|\0|  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|\0|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Обратите внимание, я не пытался поместить нулевой указатель во второй ряд.Делать это не имеет смысла, так как это набор символов.В нем нет места для указателя.

Строки расположены в памяти смежно, поэтому, если вы посмотрите на более низкий уровень, на самом деле это будет выглядеть примерно так:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| /| b| i| n| /| l| s|\0|  |  |  |  |  |  |  |  |\0|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Когда вы передаете myargs для функции, известный "распад массива" производит указатель.Это выглядит так:

void foo(char (*arg)[16]);
...
char myargs[2][16] = { "/bin/ls", "" }
foo(myargs);

+-----------+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|  POINTER==|===>| /| b| i| n| /| l| s|\0|  |  |  |  |  |  |  |  |\0|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+-----------+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Указатель arg содержит значение, которое определяет начало массива.Обратите внимание, что нет указателя, указывающего на второй ряд.Если foo хочет найти значение во второй строке, ему нужно знать, насколько велики строки, чтобы можно было разбить следующее:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| /| b| i| n| /| l| s|\0|  |  |  |  |  |  |  |  |\0|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

на это:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| /| b| i| n| /| l| s|\0|  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|\0|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Поэтому arg должно быть char (*arg)[16], а не char **arg или эквивалентным char *arg[].

Семейство функций exec не работает с этим макетом данных.Он хочет это:

+-----------+    +-----------+-----------+
|  POINTER==|===>|  POINTER  |    NULL   |
+-----------+    +-----|-----+-----------+
                       |
/----------------------/
|
|
|    +--+--+--+--+--+--+--+--+
\--->| /| b| i| n| /| l| s|\0|
     +--+--+--+--+--+--+--+--+

И когда вы хотите добавить больше аргументов, он хочет это:

+-----------+    +-----------+-----------+-   -+-----------+
|  POINTER==|===>|  POINTER  |  POINTER  | ... |    NULL   |
+-----------+    +-----|-----+-----|-----+-   -+-----------+
                       |           |
/----------------------/           |
|                                  |
| /--------------------------------/
| |
| |
| |  +--+--+--+--+--+--+--+--+
\-+->| /| b| i| n| /| l| s|\0|
  |  +--+--+--+--+--+--+--+--+
  |
  |  +--+--+--+--+--+--+
  \->| /| h| o| m| e|\0|
     +--+--+--+--+--+--+

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

...