[C/C++] простые, но забавные задачи для начинающих

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Franky_T, 10 Nov 2018.

  1. Franky_T

    Franky_T Level 8

    Joined:
    6 Nov 2018
    Messages:
    21
    Likes Received:
    66
    Reputations:
    58
    Всем добрый день!

    Собралась небольшая подборка задачек по С/С++, которая может оказаться любопытной как тем, кто только изучает эти языки, так и тем, кто с ними давно знаком. Задачи не все мои, но сейчас затруднительно установить источник, поскольку некоторые из них передавались устно. Задачи простые, но не всегда очевидный ответ на них является правильным. Совет от себя - подумайте перед тем, как компилировать, возможно, Ваши предположения не совпадут с мнением компилятора.

    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;
    }
    
     
    pravdaru, gooselin, _r_rust and 5 others like this.
  2. #colorblind

    #colorblind Moderator

    Joined:
    31 Jan 2014
    Messages:
    637
    Likes Received:
    246
    Reputations:
    42
    и зачем?
     
  3. SooLFaa

    SooLFaa Members of Antichat

    Joined:
    17 Mar 2014
    Messages:
    530
    Likes Received:
    499
    Reputations:
    154
    Всмысле зачем?! Прикольные таски на понимание языка. Я над 4 две недели сидел. Очень рекомендую.
     
    _________________________
  4. skibidi

    skibidi Banned

    Joined:
    31 Oct 2018
    Messages:
    6
    Likes Received:
    0
    Reputations:
    0
    Я не до конца понимаю суть функции fork(). Она заставляет сегмент кода, где оглашена, после віполнения основного кода повторить его в конце заново?
     
  5. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    Из одного мана.
    UPD: С примером кода: https://www.opennet.ru/docs/RUS/linux_parallel/node7.html
    Короче на пальцах - в момент вызова fork создается дочерний процесс - копия родительского у которого eip устанавливается на следующую после fork() команду.
     
    #5 DartPhoenix, 14 Nov 2018
    Last edited: 14 Nov 2018
    erwerr2321, Suicide and Franky_T like this.
  6. Franky_T

    Franky_T Level 8

    Joined:
    6 Nov 2018
    Messages:
    21
    Likes Received:
    66
    Reputations:
    58
    то, что сказал @DartPhoenix. Порождается процесс, который идентичен родительскому за исключением pid, ppid, счетчика ресурсов и списка ожидающих доставки сигналов. Код тот же, что у родителя. Отличить родителя от сына можно по возвращаемому fork() значению. У сына это всегда будет ноль, у отца - если все ок, то pid сына.
     
    Suicide likes this.
  7. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    492
    Likes Received:
    187
    Reputations:
    5
    Почему код с 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;
    }
     
  8. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    А ты пробовал поставить 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(). Раньше вообще не умел, теперь позволяет выбирать: продолжить отлаживать родительский процесс или переключиться на дочерний. И потом меня еще ухитряются спрашивать "поцчему ты не кодишь под Ляликс...". Да потому что ! :)
     
    #8 DartPhoenix, 15 Nov 2018
    Last edited: 15 Nov 2018
  9. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    492
    Likes Received:
    187
    Reputations:
    5
    Если выводить через
    printf("PID: %d\n", getpid());
    то результат 6

    а если выводить через
    printf(".");
    то результат 8

    ВОт такие чудеса
     
    Franky_T likes this.
  10. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    О_о. Черт. Дебугга нет, хрен посмотришь что там твориццо-то...
    Если добавить \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 - это уже существенная проблема. Короче х/з.
     
    #10 DartPhoenix, 15 Nov 2018
    Last edited: 15 Nov 2018
  11. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    492
    Likes Received:
    187
    Reputations:
    5
    Я думаю \n
    делает fflush каким то образом
     
    DartPhoenix likes this.
  12. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    Ну скорее всего да, но просто любопытно бы посмотреть что за херня... и сколько еще багов может вызвать сие расщепление процесса.

    UPD: Да. Так и есть. Судьба не была благосклонна к нигга и не позволила одолеть дебугга, но можно просто перед циклом добавить printf("1") например и убрать \n и fflush.
    Эта единичка будет печататься не один раз, т.ч. это уже не теоретически а имеет подтверждение.
     
    #12 DartPhoenix, 15 Nov 2018
    Last edited: 15 Nov 2018
    rudi and Franky_T like this.
  13. Franky_T

    Franky_T Level 8

    Joined:
    6 Nov 2018
    Messages:
    21
    Likes Received:
    66
    Reputations:
    58
    собственно, добавление \n или fflush() и есть два варианта решения.
    А проблема, насколько я поняла из разных источников, заключается в том, что буфер для всех порожденных процессов - разделяемый ресурс. И без явного сброса начинает ловить глюки. Однако не удалось воспроизвести ситуацию, в которой печаталось бы 7 или 9 точек (по крайней мере, мне).
    А перенос строки действительно приводит к сбросу буфера.
     
    rudi likes this.
  14. Franky_T

    Franky_T Level 8

    Joined:
    6 Nov 2018
    Messages:
    21
    Likes Received:
    66
    Reputations:
    58
    Случайно на глаза попалась хорошая статья про то, как работает на самом деле буфер вывода в С. Возможно, кому-то будет полезен как минимум инструментарий и ход мыслей.
    http://osteras.info/personal/2013/10/11/hello-world-analysis.html
     
    crlf and DartPhoenix like this.
  15. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    Тот неловкий момент когда проще дизассемблировать Идой чем ковыряться в сурцах Ляликса :)
     
  16. Franky_T

    Franky_T Level 8

    Joined:
    6 Nov 2018
    Messages:
    21
    Likes Received:
    66
    Reputations:
    58
    согласна, но ход мыслей впечатляет) я не знала многих деталей, которые вскрылись в ходе его "расследования")
     
  17. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,311
    Reputations:
    327
    чёто я не понял смысл форка. обясните пжалста
     
  18. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,108
    Likes Received:
    8,496
    Reputations:
    25
    с момента форка процесс раздваивается и выполнение следующей команды начинают уже два процесса. Второй форк если поставить следом - каждый процесс тоже раздваивается, получается 4. Третий - 8 и.т.д.
     
  19. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,311
    Reputations:
    327
    это понятно, я о технической части, новый процесс внезапно начинает хуярить с точки call fork+5? штоле? это впринципе возможно если форк копирнёт по той же базе образ, но стек и контекст никто не отменял, и это волшебство надо ручками поправить штоб заработало. а поинтеры итд?
     
  20. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,311
    Reputations:
    327
    и правильно делаешь, потому что это долбоебизм другой архитектуры. жаль что выпилился.