Что делает двойной амперсанд в этой программе? - PullRequest
1 голос
/ 15 февраля 2012

Я учусь создавать процессы с помощью fork, и я запутался в следующем. Это код:

int main() {
    int ret = fork();
    // printf("%d\n", ret);
    ret = ret && fork(); /* Here is where I am confused*/
    // print("ret: %d\n", ret);
    if(ret == 0) {
        fork();
    }
    printf("Hello world\n");
    return 1;
}

Итак, что же делает двойной амперсанд? Я запустил программу с «printf», чтобы узнать, какие именно значения, но это стало более запутанным, потому что вывод в первом «printf» равен 0, а во втором «printf» - «1». Так что я не совсем уверен, что делает двойной амперсанд.

Я ценю помощь!

Ответы [ 8 ]

6 голосов
/ 15 февраля 2012

Это называется логическим оператором И. Это позволит оценить логический результат ANDing двух операндов. Свойство этого оператора:

Сначала оценивается левый операнд, если он TRUE (не ноль), то правый операнд оценивается. Если это также правда, тогда все выражение верно или иначе ложно. С другой стороны, если левый операнд равен FALSE, то правый операнд равен , а не . Это может быть сделано, потому что, поскольку один из операндов является ложным, каким бы ни был другой операнд, выражение становится ложным. Это известно как короткое замыкание

В вашем коде, если левая рука, если ret - истина, то оценивается только правая часть, которая в итоге вызывает системный вызов fork (). Возвращаемым значением вызова является AND с текущим значением ret и переназначается на ret.

В основном это работает как

if (ret == TRUE)
{
  retval = fork ();
  ret = ret && retval;
}

Читать это:

В случае успеха PID дочернего процесса возвращается в родительском, а 0 - в дочернем. В случае ошибки -1 возвращается в родительский процесс, дочерний процесс не создается и значение errno устанавливается соответствующим образом.

Рассмотрим ветвление дерева ниже. Каждый "узел" дерева показывает последовательность выполненного оператора в каждом отдельном операторе. Одна работа в одной строке.

    (p1)
