c ++ Do-while loop in switch-case Statement [duplicate]

Вы можете доказать это очень просто:

console.log(Object.getPrototypeOf(Function.prototype));
console.log(Object.prototype === Object.getPrototypeOf(Function.prototype));

// or, being evil
Object.prototype.testFn = () => console.log('Exists on all objects');
({}).testFn();
[].testFn();
(new Number(5)).testFn();
Math.sqrt.testFn();

Первые две строки показывают, что следующий шаг в цепочке прототипов за пределами Function.prototype is Object.prototype.

Другие строки показывают, что вы можете добавить свойство в Object.prototype (серьезно не делайте этого никогда) и оно существует для всех объектов. В этом случае мы проверяем его на пустой объект, пустой массив, объект Number и на функцию Math.sqrt. Добавляя свойство (функция в этом случае) к Object.prototype, все остальные также получают свойство.

124
задан Peter Mortensen 3 June 2011 в 21:47
поделиться

8 ответов

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

int n = (count + 1) / 8;
switch (count % 8)
{
    LOOP:
case 0:
    if(n-- == 0)
        break;
    putchar('.');
case 7:
    putchar('.');
case 6:
    putchar('.');
case 5:
    putchar('.');
case 4:
    putchar('.');
case 3:
    putchar('.');
case 2:
    putchar('.');
case 1:
    putchar('.');
default:
    goto LOOP;
}
0
ответ дан Aconcagua 20 August 2018 в 23:43
поделиться

Вот подробное объяснение, которое, как я считаю, является сутью устройства Даффа:

Дело в том, что C - это в основном приятный фасад для языка ассемблера (сборка PDP-7 для конкретных , если вы изучили это, вы увидите, насколько поразительны сходства). И на языке ассемблера у вас на самом деле нет циклов - у вас есть метки и инструкции условной ветви. Таким образом, цикл является лишь частью общей последовательности команд с меткой и веткой где-то:

        instruction
label1: instruction
        instruction
        instruction
        instruction
        jump to label1  some condition

и команда переключения несколько разветвляется / прыгает вперед:

        evaluate expression into register r
        compare r with first case value
        branch to first case label if equal
        compare r with second case value
        branch to second case label if equal
        etc....
first_case_label: 
        instruction
        instruction
second_case_label: 
        instruction
        instruction
        etc...

В сборке легко мыслимо, как объединить эти две структуры управления, и когда вы думаете об этом таким образом, их комбинация в C больше не кажется такой странной.

0
ответ дан einpoklum 20 August 2018 в 23:43
поделиться

Хотя я не уверен на 100%, о чем вы просите, здесь идет ...

Проблема в том, что адреса устройства Даффа являются одним из циклов (например, вы, несомненно, увидите на ссылку Wiki, которую вы опубликовали). То, что это в основном приравнивает, - это оптимизация эффективности во время выполнения, по памяти. Устройство Duff имеет дело с серийным копированием, а не только с какой-либо старой проблемой, но является классическим примером того, как оптимизация может быть достигнута за счет сокращения количества раз, когда сравнение должно выполняться в цикле.

As альтернативный пример, который может облегчить понимание, представьте, что у вас есть массив элементов, которые вы хотите перебрать, и каждый раз добавляйте к ним 1 ... обычно вы можете использовать цикл for и цикл около 100 раз. Это кажется довольно логичным и, тем не менее, оптимизация может быть выполнена путем разворачивания цикла (очевидно, не слишком далеко ... или вы можете просто не использовать цикл).

So регулярный цикл:

for(int i = 0; i < 100; i++)
{
    myArray[i] += 1;
}

становится

for(int i = 0; i < 100; i+10)
{
    myArray[i] += 1;
    myArray[i+1] += 1;
    myArray[i+2] += 1;
    myArray[i+3] += 1;
    myArray[i+4] += 1;
    myArray[i+5] += 1;
    myArray[i+6] += 1;
    myArray[i+7] += 1;
    myArray[i+8] += 1;
    myArray[i+9] += 1;
}

То, что делает устройство Duff, реализует эту идею на C, но (как вы видели на Wiki) с серийными копиями , То, что вы видите выше, с размотанным примером, составляет 10 сравнений по сравнению с 100 в оригинале - это составляет незначительную, но, возможно, значительную оптимизацию.

3
ответ дан James B 20 August 2018 в 23:43
поделиться
  • 1
    Вам не хватает ключевой части. Речь идет не только о перемотке цикла. Оператор switch переходит в середину цикла. Вот почему устройство выглядит таким запутанным. Ваш цикл выше всегда выполняет кратное 10 копий, но Дафф выполняет любое число. – Rob Kennedy 5 February 2009 в 02:37
  • 2
    Это правда, но я пытался упростить описание для OP. Возможно, я этого не понял! :) – James B 5 February 2009 в 09:20

