1 Отредактировано Voldemar0 (28-07-2021 16:27)

Тема: Вопросик по языку C

Привет!

В широко известной в узких кругах библиотеке sofia-sip наткнулся на такую процедуру:

static
nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds)
{
  if (ds == ((nua_handle_t *)NULL)->nh_ds)
    return NULL;

  return nua_dialog_usage_get(ds, nua_session_usage, NULL);
}

Когда начал вдумываться в оператор IF понял, что я чего -то не знаю об этом мире.
Можете объяснить в каком случае он не должен приводить к runtime error ?

Я написал минимальную прогу с такой конструкцией, она закономерно падает при запуске.

#include <stdio.h>

typedef int nua_dialog_state_t;

typedef struct nd {
  nua_dialog_state_t * nh_ds;
} nua_handle_t;


void nua_dialog_usage_for_session(nua_dialog_state_t const *ds) {
  if (ds == ((nua_handle_t *)NULL)->nh_ds)
    printf("Yes\n"); else printf("No\n");

}

int main() {
  nua_dialog_state_t const *ds;

  nua_dialog_usage_for_session(ds);

}

Но может быть есть вариант, когда она не упадёт ?


Попробовал пройти отладчиком по софии:

Breakpoint 2, nua_dialog_usage_for_session (ds=0x20b0b038) at nua_session.c:321
321       if (ds == ((nua_handle_t *)NULL)->nh_ds)
Current language:  auto; currently minimal
(gdb) print ds
$1 = (const nua_dialog_state_t *) 0x20b0b038
(gdb) print (nua_handle_t *)NULL
No symbol "NULL" in current context.
(gdb) print (nua_handle_t *)0
$2 = (struct nua_handle_s *) 0x0
(gdb) print ((nua_handle_t *)NULL)->nh_ds
No symbol "NULL" in current context.
(gdb) print ((nua_handle_t *)0)->nh_ds
$3 = 0x38

Вместо указателя получается смещение поля в структуре (???).
Но сравнивают -то его с указателем ?

2 Отредактировано Voldemar0 (28-07-2021 16:41)

Re: Вопросик по языку C

Переписал свою пример чуть ближе к  оригиналу и кажется стал понимать как это работает, но вопрос о забористой траве, которую кто-то пыхнул перед тем как придумать такое, всё ещё остаётся:

#include <stdio.h>

typedef struct {
  char c;
  int i;
} nua_dialog_state_t;

typedef struct nd {
  char w;
  // nua_dialog_state_t * nh_ds;
  nua_dialog_state_t nh_ds[1];
} nua_handle_t;


void nua_dialog_usage_for_session(nua_dialog_state_t const *ds) {
  if (ds == ((nua_handle_t *)NULL)->nh_ds)
    printf("Yes\n"); else printf("No\n");

}

int main() {
  nua_dialog_state_t const *ds;

  nua_dialog_usage_for_session(ds);

}

А теперь то, что думает отладчик о таком коде:

(gdb) print ((nua_handle_t *)0)->nh_ds
$1 = 0x4
(gdb) print *((nua_handle_t *)4)
Error accessing memory address 0x4: Некорректный адрес.

3 Отредактировано Voldemar0 (28-07-2021 16:53)

Re: Вопросик по языку C

Как я понимаю это:

Что бы не представляла собой структура nua_handle_t, но указатель (nua_handle_t *)NULL всё равно равно остаётся нулём. И указывает на нулевой адрес. Но любое обращение к нулевому адресу (да и вообще большому количеству околонулевых адресов) даст по любому ошибку.

Поэтому - на первый взгляд - независимо от того, что скрывается за nh_ds, попытка обратиться к этому элементу обречена на провал, даже если это какой-то вид указателя.
((nua_handle_t *)0)->nh_ds

Но когда nh_ds оказывается массивом внутри структуры, то явного адреса этот массив не имеет и указателем без конкретного экземпляра структуры не является. Поэтому умный компилятор (а они  нынче стали очень умными), не заморачиваясь просто прибавляет к адресу структуры смещение массива и возвращает нам  - вот ваш массив, сами его и читайте дальше... Если сможете ;).

Но для чего это нужно, я всё равно пока не понимаю :((