Добрый день, друзья.
Выдалась свободная минутка, решил написать небольшой пост, по мотивам уже заданных мне ранее вопросов. Быть может кому-то эти советы, тоже будут полезны. Итак, сегодня займемся циклами.
Рассмотрим цикл for().
Как вы уже знаете, синтаксис этой конструкции имеет следующий вид:
Кроме того, мы помним, что после цикла всегда стоит один оператор, но если нам необходимо в теле цикла выполнить несколько действий, то мы можем использовать составной оператор {}. Все что заключено в фигурные скобки, будет считаться за один единственный оператор.
Если вы еще не забыли, а это не мудрено с моей-то частотой выпуска занятий, то последнем уроке мы изучали двойные массивы. Одной из типичных задач является вывод массива размерности [N][M] на экран в виде таблицы (или матрицы) в N-строк и M столбцов. Для этой задачи очень крайне удобно использовать два вложенных цикла for. Например:
Листинг 1.
Листинг 2.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++){
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
printf("\n");
}
return (0);
}
Вот так уже лучше. Так, наша программа будет выводить все так, как нужно. Вот, посмотрите на следующую картинку.
А теперь поговорим о красоте. Этот вот дополнительный перенос строки , он как шило в заднице. Из-за него одного пришлось добавить составной оператор. Некрасиво получилось. Есть ли способ этого избежать. Да, есть!
Я вам раньше не говорил, а теперь скажу. В заголовке цикла for на месте инициализации счетчика и на месте изменения счетчика, могут стоять не одна, а сразу несколько инструкций. Чтобы их туда записать нужно использовать еще один оператор, оператор последовательного выполнения – ,. Да-да, просто запятая.
Как это может помочь нам. Да вот как. Смотрите, мы добавляем перенос, на каждой новой итерации внешнего цикла, вот и добавим наш printf в блок изменение счетчика.
Листинг 3.
Рассмотрим один показательный пример. Используем для вывода двойного массива на экран один цикл for. Это будет выглядеть например так:
Листинг 4:
Но такие штуки лучше не проворачивать, так как это может сильно запутать код. Я не буду подробно комментировать этот цикл, попробуйте разобраться самостоятельно, как он работает. Возьмите листочек и пошагово выполните его, будто бы вы компьютер.
Я не помню, рассказывал я о именованных константах, которые использую в своих примерах в этом уроке. На всякий случай расскажу еще раз.
Для объявления именованных констант служит директива #define. Её синтаксис таков
Как работает эта инструкция, рассмотрим на нашем примере. Перед тем как компилятор начнет обрабатывать программу, он пройдется по всему коду и заменит все места, где встречается N или M и вместо них подставит в код их значения: 10 и 8 соответственно. Причем компилятору вообще по барабану, что на что мы заменяем. С учетом этой особенности, некоторые используют при написании код «Боярский язык». Ну т.е. пишут код на обычном русском языке. Например вот так.
Листинг 5.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
#define целое int
#define присвоить =
целое main(void){
целое arr[N][M];
for (целое i присвоить 0;i<N;i++)
for (целое j присвоить 0; j<M; j++)
arr[i][j] присвоить rand();
for (целое i присвоить 0, j присвоить 0 ;i<M,j<N;i++){
printf("%d\t",arr[i][j]);
if (M-i==1){
printf("\n");
j++;
i присвоить 0;
}
}
return (0);
}
И если вы думаете, что такой код не будет работать, то вы ошибаетесь. Хотя, конечно это все для забавы, и писать что-то серьезное так не следует. Но кому стало интересно - погуглите и найдете что-нибудь интересное для себя.
На сегодня всё. Удачи вам и красивого кода. ))
Выдалась свободная минутка, решил написать небольшой пост, по мотивам уже заданных мне ранее вопросов. Быть может кому-то эти советы, тоже будут полезны. Итак, сегодня займемся циклами.
Рассмотрим цикл for().
Как вы уже знаете, синтаксис этой конструкции имеет следующий вид:
for(инициализация счетчика; условие; изменение счетчика)
оператор
оператор
Кроме того, мы помним, что после цикла всегда стоит один оператор, но если нам необходимо в теле цикла выполнить несколько действий, то мы можем использовать составной оператор {}. Все что заключено в фигурные скобки, будет считаться за один единственный оператор.
Если вы еще не забыли, а это не мудрено с моей-то частотой выпуска занятий, то последнем уроке мы изучали двойные массивы. Одной из типичных задач является вывод массива размерности [N][M] на экран в виде таблицы (или матрицы) в N-строк и M столбцов. Для этой задачи очень крайне удобно использовать два вложенных цикла for. Например:
Листинг 1.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
return (0);
}
Но если мы оставим этот код таким, то массив будет выводиться не табличкой, а строчкой. Нужно добавить еще переход на новую строку, после того, как мы закончили выводить все элементы текущей. Ну т.е. после каждого внутреннего цикла for нужно перенести строку. Исправим.#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
return (0);
}
Листинг 2.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++){
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
printf("\n");
}
return (0);
}
Рис.1. Результат работы программы. |
А теперь поговорим о красоте. Этот вот дополнительный перенос строки , он как шило в заднице. Из-за него одного пришлось добавить составной оператор. Некрасиво получилось. Есть ли способ этого избежать. Да, есть!
Я вам раньше не говорил, а теперь скажу. В заголовке цикла for на месте инициализации счетчика и на месте изменения счетчика, могут стоять не одна, а сразу несколько инструкций. Чтобы их туда записать нужно использовать еще один оператор, оператор последовательного выполнения – ,. Да-да, просто запятая.
Как это может помочь нам. Да вот как. Смотрите, мы добавляем перенос, на каждой новой итерации внешнего цикла, вот и добавим наш printf в блок изменение счетчика.
Листинг 3.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++,printf("\n"))
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
return (0);
}
На экране мы изменений не увидим, а код стал меньше и приятнее. ) Вот видите, как может помочь оператор последовательного выполнения. Кстати, обратите внимание, если мы пишем инструкции внутри заголовка цикла, мы не ставим там дополнительных ;. Они там не нужны. #include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0;i<N;i++,printf("\n"))
for (int j=0; j<M; j++)
printf("%d\t",arr[i][j]);
return (0);
}
Рассмотрим один показательный пример. Используем для вывода двойного массива на экран один цикл for. Это будет выглядеть например так:
Листинг 4:
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0, j=0 ;i<N; j++){
printf("%d\t",arr[i][j]);
if (M-j==1){
printf("\n");
i++;
j=-1;
}
}
return (0);
}
А на мониторе снова никаких изменений. ))#include <stdlib.h>
#define N 10
#define M 8
int main(void){
int arr[N][M];
for (int i=0;i<N;i++)
for (int j=0; j<M; j++)
arr[i][j]=rand();
for (int i=0, j=0 ;i<N; j++){
printf("%d\t",arr[i][j]);
if (M-j==1){
printf("\n");
i++;
j=-1;
}
}
return (0);
}
Рис.2. Результат работы программы. |
UPD(от 2.02.14): Я говорил же вам, что этот код может запутать любого. Я и сам попался в эту ловушку. Записанный ранее код, был ошибочен. Стараниями внимательного читателя он исправлен, и теперь работает так, как ему и подобает. =))
Я не помню, рассказывал я о именованных константах, которые использую в своих примерах в этом уроке. На всякий случай расскажу еще раз.
Для объявления именованных констант служит директива #define. Её синтаксис таков
#define имя значение
Важно! Не нужно ставить в конце точку с запятой. А так же между именем и значением знак присвоения =.
Важно! Не нужно ставить в конце точку с запятой. А так же между именем и значением знак присвоения =.
Как работает эта инструкция, рассмотрим на нашем примере. Перед тем как компилятор начнет обрабатывать программу, он пройдется по всему коду и заменит все места, где встречается N или M и вместо них подставит в код их значения: 10 и 8 соответственно. Причем компилятору вообще по барабану, что на что мы заменяем. С учетом этой особенности, некоторые используют при написании код «Боярский язык». Ну т.е. пишут код на обычном русском языке. Например вот так.
Листинг 5.
#include <stdio.h>
#include <stdlib.h>
#define N 10
#define M 8
#define целое int
#define присвоить =
целое main(void){
целое arr[N][M];
for (целое i присвоить 0;i<N;i++)
for (целое j присвоить 0; j<M; j++)
arr[i][j] присвоить rand();
for (целое i присвоить 0, j присвоить 0 ;i<M,j<N;i++){
printf("%d\t",arr[i][j]);
if (M-i==1){
printf("\n");
j++;
i присвоить 0;
}
}
return (0);
}
На сегодня всё. Удачи вам и красивого кода. ))
Спасибо!
ОтветитьУдалить/* Вниманию автора!!!
УдалитьВ Листинге 4 небольшая ошибка.
Он должен выглядеть примерно так:
*/
#include
#define N 3
#define M 4
int main(void)
{
int arr[N][M] ={{1,2,3,4}, {5,6,7,8}, {9,0}};
for (int i=0, j=0 ; j < M, i < N; j++)
{
printf("%d\t",arr[i][j]);
if (M-j==1)
{
printf("\n");
i++;
j=-1;
}//if
} //for
return (0);
}//main
/*
Причём, после нескольких экспериментов, оказалось, что во втором блоке внутри заголовка цикла (на месте условия) цикл сверяется только с последним условием и не важно какая ерунда написана перед ним:
for (int i = 0, j = 0; j < M, i > N, i == 0, i < N; j++). Отразите это в уроке, а если я где-то не прав укажите мне на ошибку.
Уроки просто супер!!! Спасибо.
*/
Дмитрий, огромное вам спасибо, за указанный недочет. Сейчас же всё исправлю.=)))
УдалитьИ да, с условием вы всё верно подметили. )
И небольшая просьба. Можете посоветовать какие-то конкретные учебники по С, С++, в которых всё также доходчиво и подробно расписано как у Вас, желательно чтобы там были примеры и задачки.
УдалитьПросто идти в книжно-компьютерный магазин и выбирать НЕТ ВОЗМОЖНОСТИ, могу только заказать через интернет, но тогда покупаешь по сути кота в мешке т.к. сайты не дают возможность полистать учебники.
А если ждать Ваши новые уроки, то программистом я стану лет так через 20 :)
Если не через 30. =)
УдалитьЯ не знаю таких книг. И не ищите их, надо же развиваться и переходить к более серьезной литературе. И кстати, может вам попрактиковаться в решении олимпиадных задачек?
У вас уже достаточно знаний, попробуйте-ка посмотреть следующий сайт http://acmp.ru/
Спасибо, KaDeaT. Не подскажешь ли ты почему у меня программы на си работают, а вот стоит даже самую элементарную отправить на acmp.ru (их задачу самую первую,например) так ругает и все, никак не принимает
ОтветитьУдалитьСкорее всего, это несоблюдение формата входных и выходных данных. Вы все время пишите "Vvedite N" и прочее. Там всего этого не должно быть. Только то, что требуется. Ну либо вы допускаете какие-то ошибки. Покажите ваш код, который вы отправляете и что вам отвечает система. )
Удалить=) сегодня принял ту же задачу, которую не принял вечером
УдалитьХорошо, что еще есть уроки. Я всегда хотел начать писать, но не знал, с чего начать. За месяца два по твоим урокам(раньше я никакие уроки не понимал, писал код, но не осмыслял его) я научился делать более-менее интересные вещи. Написал игру "угадай число", прикрутил туда счетчики очков и режимы сложности. Теперь пытаюсь сделать крестики нолики, продумываю AI, все благодаря тебе, ты дал мне нужный толчок для развития, а дальше я сам уже начну разбираться. Спасибо.
ОтветитьУдалитьСпасибо, за приятный отзыв. )
УдалитьПрошло 3 года. Хочу сказать, что я уже имею в своем портфолио программу для администрирования групп через VK Api (C#), эмулятор старенькой восьмибитной консоли (C + OpenGL), пару утилит для Linux (C), программы автоматизации для развертывания веб-серверов (Python), парочку веб-приложений на RubyOnRails, расширения для ВК (JS) и несколько игр (Unity + C#). Вроде это не все, есть много мелких программ и скриптов.
УдалитьСпустя 3 года решил заглянуть и вспомнить, как я учился и не понимал урок по указателям. А сейчас для меня все это не представляет серьезной проблемы. Круто поностальгировать и посмотреть на себя в прошлом и заметить развитие. Конечно, если бы не лень, я бы за 3 года мог бы большего добиться. Но я стараюсь ее побороть.
И вам спасибо большое, что не забываете!)
УдалитьОчень благодарен вам за ваши уроки. Действительно прям с нуля можно. Всю неделю бился над создание программы по решению квадратных уравнений
ОтветитьУдалить#include
#include
int main()
{
int a,b,c;
double f,g,d,s;
scanf ("%d", &a);
scanf ("%d", &b);
scanf ("%d", &c);
d=pow(b,2)-4*a*c;
s=sqrt(d);
f=(-b+s)/(2*a);
g=(-b-s)/(2*a);
if (f==g) { printf ("odin coren %f\n", f);}
else {
printf ("%f\n",f);
printf ("%f\n",g);}
return 0;
}
всё благодаря вам. Только у меня был codeblocks
Здравствуйте!
ОтветитьУдалитьОгромное спасибо за уроки, без них было бы непросто начать изучение такого непривычно низкоуровневого языка как С.
Пользуясь случаем, сообщаю, что на площадке pm-pu.ru вижу только вопросительные знаки, перебор кодировок ничего не дал. С другими вашими доменами такой проблемы нет. openSUSE 13.1-13.2 Linux x64.
Добрый день. Я вроде поправлял кодировку. Проверьте, пожалуйста, сейчас всё ещё вопросы?
УдалитьТеперь всё читаемо, спасибо!
Удалить