Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
#1

Указатели на функции (Прата) - не пойму, как это работает - C++

16.07.2014, 17:42. Просмотров 1054. Ответов 28
Метки нет (Все метки)

Стивен Прата "Язык программирования C++. Лекции и упражнения"
7 глава, стр. 355, листинг 7.18. fun_ptr.cpp

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// fun_ptr.cpp -- pointers to functions
#include <iostream>
double betsy(int);
double pam(int);
 
// second argument is pointer to a type double function that
// takes a type int argument
void estimate(int lines, double (*pf)(int));
 
int main()
{
    using namespace std;
    int code;
 
    cout << "How many lines of code do you need? ";
    cin >> code;
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);
    // cin.get();
    // cin.get();
    return 0;
}
 
double betsy(int lns)
{
    return 0.05 * lns;
}
 
double pam(int lns)
{
    return 0.03 * lns + 0.0004 * lns * lns;
}
 
void estimate(int lines, double (*pf)(int))
{
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
}
Вопрос. Я как бы понял, что в прототипе функции можно написать только типы
аргументов, опустив имена; либо написать те самые имена, которые будут в
реализации функции; либо вовсе написать любые удобные имена, которые будут
служить своеобразной подсказкой - это не важно, компилятор всё равно эти имена
пропустит.
В прототипах функций betsy() и pam() указаны только типы аргументов, это я
понял. В прототипе функции estimate() указаны имена-подсказки lines и pf
соответственно. Несмотря на то, что нигде потом не будет объявлена переменная
int lines и указатель pf, компилятор понимает, что первым аргументом функции
estimate() должно быть целое число, либо целочисленная переменная (в нашем
случае - переменная int code), а вторым аргументом указатель на функцию (в нашем
случае это betsy и pam, соответственно). Понимает почему? Потому что при вызове
функции estimate() на месте int lines стоит int code, а на месте double (*pf)(int) стоит
betsy (или во втором вызове - pam).
Но я хоть убей не могу понять, каким волшебным образом компилятор понимает,
что переменная int lns - это та же самая int code. Откуда он берёт это знание?
С чего он вообще решил, что надо подставить вместо int lns переменную int code?
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
16.07.2014, 17:42     Указатели на функции (Прата) - не пойму, как это работает
Посмотрите здесь:

