Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Assembler / P13

.pdf
Скачиваний:
59
Добавлен:
02.06.2015
Размер:
416.13 Кб
Скачать

#P4#4:

num = -num;

 

cs:029D 8BC2

mov

ax,dx

cs:029F F7D8

neg

ax

cs:02A1 8BD0

mov

dx,ax

#P4#5:

return num;

 

cs:02A3 8BC2

mov

ax,dx ; вернуть результат в AX

#P4#6: }

 

 

 

cs:02A5 5D

pop

bp

cs:02A6 C3

ret

 

Упражнение. Какое предупреждение выдаст компилятор, если поместить определение функции после функции main? Добавьте перед функцией main прототип int abs_int(int); и убедитесь, что предупреждений нет.

Пример: Функция обменивает значения своих двух параметров.

Сначала опишем функцию swap, а вслед за ней функцию main. При таком порядке расположения описаний прототип не нужен. При компиляции функции main количество и тип параметров известны.

Решение (неверное!).

#include <stdio.h> void swap( int a, int b) {

int t; t = a; a = b; b = t;

}

int main() {

int c = 5, d = 8; swap( c, d);

printf("c = %d, d = %d\n", c, d); return 0;

}

Результат: c = 5, d = 8

В чем ошибка? При вызове функции ее параметры (а точнее, их копии) помещаются в стек:

младший адрес

адрес возврата

 

c (5)

старший адрес

d (8)

Функция меняет в стеке значения

младший адрес

старший адрес

адрес возврата

c(8)

d(5)

После этого в главной программе (функции main) стек уничтожается. Исходные переменные, естественно, не претерпевают изменений, меняются лишь их копии в стеке.

Упражнение. Проверьте в TD эти утверждения. Откомпилируйте программу так: bcc –r– –v swap.c

(т.е. запретите использование регистровых переменных).

Как исправить программу? Надо записывать в стек не сами переменные, а их адреса, и менять значения по этим адресам.

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

#include <stdio.h>

void swap( int *a, int *b); // можно и так: void swap( int *, int *); int main() {

int c = 5, d = 8;

swap( &c, &d); // вызов изменился! printf("c = %d, d = %d\n", c, d); return 0;

}

void swap( int *a, int *b){ int t;

t = *a; *a = *b; *b = t;

}

13.7. Примеры использования функций. Функция суммирует элементы массива.

В функцию нужно передать два параметра: адрес массива и его размер.

#include <stdio.h> int sum( int a[], int n); int main() {

int a[] = {5, -3, 8}, dim_a = sizeof(a) / sizeof(a[0]); printf("sum = %d\n", sum( a, dim_a));

getchar(); return 0;

}

int sum( int a[], int n) { int i, summa;

for (i = summa = 0; i < n; i++) summa += a[i];

return summa;

}

Самостоятельно проанализируйте сгенерированный код.

Замечание. Типичная ошибка начинающих: использовать в функции ее имя как возвращаемое значение

int sum( int a[], int n) { int i;

for (i = sum = 0; i < n; i++) sum += a[i];

return sum;

}

Последует сообщение об ошибке: Lvalue required. Имя функции не является объектом, которому можно присвоить значение. Любопытно, что если добавить описание локальной переменной int i, sum;, то компиляция пройдет нормально, и функция будет работать правильно.

Задача. Написать функцию, которая возвращает максимальный элемент массива длинных целых и номер его первой позиции. Главная программа выводит эти значения.

13.8.Классы памяти переменных. Область видимости.

…………

13.9.Функция scanf().

До этого момента мы могли вводить в программу только одиночные символы с помощью getchar(). Теперь мы сможем организовать ввод в программу произвольных значений.

Освоим функцию для ввода scanf (сканирование форматное). Она аналогична функции printf. В списке ее аргументов сначала размещается форматная строка, а затем через запятую перечисляются указатели на вводимые значения. Типичная ошибка начинающих: перечисляют не указатели на переменные, а имена переменных. Функция возвращает количество прочитанных значений.

