exit(1);
}
i = atoi (argv[1]); /* строка -> целое, ею изображаемое */
if( argc > 2 ) debug = 1;
printf ("\t*** Таблица простых чисел от 2 до %d ***\n", i);
n = 0;
for (j = 1; j <= i; j++)
if (is_prime (j)){
/* распечатка в COL колонок */
printf ("%3d%s", j, n == COL-1 ? "\n" : "\t");
if( n == COL-1 ) n = 0;
else n++;
}
printf( "\n---\n" );
exit (0);
}
1.35. Составьте программу ввода двух комплексных чисел в виде A + B * I (каждое на
отдельной строке) и печати их произведения в том же виде. Используйте scanf и printf.
Перед тем, как использовать scanf, проверьте себя: что неверно в нижеприведенном опе-
раторе?
int x;
scanf( "%d", x );
Ответ: должно быть написано "АДРЕС от x", то есть scanf( "%d", &x );
1.36. Напишите подпрограмму вычисления корня уравнения f(x)=0 методом деления
отрезка пополам. Приведем реализацию этого алгоритма для поиска целочисленного квад-
ратного корня из целого числа (этот алгоритм может использоваться, например, в машин-
ной графике при рисовании дуг):
/* Максимальное unsigned long число */
#define MAXINT (~0L)
/* Определим имя-синоним для типа unsigned long */
typedef unsigned long ulong;
/* Функция, корень которой мы ищем: */
#define FUNC(x, arg) ((x) * (x) - (arg))
/* тогда x*x - arg = 0 означает x*x = arg, то есть
* x = корень_квадратный(arg) */
/* Начальный интервал. Должен выбираться исходя из
* особенностей функции FUNC */
#define LEFT_X(arg) 0
#define RIGHT_X(arg) (arg > MAXINT)? MAXINT : (arg/2)+1;
/* КОРЕНЬ КВАДРАТНЫЙ, округленный вниз до целого.
* Решается по методу деления отрезка пополам:
* FUNC(x, arg) = 0; x = ?
А. Богатырев, 1992-95 - 13 - Си в UNIX
*/
ulong i_sqrt( ulong arg ) {
register ulong mid, /* середина интервала */
rgt, /* правый край интервала */
lft; /* левый край интервала */
lft = LEFT_X(arg); rgt = RIGHT_X(arg);
do{ mid = (lft + rgt + 1 )/2;
/* +1 для ошибок округления при целочисленном делении */
if( FUNC(mid, arg) > 0 ){
if( rgt == mid ) mid--;
rgt = mid ; /* приблизить правый край */
} else lft = mid ; /* приблизить левый край */
} while( lft < rgt );
return mid;
}
void main(){ ulong i;
for(i=0; i <= 100; i++)
printf("%ld -> %lu\n", i, i_sqrt(i));
}
Использованное нами при объявлении переменных ключевое слово register означает, что
переменная является ЧАСТО ИСПОЛЬЗУЕМОЙ, и компилятор должен попытаться разместить ее
на регистре процессора, а не в стеке (за счет чего увеличится скорость обращения к
этой переменной). Это слово используется как
register тип переменная;
register переменная; /* подразумевается тип int */
От регистровых переменных нельзя брать адрес: &переменная ошибочно.
1.37. Напишите программу, вычисляющую числа треугольника Паскаля и печатающую их в
виде треугольника.
C(0,n) = C(n,n) = 1 n = 0...
C(k,n+1) = C(k-1,n) + C(k,n) k = 1..n
n - номер строки
В разных вариантах используйте циклы, рекурсию.
1.38. Напишите функцию вычисления определенного интеграла методом Монте-Карло. Для
этого вам придется написать генератор случайных чисел. Си предоставляет стандартный
датчик ЦЕЛЫХ равномерно распределенных псевдослучайных чисел: если вы хотите получить
целое число из интервала [A..B], используйте
int x = A + rand() % (B+1-A);
Чтобы получать разные последовательности следует задавать некий начальный параметр
последовательности (это называется "рандомизация") при помощи
srand( число ); /* лучше нечетное */
Чтобы повторить одну и ту же последовательность случайных чисел несколько раз, вы
должны поступать так:
srand(NBEG); x=rand(); ... ; x=rand();
/* и повторить все сначала */
srand(NBEG); x=rand(); ... ; x=rand();
Используемый метод получения случайных чисел таков:
А. Богатырев, 1992-95 - 14 - Си в UNIX
static unsigned long int next = 1L;
int rand(){
next = next * 1103515245 + 12345;
return ((unsigned int)(next/65536) % 32768);
}
void srand(seed) unsigned int seed;
{ next = seed; }
Для рандомизации часто пользуются таким приемом:
char t[sizeof(long)];
time(t); srand(t[0] + t[1] + t[2] + t[3] + getpid());
1.39. Напишите функцию вычисления определенного интеграла по методу Симпсона.
/*#!/bin/cc $* -lm
* Вычисление интеграла по методу Симпсона
*/
#include
extern double integral(), sin(), fabs();
#define PI 3.141593
double myf(x) double x;
{ return sin(x / 2.0); }
int niter; /* номер итерации */
void main(){
double integral();
printf("%g\n", integral(0.0, PI, myf, 0.000000001));
/* Заметьте, что myf, а не myf().
* Точное значение интеграла равно 2.0
*/
printf("%d итераций\n", niter );
}
А. Богатырев, 1992-95 - 15 - Си в UNIX
double integral(a, b, f, eps)
double a, b; /* концы отрезка */
double eps; /* требуемая точность */
double (*f)(); /* подынтегральная функция */
{
register long i;
double fab = (*f)(a) + (*f)(b); /* сумма на краях */
double h, h2; /* шаг и удвоенный шаг */
long n, n2; /* число точек разбиения и оно же удвоенное */
double Sodd, Seven; /* сумма значений f в нечетных и в
четных точках */
double S, Sprev;/* значение интеграла на данной
и на предыдущей итерациях */
double x; /* текущая абсцисса */
niter = 0;
n = 10L; /* четное число */
n2 = n * 2;
h = fabs(b - a) / n2; h2 = h * 2.0;
/* Вычисляем первое приближение */
/* Сумма по нечетным точкам: */
for( Sodd = 0.0, x = a+h, i = 0;
i < n;
i++, x += h2 )
Sodd += (*f)(x);
/* Сумма по четным точкам: */
for( Seven = 0.0, x = a+h2, i = 0;
i < n-1;
i++, x += h2 )
Seven += f(x);
/* Предварительное значение интеграла: */
S = h / 3.0 * (fab + 4.0 * Sodd + 2.0 * Seven );
do{
niter++;
Sprev = S;
/* Вычисляем интеграл с половинным шагом */
h2 = h; h /= 2.0;
if( h == 0.0 ) break; /* потеря значимости */
n = n2; n2 *= 2;
Seven = Seven + Sodd;
/* Вычисляем сумму по новым точкам: */
for( Sodd = 0.0, x = a+h, i = 0;
i < n;
i++, x += h2 )
Sodd += (*f)(x);
/* Значение интеграла */
S = h / 3.0 * (fab + 4.0 * Sodd + 2.0 * Seven );
} while( niter < 31 && fabs(S - Sprev) / 15.0 >= eps );
/* Используем условие Рунге для окончания итераций */
return ( 16.0 * S - Sprev ) / 15.0 ;
/* Возвращаем уточненное по Ричардсону значение */
}
А. Богатырев, 1992-95 - 16 - Си в UNIX
1.40. Где ошибка?
struct time_now{
int hour, min, sec;
} X = { 13, 08, 00 }; /* 13 часов 08 минут 00 сек.*/
Ответ: 08 - восьмеричное число (так как начинается с нуля)! А в восьмеричных числах
цифры 8 и 9 не бывают.
1.41. Дан текст:
int i = -2;
i <<= 2;
printf("%d\n", i); /* печать сдвинутого i : -8 */
i >>= 2;
printf("%d\n", i); /* печатается -2 */
Закомментируем две строки (исключая их из программы):
int i = -2;
i <<= 2;
/*
printf("%d\n", i); /* печать сдвинутого i : -8 */
i >>= 2;
*/
printf("%d\n", i); /* печатается -2 */
Почему теперь возникает ошибка? Указание: где кончается комментарий?
Ответ: Си не допускает вложенных комментариев. Вместо этого часто используются
конструкции вроде:
#ifdef COMMENT
... закомментированный текст ...
#endif /*COMMENT*/
и вроде
/**/ printf("here");/* отладочная выдача включена */
/* printf("here");/* отладочная выдача выключена */
или
/* выключено(); /**/
включено(); /**/
А вот дешевый способ быстро исключить оператор (с возможностью восстановления) -
конец комментария занимает отдельную строку, что позволяет отредактировать такой
текст редактором почти не сдвигая курсор:
/*printf("here");
*/
1.42. Почему программа печатает неверное значение для i2 ?
А. Богатырев, 1992-95 - 17 - Си в UNIX
int main(int argc, char *argv[]){
int i1, i2;
i1 = 1; /* Инициализируем i1 /
i2 = 2; /* Инициализируем i2 */
printf("Numbers %d %d\n", i1, i2);
return(0);
}
Ответ: в первом операторе присваивания не закрыт комментарий - весь второй оператор
присваивания полностью проигнорировался! Правильный вариант:
int main(int argc, char *argv[]){
int i1, i2;
i1 = 1; /* Инициализируем i1 */
i2 = 2; /* Инициализируем i2 */
printf("Numbers %d %d\n", i1, i2);
return(0);
}
1.43. А вот "шальной" комментарий.
void main(){
int n = 10;
int *ptr = &n;
int x, y = 40;
x = y/*ptr /* должно быть 4 */ + 1;
printf( "%d\n", x ); /* пять */
exit(0);
}
/* или такой пример из жизни - взят из переписки в Relcom */
...
cost = nRecords/*pFactor /* divided by Factor, and */
+ fixMargin; /* plus the precalculated */
...
Результат непредсказуем. Дело в том, что y/*ptr превратилось в начало комментария!
Поэтому бинарные операции принято окружать пробелами.
x = y / *ptr /* должно быть 4 */ + 1;
1.44. Найдите ошибки в директивах препроцессора Си [*] (вертикальная черта обозначает
левый край файла).
____________________
[*] Препроцессор Си - это программа /lib/cpp
А. Богатырев, 1992-95 - 18 - Си в UNIX
|
| #include
|#include < sys/types.h >
|# define inc (x) ((x) + 1)
|#define N 12;
|#define X -2
|
|... printf( "n=%d\n", N );
|... p = 4-X;
Ответ: в первой директиве стоит пробел перед #. Диез должен находиться в первой
позиции строки. Во второй директиве в <<>> находятся лишние пробелы, не относящиеся к
имени файла - препроцессор не найдет такого файла! В данном случае "красота" пошла
во вред делу. В третьей - между именем макро inc и его аргументом в круглых скобках