2015-11-20 4 views
1

Я читал «Learn C The Hard Way». Здесь функция List *List_create() имеет тип возвращаемого списка, который я понимаю, но я не понимаю, как работает *List_create()? Какая функция это?Как работает эта функция C?

struct ListNode; //I also don't know what this does 

typedef struct ListNode{ 
     struct ListNode * next; 
     struct ListNode * prev; 
     void *value; 
}ListNode; 

typedef struct List{ 
     int count; 
     ListNode *first; 
     ListNode *last; 
}List; 

List *List_create() 
{ 
    return calloc(1,sizeof(List)); 
} 
+2

вы знаете, что calloc делает – amdixon

+0

@amdixon Да. Я не спрашиваю, что делает функция, но как работает имя функции. – joker

+4

Осторожно с вашими типами возврата. Несмотря на размещение *, похоже, что оно прикреплено к 'List_create()', что на самом деле говорит о том, что эта функция возвращает указатель * в список, а не список. – Samidamaru

ответ

0

Функция создает экземпляр объекта типа List в динамической памяти инициализирует все элементы объекта нулей и возвращает указатель на объект.

На самом деле нет необходимости создавать список в динамической памяти. Можно также написать

List list = { 0 }; 

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

Во втором случае это компилятор, который захочет удалить объект списка.

Однако в обоих случаях вы должны сами удалить узлы списка. Поэтому вам нужно написать функцию, которая освободит всю выделенную память для выделенных узлов в списке.

+0

Спасибо, что указали. почему эта функция не работает без указателя в функции? – joker

+0

Указатели @joker принципиально отличаются от объектов, на которые они указывают. Они являются неотъемлемой частью системы типов в C. Вы не можете ожидать, что функции будут работать, если вы, например. удалите все if-утверждения. 'calloc' возвращает указатель на память. Вы не можете просто заставить его вернуть что-то еще? –

+0

@joker Вы ошибаетесь, функция на самом деле имеет указатель - 'calloc' возвращает указатель, и этот указатель в свою очередь' return'-ed by 'List_create()'. Конечно, можно создать промежуточную переменную для временного хранения указателя - что-то вроде «List * ptr = calloc (1, sizeof (List)); return ptr; ', но здесь это не обязательно, вы можете вернуть значение напрямую. – CiaPan

0

List_create выделяет память для структуры списка для дважды связанного списка и инициализирует эту структуру. Структура должна иметь нулевые указатели для первого и последнего и 0 для подсчета. Эта функция злоупотребляет тем фактом, что нулевой указатель является значением 0 на большинстве платформ. И calloc просто происходит, чтобы установить память, которую он выделяет.

+1

Это C; нет «классов». – ams

+0

Уже исправлено;) – wich

0

Хм, в этом коде есть как минимум одна основная ошибка, которая заставляет меня задаться вопросом, является ли это хорошим источником обучения.

Обратный вид: List*, а не List. Это указатель на тип List, что означает, что он будет жить до тех пор, пока вы его не освободите (вызывая free). calloc выделяет такую ​​память и устанавливает ее ко всем нулям, что является обычным способом инициализации структур.

Ошибка в коде в деклараторе функции, List_create() не подходит для современных C, это должно быть List_create(void). Может быть, это просто опечатка, или, может быть, автор больше привык к C++, где все будет хорошо, но тем не менее это ошибка.

Причина * ставится рядом с именем функции, а не рядом с List, что при объявлении переменной указателя, * прикрепляется к имени, а не к типу.

E.g. List *foo, bar; объявляет две переменные. foo является указателем на List, а bar - не указатель. Это было бы не так ясно, если бы было написано List* foo, bar; (смысл то же самое, но это путает кого-то, читающего код).

+1

Я не согласен ни с одной вашей критикой. '(Void)' -part был устаревшим с C99 согласно wikipedia: https://en.wikipedia.org/wiki/Void_type#cite_note-3. Очень часто выделяются объекты в куче в C, то есть через указатели. –

+0

У вас есть источник утверждения, что List_create() не является * правильным * C. Я никогда не сталкивался с этим. – Samidamaru

+0

Хорошо, я должен был сказать, возможно, современный C. Ссылка @MortenJensen дала источник. В нем говорится, что использование 'f()' устаревает. В частности, в разделе 6.11.6 C99 говорится: «Использование деклараторов функций с пустыми круглыми скобками (не деклараторы типа параметра прототипа) является устаревшей особенностью. –

0

В C есть два способа работы с: структурами

  1. По значение, в котором нет никаких указателей. В этом случае передача структуры в функцию или возврат ее из функции требует, чтобы вся структура была скопирована, байт для байта. Это подходит для небольших структур, но неэффективно для больших.

  2. По ссылке, в котором используется malloc или calloc к «динамически» выделить блок памяти, который будет содержать данные структуры, а затем передать указатель вокруг.

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

Если он выбрал модель по значению, то вам всегда нужно убедиться, что у вас были самые последние обновления в локальной копии структуры List. То есть List_add должен будет вернуть измененный список, что было бы неудобно и подвержено ошибкам.

Пример: по ссылке:

List *List_create() 
{ 
    return calloc(1,sizeof(List)); 
} 

void List_add(List* list, void *value) { 
    /* TODO: actual insert code */ 
} 

void example_caller() { 
    List *list = List_create(); 
    List_add(list, whatever); 
} 

Пример: значением:

List List_create() 
{ 
    List result = {0}; 
    return result; 
} 

List List_add(List list, void *value) { 
    /* TODO: actual insert code */ 
    return list; 
} 

void example_caller() { 
    List list = List_create(); 
    list = List_add(list, whatever); 
} 
Смежные вопросы