Вводимые значения пользователь может разделять любым количеством пробелов, табуляций и символов новой строки.

Пример. файл sc.c

#include <stdio.h> int main() {

int a, b; int k;

puts("Input a,b");

k = scanf("%d%d", &a, &b); printf("k = %d\n",k);

printf("a = %d, b = %d\n", a, b); return 0;

}

Примеры диалога с программой

1)С:\prog\p13>SC.EXE Input a,b

10 20 (значения разделены одним пробелом) k = 2 (прочитаны значения двух переменных) a = 10, b = 20

2)Вводимые значения разделены нескольким пробелами, табуляциями и нажатиями клавиши Enter.

С:\prog\p13 >SC.EXE Input a,b

10

20 k = 2

a = 10, b = 20

3)Вводимые значения разделены запятой.

С:\prog\p13 >SC.EXE Input a,b

10,20

k = 1 (прочитано значение только одной переменной) a = 10, b = 0

Исправим вызов функции

k = scanf("%d,%d", &a, &b);

Теперь мы ввели в форматную строку запятую. Самостоятельно убедитесь, что теперь вводимые значения, обязательно должны быть разделены запятой.

Интересное решение дается следующим оператором ввода k = scanf("%d%*с%d", &a, &b);

Запятая будет прочитана как символ, но не присвоена (присваивание запрещает звездочка перед спецификатором). Теперь правильно будут обработаны следующие попытки ввода

10 20

 

10

20

10,

20

Но неправильно

10 ,

20.

Итак, пользователю нужно явно указывать, как вводить исходные данные. А еще лучше вводить каждую переменную по отдельности.

Отдельно рассмотрим вопрос о вводе строки. Для этого служит спецификатор %s. Рассмотрим пример.

#include <stdio.h> int main() {

int str[20];

puts("Enter string"); scanf("%s", str); printf("str = %s\n", str); return 0;

}

Enter string abc def

str = abc

Функция scanf восприняла пробел как разделитель и проигнорировала оставшуюся часть строки.

Поэтому строки лучше вводить с помощью функции gets(). Замените в вышеприведенной программе строку scanf("%s", str); на gets(str);

Но функция gets в свою очередь обладает опасной особенностью: если введено больше символов, чем размер массива, зарезервированного для хранения строки, то «лишние» символы записываются в непредусмотренную для них область памяти. Рассмотрим пример.

#include <stdio.h>

char str[] = "12345", a[] = "aaaaa"; int main() {

puts("Enter string"); gets(str);

printf("str = %s\n", str); printf("a = %s\n", a); return 0;

}

Вот как работает эта программа:

Enter string abcdefgh

str = abcdefgh a = gh

Когда мы изучим работу с файлами, мы узнаем более безопасную функцию fgets, в которой в качестве параметра указывается максимальное количество символов в вводимой строке.

Пример. Определить в программе две переменные типа int, ввести с клавиатуры их значения и вывести (напечатать) их сумму.

#include <stdio.h> int main () {

int z, x, sum; printf("z = "); scanf("%d", &z); printf("x="); scanf("%d", &x); sum = z + x;

printf("sum = %d", sum); return 0;

}

Результат выполнения программы:

z = 46 <ENTER> x = -16 <ENTER> sum = 30

Эксперимент. Что будет, если программист использовал не адрес переменной, а ее имя в качестве аргумента функции scanf()?

В следующей программе сделана эта ошибка:

/* функция scanf() - ошибка в аргументе */

#include <stdio.h> int main ()

{

int z, x, sum; printf("z = ");

scanf("%d",z); // !!!!!!!!!

printf("x = "); scanf("%d",x); // !!!!!!!!!

sum = z + x; printf("sum = %d",sum); return 0;

}

Корректная трансляция!!!

Результат выполнения программы:

z = 3 <ENTER> x = 4 <ENTER> sum = 3630652

Соседние файлы в папке Assembler