Чому ця програма друкує «роздвоєно!» 4 рази?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!\n");
return 0;
}
Чому ця програма друкує «роздвоєно!» 4 рази?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!\n");
return 0;
}
Відповіді:
Перший fork()
повертає ненульове значення в процесі виклику (назвіть його p0) і 0 у дочірньому (назвіть це p1).
У p1 прийнято коротке замикання для &&
, і процес викликає printf
та припиняється. У p0 процес повинен оцінити залишок виразу. Потім він fork()
знову викликає , таким чином створюючи новий дочірній процес (p2).
У p0 fork()
повертає ненульове значення, і ||
приймається коротке замикання для , тому процес викликає printf
і завершується.
У p2 fork()
повертає 0, тому залишок || повинен бути оцінений, що є останнім fork()
; що призводить до створення дитини для p2 (назвіть це p3).
Потім Р2 виконується printf
і завершується.
Потім P3 виконується printf
і завершується.
printf
Потім виконується 4 с.
&&
дорівнює нулю, то інші операнди не обчислюються. За тією ж логікою, якщо операнд a ||
дорівнює 1, то інші операнди не потребують оцінки. Це відбувається тому, що решта операнди не можуть змінити результат логічного виразу, тому їх не потрібно виконувати, таким чином ми економимо час. Краще зараз Рона? Приємне питання до речі, я не можу зрозуміти, чому голоси проти. Ви отримали мій +1.
fork()
, але я навіть не знаю, що таке коротке замикання. Це було питання в школі?
Один походить від, main()
а інші три - від кожного fork()
.
Зверніть увагу, що всіх трьох forks()
буде страчено. Можливо, ви захочете поглянути на посилання :
ПОВЕРНЕННЯ ЗНАЧЕННЯ
Після успішного завершення fork () повертає 0 дочірньому процесу та повертає ідентифікатор процесу дочірнього процесу батьківському процесу . Обидва процеси повинні продовжувати виконуватися з функції fork (). В іншому випадку -1 повертається батьківському процесу, не створюється дочірній процес, а errno встановлюється для позначення помилки.
Зверніть увагу, що ідентифікатор процесу не може бути нульовим, як зазначено тут .
То що насправді відбувається?
Ми маємо:
fork() && (fork() || fork());
Отже, перший fork()
поверне батькові його ненульовий ідентифікатор процесу, тоді як поверне 0 дочірньому процесу. Це означає, що перший форк логічного виразу буде оцінений як істина в батьківському процесі, тоді як у дочірньому процесі він буде оцінений як хибний, і, через оцінку короткого замикання , він не викликатиме два інших fork()
s.
Отже, тепер ми знаємо, що збираємося отримати принаймні два відбитки (один з основних і один з 1-го fork()
).
Тепер fork()
буде виконано друге у батьківському процесі, і воно повертає ненульове значення батьківському процесу та нульове одиницю в дочірньому процесі.
Отже, тепер батько не буде продовжувати виконання до останнього fork()
(через коротке замикання), тоді як дочірній процес буде виконувати останній форк, оскільки перший операнд ||
0 дорівнює.
Це означає, що ми отримаємо ще два відбитки.
В результаті ми отримуємо в цілому чотири відбитки.
Коротке замикання
Тут коротке замикання в основному означає, що якщо перший операнд && дорівнює нулю, тоді інший операнд (и) є / не оцінюються. За тією ж логікою, якщо операнд || дорівнює 1, тоді решта операндів не потребують оцінки. Це відбувається тому, що решта операндів не можуть змінити результат логічного виразу, тому їх не потрібно виконувати, таким чином ми економимо час.
Див. Приклад нижче.
Процес
Пам’ятайте, що батьківський процес створює процеси нащадків, які, у свою чергу, створюють інші процеси тощо. Це призводить до ієрархії процесів (або дерева, яке можна сказати).
Маючи це на увазі, варто поглянути на подібну проблему , а також на цю відповідь.
Описне зображення
Я також зробив цю фігуру, яка, мабуть, може допомогти. Я припустив, що fork()
повернуті піди - це 3, 4 та 5 для кожного дзвінка.
Зверніть увагу, що деякі fork()
s мають червоний X над собою, що означає, що вони не виконуються через короткочасне оцінювання логічного виразу.
Значення fork()
s у верхній частині не збираються виконувати, оскільки перший операнд оператора &&
дорівнює 0, отже, весь вираз призведе до 0, тому жодної суті у виконанні решти операндів (ів) &&
.
fork()
На дні не буде виконано, так як це другий операнд ||
, де його перший операнд є ненульове число, таким чином, результат виразу вже оцінюється істина, незалежно від того , що другий операнд є.
І на наступному малюнку ви можете побачити ієрархію процесів: на основі попередньої фігури.
Приклад короткого замикання
#include <stdio.h>
int main(void) {
if(printf("A printf() results in logic true\n"))
;//empty body
if(0 && printf("Short circuiting will not let me execute\n"))
;
else if(0 || printf("I have to be executed\n"))
;
else if(1 || printf("No need for me to get executed\n"))
;
else
printf("The answer wasn't nonsense after all!\n");
return 0;
}
Вихід:
A printf() results in logic true
I have to be executed
Для всіх, хто голосував проти, це з об’єднаного, але іншого питання. Виною тому. Дякую.
Ви можете розкласти задачу на три рядки, і перший, і останній рядки просто подвоюють кількість процесів.
fork() && fork() || fork();
Оператори замикаються, отже, ось що ви отримуєте:
fork()
/ \
0/ \>0
|| fork() && fork()
/\ / \
/ \ 0/ \>0
* * || fork() *
/ \
* *
Отже, це в цілому 4 * 5 = 20 процесів, що друкують один рядок.
Примітка: Якщо з якоїсь причини fork () не вдається (наприклад, у вас є деяке обмеження на кількість процесів), він повертає -1, і тоді ви можете отримати різні результати.
fork() && fork() || fork();
, тоді як запитання тут використовується fork() && (fork() || fork());
. Відбулося злиття, як обговорювалося тут: " meta.stackoverflow.com/questions/281729/… ". Можливо, ви захочете відредагувати свою відповідь, повідомляючи майбутніх читачів.
Виконуючи fork() && (fork() || fork())
, що відбувається
Кожен fork
дає 2 процеси із відповідно значеннями pid (батьківський) та 0 (дочірній)
Перша вилка:
&& (fork() || fork())
||
частини => printforked
|| fork()
forked
forked
forked
Всього: 4 forked
Мені подобаються всі відповіді, які вже були подані. Можливо, якби ви додали ще кілька змінних до свого оператора printf, вам було б легше побачити, що відбувається.
#include<stdio.h>
#include<unistd.h>
int main(){
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld\n", getpid(), child);
return 0;
}
На моїй машині він видав такий результат:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork()));
а потім надрукуйте ПІД та три окремі значення.
Цей код:
fork();
fork() && fork() || fork();
fork();
отримує 20 процесів для себе і 20 разів Printf піде.
І для
fork() && fork() || fork();
printf піде в цілому 5 разів.
fork() && fork() || fork();
, тоді як запитання тут використовується fork() && (fork() || fork());
. Відбулося злиття, як обговорювалося тут: " meta.stackoverflow.com/questions/281729/… ". Можливо, ви захочете відредагувати свою відповідь, повідомляючи майбутніх читачів.