Assembler / P13
.pdf
#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