+--+ret = fork ();
|   printf 1 shows pid
|   && allows         fork (), ret = 1 = pid1 && pid2 
|   printf 2 shows 1    +
|   `if' not entered    |
|   show hello          |
|                       |    (p3)
|                       +--+ ret = 0 = ret && fork () (this is 0 here)
+-----+                      printf 2 shows 0
      |                      `if' is entered
      |                      fork ()
      |                      show hello
      |                            +
      |                            |
      +                            |
     (p2)                          |
    level 1                        +-------+
    print 0 in 1st printf                  |
    && DOES NOT allow fork ()            (p5)
    print 0 in 2st printf             show hello
   `if' entered
    fork () +-----------+
    show hello          |
                        |
                        +
                      (p4)
                    show hello

Вот что происходит в каждом процессе.

p1 выполняет fork () один раз и имеет pid (ненулевой) в ret. печатает пид короткое замыкание позволяет выполнить fork (). Так как это родитель, он возвращает другой pid, который добавляется к предыдущему дочернему pid, который оценивается как 1. Следовательно, ret теперь содержит 1, который печатается во втором printf. ret равно 1, if не выполняется. Привет напечатано.

p2 Дочерний элемент p1, поэтому ret имеет 0. печатает 0 в первом printf. Короткое замыкание не позволяет fork () звонить. if тело вводится, и вызывается fork (), что составляет (p4). Теперь (p2) приступает к печати Hello.

p3 Дочерний элемент p1, поэтому fork () return равно 0, что равно AND с ret и делает его 0 после присваивания. Это порождается после первого printf, поэтому только второй printf показывает 0. if введено, fork () выполнено, что составляет (p5). Теперь p4 приступает к печати Hello.

p4 начинается с if body, выходит и печатает Hello

p5 начинается с if body, выходит и печатает Hello

Выше я попытался выразить дерево порождения процесса, и последовательность работ в каждом процессе выражена в каждой строке "узла" процесса на дереве. Ребра обозначают икру, а ребра начинаются с соответствующей вилки.

6 голосов
/ 15 февраля 2012

В C двойной амперсанд && представляет собой короткое замыкание логическая операция И .Если у вас есть что-то вроде a && b, то это будет оцениваться следующим образом: он вернет true, если оба значения a и b - true, но если a - false, то b никогда не будет выполнено.

В качестве добавленного примера :

int a = 0;
if (a && myfunc(b)) {
    do_something();
}

В этом примере оценка короткого замыкания гарантирует, что myfunc (b) никогда не вызывается.Это потому, что оценка ложна.Эта функция позволяет две полезные программные конструкции.Во-первых, если первое подвыражение проверяет, нужны ли дорогостоящие вычисления, и проверка оценивается как ложное, во втором аргументе можно исключить дорогостоящие вычисления.Во-вторых, он допускает конструкцию, в которой первое выражение гарантирует условие, без которого второе выражение может вызвать ошибку во время выполнения.

Таким образом, ваш код вызывает только fork(), если ret истинно.ret затем присваивается 0 (ret равно 0) или 1 (оба значения ret и fork() имеют значение true).

2 голосов
/ 15 февраля 2012

Это логическое И, означающее, что ret - это истина (не ноль), а результат fork() - это истина (не ноль), присваивать истину ret, иначе присваивать ложь (ноль) ret.

Поскольку этот оператор с коротким замыканием , fork() будет вызываться, только если ret имеет значение true.

1 голос
/ 15 февраля 2012

Я думаю, что неправильно - неправильное понимание того, как работает fork ().Если вы работаете в UNIX, вам нужно выполнить "man fork", потому что в соответствии с тем, что я прочитал:

ОПИСАНИЕ fork () создает новый процесс, дублируя вызывающий процесс.Новый процесс, называемый дочерним, является точной копией вызывающего процесса, называемого родительским,

и ..

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ В случае успеха, PID дочернего процесса возвращается в родительском, а 0 возвращается в дочернем.В случае ошибки -1 возвращается в родительский, дочерний процесс не создается, и errno устанавливается соответствующим образом.

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

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

0 голосов
/ 15 февраля 2012

На языке C оператор && действует так:

  1. Вычисляет первый аргумент.если он равен нулю, не рассчитывайте второй аргумент, а результат равен 0.
  2. Если первый аргумент не равен нулю, вычислите второй аргумент.если он равен нулю, ответ равен 0. иначе это 1.

Функция fork возвращает 0 для дочернего процесса и еще один номер (pid ребенка) для отца, поэтому после вашегопервый fork, в отцовском процессе ret>0, а в дочернем процессе ret==0.Если вы раскомментируете первый printf, вы получите 0 и другое число.

Затем вы запускаете свою линию.У дочернего элемента ret равен 0, поэтому вычисление && останавливается до разветвления, а ret остается равным 0. У отца ret>0, поэтому он запускает fork() и создает другоеребенок.В родительском процессе fork возвращает положительное число, поэтому ret будет равно 1, а во втором дочернем элементе fork возвращает 0, поэтому ret будет равно 0. Итак, если вы раскомментируете только второе printf, вы получите 0, 0, 1 (возможно, в другом порядке).

Затем вы делаете if (ret==0) fork();, так что оба потомка (чей ret равен 0) создаютновый процесс каждый.Теперь у вас всего 5 процессов, поэтому строка Hello world\n будет напечатана 5 раз.

(довольно опасно использовать функции вывода при работе с fork - у вас есть 5 процессов, которые записывают в один и тот жефайл без какой-либо блокировки, так что вы можете получить результаты, такие как HHHHHeeeeellllllllllooooo wwwwwooooorrrrrlllllddddd\n\n\n\n\n)

0 голосов
/ 15 февраля 2012

Первый fork, ret - fork (), даст 3 возможных результата:

  • > 0 => родитель получает pid дочернего элемента
  • = 0 => дочерний процесс
  • <0 => ошибка с fork

Второй fork, ret = ret && fork (), выполнит fork (), только если ret отличен от нуля.Это может произойти в родительском хорошем случае и родительском случае ошибки.Результат этого оператора может быть следующим:

  • == 0 => ret был ненулевым, а fork () равен нулю.
  • ! = 0 => ret был ненулевым и fork ()был ненулевым.

Третий форк, если (ret == 0) {fork ()}, будет выполняться только если ret равен нулю.

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

Третий ответвление может произойти, если первый ответ на ответвление находился в дочернем элементе, второй ответвление не выполняется, а третий выполняется.Третий форк также может быть выполнен, если первый форк находится в родительском контексте, а второй форк - в дочернем.

Кто-то проверяет эту логику, поскольку я никогда не смог бы написать код такого типа.

0 голосов
/ 15 февраля 2012

Это логическое И короткое замыкание.Если ret равно 0, тогда будет выполнено fork().Если нет, то не будет.Я могу пройтись по коду для вас.

//we fork a child process
int ret = fork();

//ret is 0 if we are in the child process, -1 if fork failed
//otherwise ret is the process id of the child process

//because of this, the fork below executes only within the child process
ret = ret && fork(); /* Here is where I am confused*/

//at this point, if we did fork a process (in the child), then ret is 0 in the child
//then we fork again
if(ret == 0) {
    fork();
}

Итак, у нас есть первый процесс, процесс 1, выполняющий этот код.Давайте предположим, что все форки успешны (но вы должны убедиться в этом ... Я думаю, что это должно быть обработано в существующем коде, но это не так очевидно).

  • Форки процесса 1,создание дочернего процесса с идентификатором процесса 2.
  • ret теперь равен 2 в процессе 1 и 0 в процессе 2.
  • В процессе 1, поскольку ret не равен нулю, разветвления не происходит.
  • В процессе 2 ret равен 0, поэтому мы разветвляем дочерний процесс с идентификатором процесса 3.
  • Теперь ret в процессе 1, 2 и 3 равны 2, 3 и 0 соответственно,
  • Теперь процесс 3 разветвляет нового потомка.
0 голосов
/ 15 февраля 2012

Это ленивая логическая AND функция.Ищите таблицу истинности для AND, если вам нужна дополнительная информация об этом.Это лениво, потому что, если ret равен false, fork() не будет оцениваться, так как что-либо с AND с false всегда false.

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