не пойму что это за ошибка( C++
C++ Не пойму как работает класс
Указатели и сссылки. Надо ли обнулять? когда и как это делать? C++
Динамические двумерные массивы через указатели. Как это происходит? C++
Прата С. С++. Посчитать количество вызовов функции C++
C++ Объясните пожалуйста, как работает код. Указатели
Русификация.Работает-супер! Обьяснитте, как это работает? C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
jurok_85
238 / 221 / 76
Регистрация: 21.02.2013
Сообщений: 515
Завершенные тесты: 1
16.07.2014, 17:55     Указатели на функции (Прата) - не пойму, как это работает #2
C++
1
2
3
4
estimate(code, betsy);
...
...
void estimate(int lines, double (*pf)(int))
первый аргумент функции, получается lines равно code
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
16.07.2014, 18:46  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #3
Всё, дошло.
В 18-й строке компилятору дали понять, что вместо int lines надо подставить int code.
А в реализации estimate() в 40-й строке кода становится понятно, что переменная lines - это та же lns.
Что-то типа псевдоним псевдонима. Только нафига было огород городить? Почему было не написать так:
C++
1
2
3
4
5
6
7
8
9
double betsy(int lines)
{
    return 0.05 * lines;
}
 
double pam(int lines)
{
    return 0.03 * lines + 0.0004 * lines * lines;
}
?

Добавлено через 27 минут
Цитата Сообщение от jurok_85 Посмотреть сообщение
первый аргумент функции, получается lines равно code
А из первого сообщения топика разве было не понятно, что это я и сам понял?

Вопрос же чётко был поставлен:
Цитата Сообщение от gru74ik Посмотреть сообщение
Но я хоть убей не могу понять, каким волшебным образом компилятор понимает,
что переменная int lns - это та же самая int code. Откуда он берёт это знание?
Теперь вопрос в следующем:
Цитата Сообщение от gru74ik Посмотреть сообщение
нафига было огород городить?
Зачем Прата наплодил сущностей в этом примере?

Добавлено через 19 минут
Цитата Сообщение от gru74ik Посмотреть сообщение
Зачем Прата наплодил сущностей в этом примере?
Перечитал главу ещё раз. Дошло почему. Предполагается, что estimate() писал один
программист, а pam() и betsy() другой (или другие). Поэтому и в estimate() переменную
обозвали lines, а в pam() и betsy() - lns:
Цитата Сообщение от Стивен Прата
Проясним этот процесс на примере. Предположим, что требуется спроектировать
функцию estimate (), которая оценивает затраты времени, необходимого для
написания заданного количества строк кода, и вы хотите, чтобы этой функцией
пользовались разные программисты. Часть кода estimate () будет одинакова для
всех пользователей, но эта функция позволит каждому программисту применить
собственный алгоритм оценки затрат времени.
DrOffset
6851 / 4062 / 927
Регистрация: 30.01.2014
Сообщений: 6,859
16.07.2014, 19:11     Указатели на функции (Прата) - не пойму, как это работает #4
Цитата Сообщение от gru74ik Посмотреть сообщение
Но я хоть убей не могу понять, каким волшебным образом компилятор понимает,
что переменная int lns - это та же самая int code
Это не та же самая переменная.
Компилятор ничего не "подставляет". Он генерирует машинный код, который, условно говоря, копирует содержимое ячейки памяти обозначенной как code в ячейку памяти обозначенную как lines. Естественно в машинном коде никаких имен нет, есть только адреса. И это мы еще не касаемся такой темы как оптимизация.

Указатели на функцию здесь исключительно в качестве синтетического примера. Искать глубинный смысл этого кода довольно затруднительно, учитывая, что смысл только в демонстрации (механизма).
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
17.07.2014, 13:21  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #5
Цитата Сообщение от DrOffset Посмотреть сообщение
Компилятор ничего не "подставляет". Он генерирует машинный код, который, условно говоря, копирует содержимое ячейки памяти обозначенной как code в ячейку памяти обозначенную как lines.
Ясно. Благодарю!
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 13:59  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #6
Поиздевался над кодом, чтобы немного уяснить для себя, как всё это работает:
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// fun_ptr.cpp -- pointers to functions
#include <iostream>
double betsy(int);
double pam(int);
 
// Второй аргумент — указатель на функцию double,
// которая принимает аргумент типа int
void estimate(int lines, double (*pf)(int));
 
int main()
{
    using namespace std;
    int code;
 
    cout << "How many lines of code do you need? ";
    cin >> code;
 
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);
 
    cout << "\n\tAddress of variable int code in"
    " function estimate() is\t" << &code << endl;
    cout << "\tValue of variable int code in"
    " function estimate() is\t" << code << endl;
    cout << endl;
 
    // cin.get();
    // cin.get();
    return 0;
}
 
double betsy(int lns)
{
    std::cout << "\n\tAddress of variable int lns in"
    " function betsy() is\t" << &lns << std::endl;
    std::cout << "\tValue of variable int lns in"
    " function betsy() is\t" << lns << std::endl;
    std::cout << std::endl;
 
    return 0.05 * lns;
}
 
double pam(int lns)
{
    std::cout << "\n\tAddress of variable int lns in"
    " function pam() is\t" << &lns << std::endl;
    std::cout << "\tValue of variable int lns in"
    " function pam() is\t" << lns << std::endl;
    std::cout << std::endl;
 
    return 0.03 * lns + 0.0004 * lns * lns;
}
 
void estimate(int lines, double (*pf)(int))
{
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
 
    cout << "\tAddress of variable int lines in"
    " function estimate() is\t" << &lines << endl;
    cout << "\tValue of variable int lines in"
    " function estimate() is\t" << lines << endl;
    cout << endl;
}


