Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 05.05.2015, 11:32   #1
Programmer0
Пользователь
 
Регистрация: 05.05.2015
Сообщений: 55
По умолчанию Двойной указатель

Здравстуйте! Помогите разобраться в следующем.

В программе есть 4 файла(лишний код вырезан):

DAC.h :
Код:
typedef struct
{
    int (*Cmd)      (dac_cmd_t dac_cmd);
    int (*SetRate)  (uint32_t srate);
    int (*SendData) (uint32_t * buff, uint32_t len);
} dac_interface;
DAC.c :
Код:
static const dac_interface iDAC =
{
    DAC_myCmd,
    DAC_SetRate,
    DAC_SendData
};

static int DAC_myCmd(dac_cmd_t dac_cmd)
{
}

static int DAC_SetRate(uint32_t srate)
{
}

static int DAC_SendData(uint32_t * buff, uint32_t len)
{
}

int DAC_ Init(dac_interface ** DAC)
{
    *DAC = (dac_interface *)&iDAC;
}
Player.c :
Код:
static dac_interface * pDAC;

void Player_Init(dac_interface * DAC)
{
    pDAC = DAC;
    
    pDAC->Cmd(DAC_STOP);
    pDAC->SetRate(48000);
}
Main.c
Код:
dac_interface * DAC;

int main(void)
{
    DAC_Init(&DAC);
    Player_Init(DAC);
}
Почему и зачем функция DAC_Init принимает двойной указатель?

Как я понимаю, в файле DAC.c создается структура с указателями на функции для управления ЦАПом. И чтобы пользоваться этой структурой в файле Player.c, делается "связывание" через Main.c. Но почему не:
Код:
int DAC_ Init(dac_interface * DAC)
{
    DAC = &iDAC;
}
и в Main.c
Код:
dac_interface * DAC;

int main(void)
{
    DAC_Init(DAC);
    Player_Init(DAC);
}
Programmer0 вне форума Ответить с цитированием
Старый 05.05.2015, 13:27   #2
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,291
По умолчанию

DAC объявлен как указатель в main. Все значения в си передаются по значению. Для того, чтобы иметь возможность изменить значение, нужно передать указатель на изменяемую переменную, поэтому функция принимает двойной указатель.
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 05.05.2015, 16:24   #3
Programmer0
Пользователь
 
Регистрация: 05.05.2015
Сообщений: 55
По умолчанию

Цитата:
Сообщение от BDA Посмотреть сообщение
DAC объявлен как указатель в main. Все значения в си передаются по значению. Для того, чтобы иметь возможность изменить значение, нужно передать указатель на изменяемую переменную, поэтому функция принимает двойной указатель.
Извините, не могу понять. Туплю. Объясните, пожалуйста, как-нибудь попонятнее.

Вот, так сказать, физически указатель DAC в main.c - это ячейка памяти, допустим, с адресом 0х0AAA. Она может содержать адрес переменной типа dac_interface. Но сначала содержит NULL.
В строке DAC_Init(&DAC) передается в функцию адрес 0x0AAA.
Теперь переходим в модуль DAC.c. В нем уже создана(на этапе компиляции) структура iDAC типа dac_interface, допустим, по адресу 0x0BBB.

И вот дальше я не понимаю. Мы попадаем в функцию
Код:
int DAC_ Init(dac_interface ** DAC)
{
    *DAC = (dac_interface *)&iDAC;
}
Куда в этой функции записывается адрес 0x0AAA? В DAC(в этот DAC, который местный, а не который в main.c)? Тогда *DAC == *(0x0AAA) == NULL, а после выполнения строчки *DAC = (dac_interface *)&iDAC будет *DAC == *(0x0AAA) == 0x0BBB? Зачем тогда вторая звездочка?
ИЛИ
Создается локальная переменная(двойной указатель) **DAC. Она, допустим, размещается по адресу 0x0CCC. Далее строкой *DAC = (dac_interface *)&iDAC в ячейку 0x0CCC пишется значение 0x0BBB. А потом... потом... не знаю... по-ходу, у меня получается суп с котом...

Разъясните, пожалуйста. Запутался что-то совсем.
Programmer0 вне форума Ответить с цитированием
Старый 05.05.2015, 17:11   #4
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,330
По умолчанию

Если будет так:

Код:
typedef dac_interface* dac_interface_ptr;
int DAC_ Init(dac_interface_ptr * DAC)
Так будет понятней?

В чем разница между:
Код:
int DAC_ Init(dac_interface_ptr * DAC)
и
Код:
int DAC_ Init(int * DAC)
waleri вне форума Ответить с цитированием
Старый 05.05.2015, 17:17   #5
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,709
По умолчанию

Цитата:
Куда в этой функции записывается адрес 0x0AAA?
Ну скорее всего пойдет в регистре после оптимизатора, но не про это речь, наверное...

Цитата:
В DAC(в этот DAC, который местный, а не который в main.c)?
Угу не путайте переменные и формальные параметры функций.

Цитата:
Тогда *DAC == *(0x0AAA) == NULL, а после выполнения строчки *DAC = (dac_interface *)&iDAC будет *DAC == *(0x0AAA) == 0x0BBB?
Угу.

Цитата:
Зачем тогда вторая звездочка?
Вам же написали: указатель копируется, как параметр, в функцию и если не использовать указатель, по которому мы можем "дотянуться до изначального" NULL и изменить его, то на выходе из функции будет все тоже
Цитата:
*DAC == *(0x0AAA) == NULL
Цитата:
Создается локальная переменная(двойной указатель) **DAC. Она, допустим, размещается по адресу 0x0CCC. Далее строкой *DAC = (dac_interface *)&iDAC в ячейку 0x0CCC пишется значение 0x0BBB.
Формально и тут вы правы, но в зависимости от соглашения вызова и оптимизации будет или "ячейка 0x0CCC", или регистр процессора.

