Всем добрый день! Собралась небольшая подборка задачек по С/С++, которая может оказаться любопытной как тем, кто только изучает эти языки, так и тем, кто с ними давно знаком. Задачи не все мои, но сейчас затруднительно установить источник, поскольку некоторые из них передавались устно. Задачи простые, но не всегда очевидный ответ на них является правильным. Совет от себя - подумайте перед тем, как компилировать, возможно, Ваши предположения не совпадут с мнением компилятора. 1. Адреса в С. Скомпилируется ли код? если да, то что будет напечатано в результате? если нет, то почему? Code: #include <stdio.h> int main(void){ int x = 5; 0[&x] = x + x; printf("%d", x); return 0; } 2. Обработка исключений С++. Скомпилируется ли код? если да, то что будет напечатано в результате? если нет, то какие именно конструкции приведут к ошибке? Code: #include <iostream> using namespace std; void test(){ try{ int i = 10, j = 20; throw int(10); } catch(...){ i = 20; int j = 20; cout << i << j << endl; } } int main(void){ test(); return 0; } 3. Sscanf(). Скомпилируется ли код? если да, то что будет напечатано в результате? если нет, то какие именно конструкции приведут к ошибке? Code: #include<stdio.h> #include<string.h> int main(void){ char str[6] = "abc"; int i; sscanf(str, "%d", &i); printf("i is %d\n", i); return 0; } 4. Fork() Сколько точек будет напечатано в результате работы программы? Как сделать так, чтобы их было напечатано ровно 6 (есть как минимум два способа, требующих изменить ровно одну строчку в программе). Code: #include <stdio.h> #include <unistd.h> int main(void) { int i; for (i = 0; i < 2; i++) { fork(); printf("."); } return 0; } 5. Сигналы Предположим, была поставлена задача написать обработчик SIGINT, который перехватывал бы пять сигналов, а по шестому программа бы завершалась. Был написан следующий код. Однако в нем есть два серьезных недостатка, когда поведение программы может быть совсем не таким, как ожидал программист. Найдите эти недостатки. Code: #include <stdio.h> #include <signal.h> int counter = 0; void sigint_handler(int sig){ printf("Some very long string about pressing Ctrl+C\n"); if (++counter == 5) { signal(SIGINT, SIG_DFL); raise(SIGINT); } } int main(void){ signal(SIGINT, sigint_handler); while (1) { printf("I'm still running\n"); sleep(1); } return 0; }
Я не до конца понимаю суть функции fork(). Она заставляет сегмент кода, где оглашена, после віполнения основного кода повторить его в конце заново?
Из одного мана. UPD: С примером кода: https://www.opennet.ru/docs/RUS/linux_parallel/node7.html Короче на пальцах - в момент вызова fork создается дочерний процесс - копия родительского у которого eip устанавливается на следующую после fork() команду.
то, что сказал @DartPhoenix. Порождается процесс, который идентичен родительскому за исключением pid, ppid, счетчика ресурсов и списка ожидающих доставки сигналов. Код тот же, что у родителя. Отличить родителя от сына можно по возвращаемому fork() значению. У сына это всегда будет ноль, у отца - если все ок, то pid сына.
Почему код с FORK выводит 8 точек? Потому что идет копирование буфера? Типа так? i = 0, потока 2, в буфер пишутся 2 точки. i = 1, потоков 4, у каждого в буфере по точке, дописывается еще по одной и выводится 8 точек Добавил fflush и стало 6 вместо 8 Code: #include <stdio.h> #include <unistd.h> int main(void) { int i; for (i = 0; i < 2; i++) { fflush (stdout); fork(); printf("."); } return 0; }
А ты пробовал поставить PID вместо точки и посмотреть какой процесс ее напечатал ? printf("PID: %d\n", getpid()); UPD: я и так и так пробовал - 6 получается. Добавил wait() чтобы посмотреть что там: My PID: 3149 Parent PID: 2725 My PID: 3150 Parent PID: 3149 My PID: 3150 Parent PID: 3149 My PID: 3151 Parent PID: 3150 My PID: 3149 Parent PID: 2725 My PID: 3152 Parent PID: 3149 SHIT: отлично. GDB не умеет нормально отлаживать fork(). Раньше вообще не умел, теперь позволяет выбирать: продолжить отлаживать родительский процесс или переключиться на дочерний. И потом меня еще ухитряются спрашивать "поцчему ты не кодишь под Ляликс...". Да потому что !
Если выводить через printf("PID: %d\n", getpid()); то результат 6 а если выводить через printf("."); то результат 8 ВОт такие чудеса
О_о. Черт. Дебугга нет, хрен посмотришь что там твориццо-то... Если добавить \n - будет тоже 6. Если убрать из printf("PID..... - то тоже 8 Срань какая-то. Как они плять своим gdb пользуются... My PID: 2182 Parent PID: 2181 i:0 My PID: 2183 Parent PID: 2182 i:1 My PID: 2182 Parent PID: 2181 i:0 My PID: 2182 Parent PID: 2181 i:1 My PID: 2181 Parent PID: 1938 i:0 My PID: 2184 Parent PID: 2181 i:1 My PID: 2181 Parent PID: 1938 i:0 My PID: 2181 Parent PID: 1938 i:1 ============================= Не ну чисто по логике должно быть как... Основной процесс доходит до fork и раздваивается. Оба процесса имеют i=0 и печатают текст. Оба увеличивают счетчик цикла, соответственно процессов становится 4 и они все дружно вчетвером печатают опять же текст. Всего выводов получается 6. Поцчему именно 2 лишних - хренЬ его знает и как на это влияет \n - тем более Но если предположить слегка... то получается что память, занятая процессом "в прошлой жизни" остается при fork'e и ее надо чистить. Но достоверно сие узнать помог бы gdb... или... область памяти но как ее посмотреть в этом сраном gdb - это уже существенная проблема. Короче х/з.
Ну скорее всего да, но просто любопытно бы посмотреть что за херня... и сколько еще багов может вызвать сие расщепление процесса. UPD: Да. Так и есть. Судьба не была благосклонна к нигга и не позволила одолеть дебугга, но можно просто перед циклом добавить printf("1") например и убрать \n и fflush. Эта единичка будет печататься не один раз, т.ч. это уже не теоретически а имеет подтверждение.
собственно, добавление \n или fflush() и есть два варианта решения. А проблема, насколько я поняла из разных источников, заключается в том, что буфер для всех порожденных процессов - разделяемый ресурс. И без явного сброса начинает ловить глюки. Однако не удалось воспроизвести ситуацию, в которой печаталось бы 7 или 9 точек (по крайней мере, мне). А перенос строки действительно приводит к сбросу буфера.
Случайно на глаза попалась хорошая статья про то, как работает на самом деле буфер вывода в С. Возможно, кому-то будет полезен как минимум инструментарий и ход мыслей. http://osteras.info/personal/2013/10/11/hello-world-analysis.html
согласна, но ход мыслей впечатляет) я не знала многих деталей, которые вскрылись в ходе его "расследования")
с момента форка процесс раздваивается и выполнение следующей команды начинают уже два процесса. Второй форк если поставить следом - каждый процесс тоже раздваивается, получается 4. Третий - 8 и.т.д.
это понятно, я о технической части, новый процесс внезапно начинает хуярить с точки call fork+5? штоле? это впринципе возможно если форк копирнёт по той же базе образ, но стек и контекст никто не отменял, и это волшебство надо ручками поправить штоб заработало. а поинтеры итд?