2010-10-12 3 views
0

Я столкнулся с этой проблемой при разработке в Objective-C для iOS, но это должно применяться к любому C/C++/Objective-C-коду с использованием компоновщика Mac OS X/iOS. Решение покрыто another question, но меня интересует , почему.Проверка наличия внешних идентификаторов в C

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

extern char * const BrandNewIdentifier; 

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

Теперь, если есть функция, которая определена только в самой последней версии библиотеки, я могу это сделать:

if (BrandNewFunc) { 
    BrandNewFunc("foobar", 42); 
} else { 
    printf("Your system does not support some thing."); 
} 

Вместо того, содержащий адрес кода функции, BrandNewFunc вычисляется в NULL. Я бы подумал, что константа будет вести себя одинаково, но если я попробую тот же шаблон, приложение умирает во время выполнения проверки (выдает EXC_BAD_ACCESS на iOS). В частности:

if (BrandNewIdentifier) ... // blows up here 

Что работает, а не проверяет адрес идентификатора:

if (&BrandNewIdentifier) { 
    printf("You got it!"); 
} 

Я могу видеть логику: BrandNewIdentifier не имеет никакого значения, поэтому доступ к ней должен потерпеть неудачу. Но тогда почему этот синтаксис работает в случае с BrandNewFunc? Не следует ли мне также проверять его адрес? Или это на самом деле непротиворечиво, и я кое-что упустил из виду?

+0

Я не думаю, что это относится к C вообще, поскольку оно будет зависеть от реализации общей библиотеки. Для статически связанных библиотек у вас просто есть ошибки компоновщика. – jamesdlin

+0

Справедливо, я отредактировал вопрос. – benzado

ответ

2

Соответствующая часть стандарта C - это раздел 6.3.2.1 «Значения Lвалюс, массивы и функции». Вот что он говорит о функциях:

функция целеуказатель это выражение, которое имеет тип функции. Кроме случаев, когда это операнд sizeof оператора или одинарный & оператора, функция целеуказатель с типом «» функцию, возвращающую типа «» преобразуется в выражение, которое имеет тип «» указатель на функцию, возвращающую тип ''.

[сноска 65] Поскольку это преобразование не происходит, операнд оператора sizeof остается функция целеуказатель и нарушает ограничение в 6.5.3.4 [изд: ограничение в 6.5.3.4 говорит, что вы не можете обратиться к sizeof обозначение функции - это семантическая ошибка].

Идентификатор, который называет функцию, является простейшим видом выражения, имеющим тип функции. Так что это означает, что если foo был объявлен как функция, идентификатор foo оценивает как указатель на эту функцию, кроме, когда это операнд & (в этом случае выражение большего размера &foo оценивает как указатель на эту функцию) или операндом sizeof (в этом случае большее выражение, sizeof(foo), вызывает ошибку компиляции).

tl, dr: Когда foo является функцией, foo и &foo являются эквивалентными по определению. Это специальное правило для функций. Это не совсем отличается от специального правила для массивов, которые также «распадаются» на указатели во многих контекстах (это правило - один абзац выше того, который я цитировал).

Помимо этого: Да, это означает, что оператор функционального вызова всегда работает с указателем на функцию. Когда pfunc является переменной указателя на функцию, (*pfunc)() обрабатывается, как если бы он читал (&(*pfunc))() ... или только pfunc().

+0

Значит, вы говорите: «BrandNewFunc» - это то же самое, что и «& BrandNewFunc»? И как такие массивы? «BrandNewIdentifier» - это адрес первого байта; '& BrandNewIdentifier' - адрес адреса первого байта. – benzado

+0

Все, что я имею в виду, массивы «разлагаются» на указатели, а также выполняют функции; вы не можете передать ни один из них по значению. Верно, что тип «BrandNewIdentifier» не совпадает с типом «& BrandNewIdentifier», если «BrandNewIdentifier» является массивом; это не так для функций, типы «BrandNewFunc» и «& BrandNewFunc» одинаковы, если они используются в контексте, который ожидает от указателя к функции. – zwol

+0

Можете ли вы изменить свой вопрос, чтобы объяснить, что вы подразумеваете под «контекстом»? Насколько я знаю, выражения в C не меняют смысл на основе контекста, это для сумасшедших языков, таких как Perl. – benzado