UPDATE:
Давайте так:
1. у вас есть DAC - указатель, который лежит в ячейке 0xAAAA, т.е. формально *DAC == *(0xAAAA) == NULL
2. когда вы пишите
Код:
int DAC_ Init(dac_interface * DAC)
то значение указателя копируется, т.е. в ячеку 0хСССС пишется NULL и с эти вы внутри функции работаете
после выхода из функции значение в 0хАААА, т.е. DAC не изменится
3. когда вы пишите
Код:
int DAC_ Init(dac_interface ** DAC)
то копируется значение указателя на DAC, т.е. в ячейку 0хСССС пишется 0хАААА и вы внутри функции уже стучитесь и работаете с тем, что в 0хАААА
после выхода из функции значение в 0хАААА, т.е. DAC изменится

Последний раз редактировалось p51x; 05.05.2015 в 17:47.
p51x вне форума Ответить с цитированием
Старый 06.05.2015, 15:11   #6
Programmer0
Пользователь
 
Регистрация: 05.05.2015
Сообщений: 55
По умолчанию

Извините, что поздно отвечаю. Пока только на работе получается разбираться.
Цитата:
Сообщение от waleri Посмотреть сообщение
В чем разница между:
Код:
int DAC_ Init(dac_interface_ptr * DAC)
и
Код:
int DAC_ Init(int * DAC)
В первом случае функция ожидет указатель на "тип-указатель, указывающий на переменную типа dac_interface"?
Во втором - ожидается указатель на переменную типа int?

Как я понимаю, указатели - это отдельный тип. И в первом, и во втором случе при попадании в функцию создается локальная переменная DAC. И в обоих случаях под эту переменную выделится одинаковый объем памяти (4 байта?). Только во втором случае локальная переменная DAC может хранить адрес переменной только типа int(и никакого другого). А в первом - адрес переменной типа указатель (которая в свою очередь хранит адрес переменной только типа dac_interface). Правильно?
Programmer0 вне форума Ответить с цитированием
Старый 06.05.2015, 15:39   #7
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,709
По умолчанию

Цитата:
4 байта?
А это зависит от битности платформы.

Цитата:
Только во втором случае локальная переменная DAC может хранить адрес переменной только типа int(и никакого другого).
Хорошо, что вы до приведения типов еще не дочитали...
p51x вне форума Ответить с цитированием
Старый 06.05.2015, 15:44   #8
Programmer0
Пользователь
 
Регистрация: 05.05.2015
Сообщений: 55
По умолчанию

Также прошу прощения за поздний ответ.
Цитата:
Сообщение от p51x Посмотреть сообщение
3. когда вы пишите
Код:
int DAC_ Init(dac_interface ** DAC)
то копируется значение указателя на DAC, т.е. в ячейку 0хСССС пишется 0хАААА и вы внутри функции уже стучитесь и работаете с тем, что в 0хАААА
после выхода из функции значение в 0хАААА, т.е. DAC изменится
Вроде понял. Я еще не мог понять, почему функция
Код:
int DAC_ Init(dac_interface ** DAC)
ожидает двойной указатель, а в нее передается, как я думал, одинарный:
Код:
dac_interface * DAC;
А вот сейчас правильно ли я думаю, что: функция, например,
Код:
void func(int *ptr)
ожидает адрес переменной типа int. И при вызове, например,
Код:
int a=10; //Допустим, &a==0xAAAA
func(&a);
в функции создастся локальная переменная ptr типа указатель, ему присвоится адрес переменной a, который передали в функцию(0xAAAA). И через средства работы с типом указатель можно менять значение переменной a.
А функция
Код:
void func2(int **ptr2)
ожидает адрес указателя. Т.е. *(*ptr)==адрес(*ptr)==адрес указателя на переменную типа int.
Извините за сумбур, если что.

Это я к тому, что пишут: если функция ожидает int - даем ей int, если char - даем char. Соответственно, если указатель -даем указатель. Если двойной указатель - даем двойной. А она, оказывается, при наличии указателя в параметрах ждет адрес... Впрочем, адреса - это и есть указатели... Наверное...

Последний раз редактировалось Programmer0; 06.05.2015 в 16:31.
Programmer0 вне форума Ответить с цитированием
Старый 06.05.2015, 19:27   #9
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,709
По умолчанию

Цитата:
А она, оказывается, при наличии указателя в параметрах ждет адрес... Впрочем, адреса - это и есть указатели... Наверное...
Указатель - это просто тип, который хранит адрес... Как и int - это тип, который, условно, хранит 4 байта, ну назвали их просто int. Вот и небольшая каша у вас.
p51x вне форума Ответить с цитированием
Старый 07.05.2015, 08:05   #10
Programmer0
Пользователь
 
Регистрация: 05.05.2015
Сообщений: 55
По умолчанию

Ну вроде понимается. Еще попрактикуюсь - въеду. Всем большое спасибо за помошь!
Programmer0 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
двойной указатель! Ann3ooo Помощь студентам 15 28.01.2013 00:13
Передать указатель на указатель в функцию erro Общие вопросы C/C++ 3 29.10.2012 17:59
двойной ping stupid C# (си шарп) 6 29.03.2011 11:08
Двойной инкримент Veiron SQL, базы данных 5 04.06.2010 13:11
Двойной запуск SONce Общие вопросы Delphi 4 15.04.2009 20:50