Точка устройства duffs состоит в том, чтобы уменьшить количество сравнений, выполненных в жесткой реализации memcpy.

Предположим, вы хотите скопировать байты «count» с a на b, прямой подход - сделать следующее:

  do {                      
      *a = *b++;            
  } while (--count > 0);

Сколько раз вам нужно сравнивать счетчик, чтобы увидеть, если он выше 0? «count».

Теперь устройство duff использует неприятный непреднамеренный побочный эффект корпуса коммутатора, который позволяет уменьшить количество сравнений, необходимых для подсчета / 8.

Теперь предположим, что вы хотите скопировать 20 байтов с помощью устройства duffs, сколько сравнений вам нужно? Только 3, поскольку вы копируете восемь байтов за раз, за ​​исключением последнего первого, где вы копируете только 4.

ОБНОВЛЕНО: вам не нужно делать 8 сравнений / case-in-switch, но это разумный компромисс между размером функции и скоростью.

11
ответ дан Lawrence Dol 20 August 2018 в 23:43
поделиться
  • 1
    Обратите внимание, что устройство Duff не ограничено 8 дублированиями в инструкции switch. – strager 5 February 2009 в 02:29
  • 2
    почему вы не можете использовать вместо --count, count = count-8? и использовать второй цикл для работы с остатком? – hhafez 5 February 2009 в 02:36
  • 3
    Hhafez, вы можете использовать второй цикл для работы с остатком. Но теперь у вас есть вдвое больше кода, чтобы выполнить одно и то же без увеличения скорости. – Rob Kennedy 5 February 2009 в 02:39
  • 4
    Йохан, ты отступил назад. Остальные 4 байта копируются на first итерации цикла, а не последние. – Rob Kennedy 5 February 2009 в 02:40
  • 5
    @Rob: Отредактировано, чтобы исправить это. – Lawrence Dol 5 February 2009 в 06:00

Когда я впервые прочитал его, я автоматически отформатировал его на этом

void dsend(char* to, char* from, count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
        case 0: do {
                *to = *from++;
                case 7: *to = *from++;
                case 6: *to = *from++;
                case 5: *to = *from++;
                case 4: *to = *from++;
                case 3: *to = *from++;
                case 2: *to = *from++;
                case 1: *to = *from++;
            } while (--n > 0);
    }
}

, и я понятия не имел, что происходит.

Возможно, нет, когда задан этот вопрос , но теперь Wikipedia имеет очень хорошее объяснение

. Устройство является действительным, законным C в силу двух атрибутов в C:

  • Расслабленная спецификация оператора switch в определении языка. Во время изобретения устройства это был первый выпуск языка программирования C, который требует только того, чтобы управляемая инструкция коммутатора была синтаксически корректным (составным) оператором, в котором метки меток могут отображаться с префиксом любого подзаголовка. В сочетании с тем фактом, что при отсутствии инструкции break поток контроля будет проходить через оператор, управляемый одним ярлыком case, на управляемый следующим, это означает, что код указывает последовательность копий count из последовательные исходные адреса на порт вывода с отображением памяти.
  • Возможность юридически переходить в середину цикла в C.
8
ответ дан Lazer 20 August 2018 в 23:43
поделиться

Объяснение в журнале доктора Добба - лучшее, что я нашел на эту тему.

Это мой момент AHA:

for (i = 0; i < len; ++i) {
    HAL_IO_PORT = *pSource++;
}

становится:

int n = len / 8;
for (i = 0; i < n; ++i) {
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
    HAL_IO_PORT = *pSource++;
}

:) становится:

