Я думаю, это поможет вам больше всего узнать, что происходит в голове компилятора. Использование вашего примера:
typedef struct {
int lengthInSeconds;
int yearRecorded;
} Song;
Здесь на самом деле происходят две вещи. Во-первых, вы определяете анонимный struct
:
struct {
int lengthInSeconds;
int yearRecorded;
}
Этот анонимный struct
может быть использован в любом месте типа (например, int
или const char*
) могут быть использованы. Следующий пример на самом деле действует, хотя никто и никогда не должен писать код, как это, потому что это обслуживание кошмар:
struct {
int lengthInSeconds;
int yearRecorded;
}* GetSongInfo(const char* songName);
Объявляет функцию, которая получает информацию для песни с заданным именем (предполагающие имена уникальны, неверное предположение в реальной жизни) и возвращает указатель на struct
с его метаданными.
я говорил, что в качестве «анонимного struct
», потому что с именем форма структуры выглядит следующим образом:
struct Song {
int lengthInSeconds;
int yearRecorded;
}
Теперь вы можете обратиться к этой структуре по имени, но не в моде вы думаете ближайшее от языков, отличных от C! В частности, функция выше теперь будет объявлена как:
struct Song* GetSongInfo(const char* songName);
Обратите внимание, что имя типа является struct Song
и не только Song
! По сути, у struct
s есть собственное собственное пространство имен ... так что ничего не существует, чтобы гарантировать, что struct Song
и Song
- это то же самое! Я понятия не имею, поддерживает ли Objective C это безумие, но я бы сказал, что он находится в безопасности.
Во-вторых, вы подаете эту анонимную структуру до typedef
. typedef
просто берет один тип и определяет имя для него, которое вы можете использовать позже. Итак, вы действительно говорите: «Song
является псевдонимом для этого struct {...}
Я только что объявил».
По typedef
ING Song
, вы можете вместо этого написать:
Song* GetSongInfo(const char* songName);
(Да, я знаю, что это было бы лучше вернуться const Song*
, но это не относится к делу.)
Это может заинтересовать вас обратите внимание, что C имеет множество typedefs в своих стандартных библиотеках, предназначенных для отделения данных платформы от деталей стандарта C. Например, <unistd.h>
определяет size_t
, целочисленный тип, который представляет собой размер (и является типом возврата встроенной функции sizeof()
), и по расширению тип, который вы должны передать таким функциям, как malloc()
. Существует также недавний заголовок <stdint.h>
, который определяет целочисленные типы по их размеру в битах, например. uint8_t
для 8-разрядного целого числа без знака. Линия в <stdint.h>
вероятно выглядит примерно так:
typedef unsigned char uint8_t;
Поскольку большинство платформ имеют 8-битные символы. (Я запрограммировал для некоторых платформ, таких как TI DSP, где char
не был 8 бит, обычно это было 16, поскольку процессор не мог получить доступ к частям слов. Пока не будем игнорировать вырожденные платформы.)
Наконец, чтобы объяснить свои примеры:
Song myvariable;
объявляет память для Song
структуры. Если это определение функции, это хранилище будет выделено в стеке и выпущено, когда функция завершится. Если он находится вне определения функции, он будет выделен как глобальный. В любом случае он будет неинициализирован, что в C означает, что он может содержать любые случайные значения. C не отслеживает неинициализированные переменные или задает их как «неинициализированные» или что-то вроде PHP и Python. C не генерирует исключений.
Song myfunction(int params) { }
Это определяет функцию, которая принимает целое число и возвращает Song
структуру.Оператор return
для функции должен иметь значение Song
. Например:
Song myfunction(int params)
{
Song result;
result.lengthInSeconds = GetSongLength(params);
result.yearRecorded = GetSongYear(params);
return result;
}
Эта функция выделяет пространство в стеке для Song
, заполняет его поля с помощью вызова функций, которые возвращают int
с, затем return
копирует структуру result
в пространство, отведенное для возвращаемого значения, и, наконец, при завершении функции освобождается память для result
. Обычно компилятор может оптимизировать копию, неявную в инструкции return
. Кроме того, там может быть другой (возможно, оптимизировано) скопировать из хранилища возвращаемого значения до конечного пункта назначения, например, когда вы говорите:
Song foundSongInfo = myfunction(params);
Кстати, возвращаясь struct
с на стек неодобрением, так как компиляторы обычно лучше оптимизировать, если возвращаемое значение вписывается в регистр (т. е. является простым типом). (Есть исключения в C++ с перегрузкой операторов, но это не по теме.) Лучшее определение было бы:
void myfunction(int params, Song* found)
{
found->lengthInSeconds = GetSongLength(params);
found->yearRecorded = GetSongYear(params);
}
Это позволяет избежать всех дополнительных копий. Вы называете это так:
Song foundSongInfo;
myfunction(params, &foundSongInfo);
Это один случай, когда две линии могут быть быстрее одного.
я, возможно, пропустили некоторые вещи, дайте мне знать, если это имеет смысл ...
я прочитал статью на википедии. но не понимаю его цели, будет ли он похож на написание классов в php? – SarmenHB
Структуры аналогичны классам, в которых они группируют несколько других переменных (в данном случае два ints) в автономный пакет данных (см. Другие ответы NSResponder и lpthnc). Однако идеологии C-структуры и PHP-класса довольно разные. –