Данная загадка для новичков, проффи "вкурят" достаточно быстро. Я тут игрался с алгоритмом "пузырьковой" сортировки, получил такой глюк, благо найти его потребовалось чуть больше минуты НО он мне показался весьма поучительным и интересным, посему решил выложить код и дать возможность подумать другим кому любопытно. Попробуйте найти в нем ошибку и разобраться почему переменная len изменяет свое значение после вызова функции сортировки. Тут простой алгоритм "пузырьковой" сортировки. Код НЕ для ардуина. Можете если желаете скомпилировать его у себя и запустить в консоле. И так, код: Код (Text): #include <iostream> void sort( int *, int); void dislayArray( int *, int); int main( void ) { using std::cout; using std::endl; int arr[] = {3214,12,3618,4381,1255,124,435,143,153,1213,41,172,3814,1245,2145}; int len = ( sizeof( arr ) / sizeof( int ) ); cout << "Array size before sort: " << len << endl; cout << "Array before:\t"; dislayArray( arr, len ); sort( arr, len ); cout << "Array size after sort: " << len << endl; cout << "Array after:\t"; dislayArray( arr, len ); return 0; } void sort( int *arr, int size ) { int in = 0; int out = 0; int temp = 0; for(out = size; out > 1; out--) { for( in = 0; in < size; in++ ) { if( arr[ in ] > arr[ in + 1 ]) { temp = arr[ in ]; arr[ in ] = arr[ in + 1 ]; arr[ in + 1 ] = temp; } } } } void dislayArray( int *arr, int size ) { using std::cout; using std::endl; int i; for( i = 0; i < size; i++ ) { cout << arr[ i ] << ' '; } cout << endl; } Вот что выводит программа: Код (Text): Array size before sort: 15 Array before: 3214 12 3618 4381 1255 124 435 143 153 1213 41 172 3814 1245 2145 Array size after sort: 4381 Array after: 12 15 41 124 143 153 172 435 1213 1245 1255 2145 3214 3618 3814 4381 4201160 7876056 6 2130567168 0 0 2686824 4198585 1 3805120 3806864 -1 2686808 1959103701 1285187942 -2 ......................... За многоточиями скрывается еще длинный набор цифр. Попробуйте понять почему вышло так: Array size before sort: 15 Array size after sort: 4381
С глюком все понятно, но спойлерить не буду. Вместо этого немного поругаюсь. С помощью этого кода вы выпустили в мир некоторое количество нечисти: 1) использованием using; 2) объявлением переменной цикла вне цикла без какой-либо на то необходимости; 3) переменные цикла стилистически неотличимы от локальных переменных и аргументов функций; Постарайтесь обходиться без излишних сокращений, иначе в большом коде можете получить неприятные коллизии. В остальном все ОК. Ну еще неплохо было бы употребить const везде, где уместно.
Стилистика С++ для меня сейчас нова, пока только вырабатываю "правила хорошего тона" в С++. Привык в С, указывать переменную цикла вне цикла, так как компилятор при компиляции С ругался на объявления в цикле. Сейчас вот пытаюсь отвыкнуть, не всегда получается С using пока не определился как лучше, тот же Стивен Прата в своей книге рекомендует использовать using с "раскрытием" только требуемых функций и преподносит это как разумный подход, когда using используется лишь в функциях где он нужен и лишь с раскрытием только требуемых функций (using std::cout) В то же время вариант с использованием std::cout считается хорошим но для сокращения строки и улучшения читаемости опять таки рекомендуется способ выше, в общем согласно его совету плохо лишь так: using namespace std; Да еще если это "раскрывает" пространство std на все функции (весь файл). Так что тут пока я не "выработал" правильного подхода За критику большое спасибо, критику я люблю, тем более от намного более опытных людей, всегда читаю, всегда учитываю, всегда "мотаю на ус"
Ну Пратт в принципе верно сказал про using, просто я более критичен. Смешением пространств имн можно дикие глюки поиметь, если там что-то начнет совпадать... Более того, иногда я упаковываю некоторые файлы в свои пространства имен, чтобы изолировать фрагменты кода от возможных перекрытий, особенно если там чисто процедурный подход использован.
В том что вы критично подходите я вас целиком поддерживаю, сам имею некую манию в стремлении добиться максимально качественного кода, благодаря чему часто экспериментирую с разными подходами. Как в самом коде так и архитектуре. По поводу объявления переменной цикла в инициализации цикла, вы совершенно правы, если она объявляется в инициализации цикле то её область видимости лишь цикл, если до инициализации цикла то область видимости вся функция в которой расположен цикл, что может вызвать проблемы если далее переменная с таким же именем будет использована без явного присвоения начального значения или же по опечатке и компилятор даже не заругается ведь такая переменная уже была объявлена. (Сие я разумеется написал не для вас, просто для новичков которые прочитают ваши замечания, дабы были более поняты причины замечания) Имена аргументов функций лично я обычно люблю предварять словом input ... inputSize, inputSpeed, inputWorker ... и тд. тп ... так название явно показывает о входящий данных. Так что лично я, всегда буду рад услышать ваше мнение или критику
И так, кто желал поломал голову, теперь о поучительном глюке для тех кто ломал голову и не ломал. Мы видим что в программе сперва объявляется массив на 15 значений а затем переменная длинны. Дело в том что переменная длинны идет в памяти сразу же за последней ячейкой массива. Функция сортировки работает с массивом переданным по ссылке, а вот размер массива передается по значению, то есть мы в функции напрямую не можем изменить значение переменной в вызывающей функции, но оно ка кто изменилось ..... И так в функции сортировки происходит беда беда. Длинна массива 15 но по факту конечная ячейка имеет значение 14. В условии цикла есть строчка i < size что дает нам крайнее значение i для массива 14 ... это и есть крайняя ячейка массива НО при перемещении используется конструкция arr[ in + 1 ] и вот тут то все и происходит. По сути выходит следующие arr[ 14 + 1 ] что дает нам значение 15-й ячейки которой у массива нет. Указатели это очень сильная сторона С/С++ но очень часто самые сильные стороны бывают и самыми слабыми. Компилятор никак не следит за выходом за границы массива и программа послушно записывается значение в память которая должна была бы принадлежать 15 ячейке массива... а эта память как раз отдана переменной длинны, таким образом в эту ячейку памяти попадает самое большое отсортированное значение из массива. Так, не явно была изменена совсем другая переменная к которой функция сортировки не имела доступа. Баги связанные с указателями считают одними из самых частых и порой одними из самых сложно отыскиваемых, посему будьте внимательны
Не так... Ошибки возникают в тех местах, где самые сильные стороны языка сталкиваются с самыми слабыми сторонами программиста.