Вот интересно, каким образом int lns в функции betsy() и int lns в функции pam() имеют один и тот же адрес? Я так понимаю, это должны быть разные переменные с разными адресами, ведь они локальные (инкапсулированы внутри функций)?
Миниатюры
Указатели на функции (Прата) - не пойму, как это работает  
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
19.07.2014, 14:05     Указатели на функции (Прата) - не пойму, как это работает #7
Они локальные, и находятся в стеке, поэтому вполне очевидно что при вызове этих функций указатель стека будет одинаковым.
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 14:09  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #8
Цитата Сообщение от castaway Посмотреть сообщение
Они локальные, и находятся в стеке
Это понял.
Цитата Сообщение от castaway Посмотреть сообщение
очевидно что при вызове этих функций указатель стека будет одинаковым
Это не понял. Какой указатель?
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 14:15  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #9
Блин, разные функции, разные переменные. Как у них может быть один и тот же адрес?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// fun_ptr.cpp -- pointers to functions
#include <iostream>
double betsy(int);
double pam(int);
 
// Второй аргумент — указатель на функцию double,
// которая принимает аргумент типа int
void estimate(int lines, double (*pf)(int));
 
int main()
{
    using namespace std;
    int code;
 
    cout << "How many lines of code do you need? ";
    cin >> code;
 
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);
 
    cout << "\n\tAddress of variable int code in"
    " function estimate() is\t" << &code << endl;
    cout << "\tValue of variable int code in"
    " function estimate() is\t" << code << endl;
    cout << endl;
 
    // cin.get();
    // cin.get();
    return 0;
}
 
double betsy(int bet_lns)
{
    std::cout << "\n\tAddress of variable int bet_lns in"
    " function betsy() is\t" << &bet_lns << std::endl;
    std::cout << "\tValue of variable int bet_lns in"
    " function betsy() is\t" << bet_lns << std::endl;
    std::cout << std::endl;
 
    return 0.05 * bet_lns;
}
 
double pam(int pam_lns)
{
    std::cout << "\n\tAddress of variable int pam_lns in"
    " function pam() is\t" << &pam_lns << std::endl;
    std::cout << "\tValue of variable int pam_lns in"
    " function pam() is\t" << pam_lns << std::endl;
    std::cout << std::endl;
 
    return 0.03 * pam_lns + 0.0004 * pam_lns * pam_lns;
}
 
void estimate(int lines, double (*pf)(int))
{
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
 
    cout << "\tAddress of variable int lines in"
    " function estimate() is\t" << &lines << endl;
    cout << "\tValue of variable int lines in"
    " function estimate() is\t" << lines << endl;
    cout << endl;
}
Миниатюры
Указатели на функции (Прата) - не пойму, как это работает  
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
19.07.2014, 14:16     Указатели на функции (Прата) - не пойму, как это работает #10
В процессорах есть регистр SP (stack pointer), в котором хранится адрес вершины стека.
Хотя бы небольшое знание языка Assembler поможет лучше разобраться в языке C++.
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 14:19  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #11
Цитата Сообщение от castaway Посмотреть сообщение
Хотя бы небольшое знание языка Assembler поможет лучше разобраться в языке C++.
Ладно. Пока приму как данность. Сейчас ещё и в ассемблер вникать не потяну.
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
19.07.2014, 14:24     Указатели на функции (Прата) - не пойму, как это работает #12
Я бы попробовал объяснить, но мне с телефона это будет очень неудобно.
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 14:32  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #13
Цитата Сообщение от castaway Посмотреть сообщение
Я бы попробовал объяснить
Благодарю за отзывчивость!

Цитата Сообщение от castaway Посмотреть сообщение
но мне с телефона это будет очень неудобно.
Не стоит если не слишком удобно - мне не горит. Потом как-нибудь. Или сам разберусь чуть позже.
nonedark2008
853 / 592 / 116
Регистрация: 28.07.2012
Сообщений: 1,597
19.07.2014, 15:36     Указатели на функции (Прата) - не пойму, как это работает #14
Цитата Сообщение от castaway Посмотреть сообщение
В процессорах есть регистр SP (stack pointer), в котором хранится адрес вершины стека.
Когда вызывается функция, то ее аргументы помещаются в вершину стека, после отработки функции они из стека убираются. Следовательно, вполне очевидно, что при следующем вызовн функции ее аргументы будут помещены в стеке на то же место, где были предыдущие.
Tulosba
:)
Эксперт С++
4390 / 3233 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
19.07.2014, 15:41     Указатели на функции (Прата) - не пойму, как это работает #15

Не по теме:

Цитата Сообщение от castaway Посмотреть сообщение
Я бы попробовал объяснить, но мне с телефона это будет очень неудобно.
С телефона проще оставить голосовое сообщение