int n = (len + 8 - 1) / 8;
switch (len % 8) {
    case 0: do { HAL_IO_PORT = *pSource++;
    case 7: HAL_IO_PORT = *pSource++;
    case 6: HAL_IO_PORT = *pSource++;
    case 5: HAL_IO_PORT = *pSource++;
    case 4: HAL_IO_PORT = *pSource++;
    case 3: HAL_IO_PORT = *pSource++;
    case 2: HAL_IO_PORT = *pSource++;
    case 1: HAL_IO_PORT = *pSource++;
               } while (--n > 0);
}
100
ответ дан Peter Mortensen 20 August 2018 в 23:43
поделиться
  • 1
    Хорошая почта (плюс я должен найти один хороший ответ от вас, чтобы перевернуть;) 2 вниз, 13, чтобы пойти: stackoverflow.com/questions/359727#486543 ). Наслаждайтесь приятным значком. – VonC 6 February 2009 в 08:40
  • 2
    Спасибо за комментарии, объясняющие синтаксис; +1! – Pops 1 June 2010 в 17:56
  • 3
    +1 потому что downvoting кто-то, потому что вы не понимаете синтаксис Си, несправедливо – Dancrumb 28 August 2010 в 19:20
  • 4
    Важнейший факт здесь, и который сделал устройство Даффа непонятным для меня в течение самого долгого времени, заключается в том, что при причуде C после первого раза он достигает времени, он отскакивает назад и выполняет все утверждения. Таким образом, даже если len%8 равен 4, он выполнит случай 4, случай 2, случай 2 и случай 1, а затем отскочит назад и выполнит все случаи из следующего цикла вперед. Это часть, которая требует объяснения, как цикл и оператор переключения «взаимодействуют». – ShreevatsaR 24 January 2012 в 14:24
  • 5
    Статья доктора Доббса хороша, однако, помимо ссылки, ответ ничего не добавляет. См. Ответ Роба Кеннеди ниже, в котором фактически содержится важный момент о том, как обрабатывается оставшийся размер передачи, за которым следуют ноль или более блоков передачи из 8 байтов. По-моему, это ключ к пониманию этого кода. – Richard Chambers 21 April 2013 в 00:03
  • 6
    Я что-то упустил, или во втором фрагменте кода len % 8 байты не будут скопированы? – newbie 30 March 2014 в 22:54
  • 7
    Как можно исключить аргумент 0: и продолжить проверку других предложений, которые находятся внутри цикла do while, который является аргументом пропущенного предложения? Если исключено только условие, выходящее за цикл do while, почему коммутатор не заканчивается? – Aurelius 17 September 2015 в 14:26
  • 8
    Не смотрите на скобки так сильно. Не смотрите на do так много. Вместо этого посмотрите на switch и while в виде старомодных вычисленных GOTO статусов или ассемблерных jmp операторов со смещением. switch выполняет некоторую математику, а затем jmp s в нужном месте. while выполняет булевскую проверку, а затем слепо jmp s справа, где находился do. – Clinton Pierce 17 September 2015 в 18:40

1: Устройство Duffs - это особая модификация разворота цикла. Что такое разворот цикла? Если у вас есть операция для выполнения N раз в цикле, вы можете торговать размером программы для скорости, выполняя цикл N / n раз, а затем в петле, вставляя (разматывая) код цикла n раз, например. заменяя:

for (int i=0; i<N; i++) {
    // [The loop code...] 
}

с

for (int i=0; i<N/n; i++) {
    // [The loop code...]
    // [The loop code...]
    // [The loop code...]
    ...
    // [The loop code...] // n times!
}

Это отлично работает, если N% n == 0 - нет необходимости в Duff! Если это не так, вам придется обрабатывать остаток - это боль.

2: Как устройство Duffs отличается от этого стандартного цикла? Устройство Duffs - это всего лишь умный способ борьбы с циклами циклов останова, когда N% n! = 0. Весь do / while выполняет N / n число раз в соответствии со стандартным циклом (поскольку применяется случай 0). При последнем прохождении цикла («N / n + 1'-й раз) случай срабатывает, и мы переходим в случай N% n и запускаем код цикла« остальное »количество раз.

6
ответ дан Ricibob 20 August 2018 в 23:43
поделиться

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

Второй аспект - это оператор switch. Это позволяет коду с первого раза перейти в середину середины цикла. Удивительная часть большинства людей заключается в том, что такая вещь разрешена. Ну, это разрешено. Выполнение начинается с вычисленного ярлыка case, а затем попадает через в каждый следующий оператор присваивания, как и любой другой оператор switch. После последней метки ярлыка выполнение достигает нижней части цикла, после чего оно возвращается к вершине. Верхняя часть цикла внутри оператора switch, поэтому переключатель больше не переоценивается.

Исходный цикл разматывается восемь раз, поэтому число итераций разделяется на восемь. Если количество байтов, которые нужно скопировать, не кратно восьми, то есть несколько оставшихся байтов. Большинство алгоритмов, которые копируют блоки байтов за один раз, будут обрабатывать оставшиеся байты в конце, но устройство Даффа обрабатывает их в начале. Функция вычисляет count % 8 для оператора switch, чтобы определить, что останется в остатке, перескакивает на метку case для этого количества байтов и копирует их. Затем цикл продолжает копировать группы из восьми байтов.

68
ответ дан Rob Kennedy 20 August 2018 в 23:43
поделиться
  • 1
    Это имело смысл для меня. +1 – Almo 23 February 2012 в 18:35
  • 2
    Это объяснение имеет больше смысла. ключ для меня понять, что остаток копируется сначала, а остальное в блоках по 8 байт, что необычно, поскольку, как упоминалось большую часть времени, вы копируете в блоках по 8 байт, а затем копируете остаток. выполнение остатка сначала является ключом к пониманию этого алгоритма. – Richard Chambers 20 April 2013 в 23:55
  • 3
    +1 для упоминания сумасшедшего размещения / вставки цикла switch / while. Невозможно представить, исходя из языка, такого как Java ... – Parobay 3 February 2014 в 09:06
Другие вопросы по тегам:

Похожие вопросы: