2017-02-15 22 views
-1

В настоящее время я пишу новую структуру String в C, как показано ниже, и практикую арифметику указателей.C указатель арифметики со структурой

struct String { 
    uint32_t check; 
    uint32_t capacity; 
    uint32_t length; 
    char data[1]; 

} String; 

, и я был немного смущен этой части кода

define STRING(s) ((String*)(s - 3*sizeof(uint32_t))) 

void utstrfree(char* self) { 

// if(*(self - sizeof(uint32_t) * 3) == SIGNATURE) WHY DOES THIS NOT >WORK? 

    assert((STRING(self)->check) == SIGNATURE);  //check if it's a >utstring 

    free(self-sizeof(uint32_t) * 3); //properly free the malloc and alternative could have been free(STRING(self)) 

} 

Я понимаю, как мой STRING макрос работает и работает правильно, но я не совсем уверен, почему мое заявление, если не Работа. Кто-нибудь может мне это объяснить?

+0

Отключить тему: это волшебное изменение размера 'struct' trick kosher в современном стандарте C? – user4581301

+1

@ user4581301 Современный C должен использовать гибкий элемент массива - 'char data [];', а не 'char data [1];', в противном случае это законно (манипуляция строками размера через char * с гибким элементом массива немного рыбный, хотя - не очень безопасный тип). – PSkocik

+2

Нет, @ user4581301, и он никогда не был кошерным по любому предыдущему стандарту, хотя на самом деле он обычно * работает *. В наши дни стандарт имеет прямую поддержку такого рода вещей через [гибкие элементы массива] (http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p18). –

ответ

2

Как уже отмечалось @AslakBerby выражения в своем утверждении, что я предполагаю, делает работы, не эквивалентно выражению условия в вашем закомментированный if заявления. В частности, поскольку self является char *, выражение *(self - sizeof(uint32_t) * 3) имеет тип char, тогда как выражение STRING(self)->check имеет тип uint32_t. Предполагая, что оценка последнего вообще определяет поведение, первый дает только первый байт последнего в системном порядке хранения.

В целом, однако, в той степени, что эта общая схема работает на всех, она является хрупким и небезопасным:

  • Это делает предположения о представлении типа struct String, что реализации не требуется, чтобы удовлетворить;
  • Он регулярно перехватывает границы массива, тем самым вызывая неопределенное поведение;
  • Это не дает механизма для представления подстрок на месте;
  • Он обеспечивает достаточное взаимодействие со строковыми функциями стандартной библиотеки, чтобы дать пользователям ожидание большей функциональной совместимости, чем это фактически обеспечивает.

Рекомендации:

  • Положитесь на offsetof() для вычисления относительного расположения внутри структуры. Это безопаснее и понятнее.
  • Не пытайтесь напрямую взаимодействовать с равными char Строками массива - обрабатывайте эти объекты так, как они есть на самом деле. В конце концов, точкой такого упражнения обычно является не столько сама структура данных, сколько улучшенные функции, которые идут с ней, например, O (1) версия strlen(). Вам не нужно уходить с дороги, чтобы использовать эти объекты со стандартными функциями библиотеки.
  • Рассмотрите возможность использования указателя на данные вместо того, чтобы вставлять данные непосредственно в объект. Это, безусловно, сделало бы лучшую работу по адаптации размеров, и это могло бы также поддерживать подстроки на месте.
  • Если вы вставляете данные непосредственно в объект, используйте для этого гибкий элемент массива.
1

Ну, ваш, если не равен макросу. Если вы должны ввести его это должно быть что-то вроде этого:

if(((String*)(self - 3*sizeof(uint32_t)))->check) == SIGNATURE) ... 

Вы должны типаж указателя вы создаете, а затем использовать атрибут, который вы ищете.

+0

В частности, учитывая, что 'self' является' char * ', оператор' if' сравнивает * 'char' * с' SIGNATURE'. Кажется, это не то, что он намеревается. –

+0

Да. И поскольку мы не знаем, как определяется «ПОДПИСЬ», невозможно сказать, почему нет. Однако он мог это сделать: 'if (* ((unit32_t *) (self-3 * sizeof (uint32_t))) == ПОДПИСЬ)' Должен также работать. –