По теме: будет полезно ознакомиться с соглашениями о вызове.
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
19.07.2014, 16:54     Указатели на функции (Прата) - не пойму, как это работает #16
Tulosba, да, некоторые соглошения о вызовах подразумевают использование регистров как средство передачи нескольких первых параметров. Но все известные мне компиляторы по-умолчанию используют соглошение о вызове с параметрами в стеке.
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 17:27  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #17
Цитата Сообщение от castaway Посмотреть сообщение
Но все известные мне компиляторы по-умолчанию используют соглошение о вызове с параметрами в стеке.
The codeblocks-13.12mingw-setup-TDM-GCC-481.exe file includes the TDM-GCC compiler, version 4.8.1, 32 bit.
Этот тоже?
Psilon
Master of Orion
5769 / 4717 / 622
Регистрация: 10.07.2011
Сообщений: 14,195
Записей в блоге: 5
Завершенные тесты: 4
19.07.2014, 19:57     Указатели на функции (Прата) - не пойму, как это работает #18
Ребят, скажите, а есть ли разница между
C++
1
(*pf)(lines)
и
C++
1
pf(lines)
или он автоматически разыменовывает, и это просто синтаксический сахар, чтобы не писать лишних скобочек (привет, Лисп)?

Всегда писал по второму варианту, сейчас увидел первый, удивился
zhvan
Универсальный программист
41 / 33 / 4
Регистрация: 21.12.2013
Сообщений: 375
Записей в блоге: 1
19.07.2014, 20:02     Указатели на функции (Прата) - не пойму, как это работает #19
Цитата Сообщение от gru74ik Посмотреть сообщение
что переменная int lns - это та же самая int code. Откуда он берёт это знание?
С чего он вообще решил, что надо подставить вместо int lns переменную int code?
и все же разница между переменными есть, code в функции main(),
а lns в betsy и pam и доступна только в них
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
19.07.2014, 21:02     Указатели на функции (Прата) - не пойму, как это работает
Еще ссылки по теме:

Не пойму что это за ошибки C++
Как правильно возвращать указатели из функции C++
Как это работает? Я хочу спросить как работает C++ и где можно про него почитать C++
Не пойму, куда и как вставить функции и указатели C++
C++ Не пойму как работает char

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
gru74ik
Модератор
Эксперт CЭксперт С++
3924 / 1682 / 189
Регистрация: 20.02.2013
Сообщений: 4,742
Записей в блоге: 21
19.07.2014, 21:02  [ТС]     Указатели на функции (Прата) - не пойму, как это работает #20
Цитата Сообщение от Psilon Посмотреть сообщение
Всегда писал по второму варианту, сейчас увидел первый, удивился
Как-то так:
Цитата Сообщение от Стивен Прата
Язык программирования С++. Лекции и упражнения (2012, 6-е издание), глава 7, стр. 355:

История против логики
О, великий синтаксис! Как pf и (*pf) могут быть эквивалентными? Сторонники одной шко-
лы утверждают, что поскольку pf — указатель на функцию, то *pf — функция, поэтому вы
должны использовать для ее вызова (*pf) (). Сторонники другой школы придерживаются
мнения, что поскольку имя функции является указателем на эту функцию, то и любой указа-
тель на функцию должен вести себя как имя функции; отсюда вызов функции через указа-
тель следует записывать как pf (). Язык C++ придерживается компромиссной точки зрения
о том, что обе формы корректны, или, по крайней мере, допустимы, даже несмотря на то,
что они логически несовместимы. Прежде чем вы отвергнете компромисс и выберете для
себя одну форму, вспомните, что допущение несогласованных и логически несовместимых
представлений вполне присуще человеческому мышлению.
Добавлено через 4 минуты
Цитата Сообщение от zhvan Посмотреть сообщение
и все же разница между переменными есть, code в функции main(),
а lns в betsy и pam и доступна только в них
Это понятно (адреса переменных видны же на прикреплённом скриншоте).
Не понятно почему переменная в betsy() и переменная в pam() - это одна и та же переменная? Функции разные, имена переменных разные, а адрес у обеих один и тот же!
Yandex
Объявления
19.07.2014, 21:02     Указатели на функции (Прата) - не пойму, как это работает
Ответ Создать тему
Опции темы

Текущее время: 18:55. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru