Да, вы создали структуру в куче. Вы не заполнили его правильно, и у вас возникнут проблемы с его удалением - я не уверен, охватывало ли это домашнее задание или нет. В нынешнем виде у вас больше шансов получить повреждение памяти или, если вам повезет, утечку памяти, чем освобождение одной из этих строк.
Код, который работает со стандартными C89 и C99
Ваш код, несколько исправлен...
typedef
struct String {
int length;
int capacity;
char *ptr;
} String;
char* modelstrdup(char* src){
int length = strlen(src);
char *space = malloc(sizeof(String) + length + 1);
//String *string = space; // Original code - compilers are not keen on it
String *string = (String *)space;
assert(space != 0);
string->ptr = space + sizeof(String); // or sizeof(*string)
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
Этот код будет работать как в C89, так и в C99 (за исключением комментариев C99/C++). Вероятно, вы можете оптимизировать его для работы с «структурным взломом» (сохраняет указатель в структуре, но только если у вас есть компилятор C99). Утверждение является неоптимальной обработкой ошибок. Код не защищает себя от нулевого указателя для ввода. В этом контексте ни длина, ни емкость не дают никакой пользы — в наборе должны быть другие функции, которые смогут использовать эту информацию.
Как уже упоминалось, вы столкнетесь с проблемами при удалении строковой структуры, когда возвращаемое значение не является указателем на строку. Вам нужно внести некоторые тонкие корректировки указателя.
Код, который работает только со стандартным C99
В C99, раздел 6.7.2.1, параграф 16, описывает «гибкие элементы массива»:
В особом случае последний элемент структуры с более чем одним именованным членом может иметь тип неполного массива; это называется гибким членом массива. За двумя исключениями гибкий элемент массива игнорируется. Во-первых, размер структуры должен быть равен смещению последнего элемента идентичной структуры, которая заменяет гибкий элемент массива массивом неопределенной длины.106) Во-вторых, когда a . (или ->) имеет левый операнд, который является (указателем) структурой с гибким элементом массива, а правый операнд называет этот элемент, он ведет себя так, как если бы этот элемент был заменен самым длинным массивом (с тем же типом элемента ) это не сделало бы структуру больше, чем объект, к которому осуществляется доступ; смещение массива должно оставаться смещением гибкого элемента массива, даже если оно будет отличаться от смещения замещающего массива. Если этот массив не имеет элементов, он ведет себя так, как если бы он имел один элемент, но поведение не определено, если предпринимается какая-либо попытка получить доступ к этому элементу или сгенерировать указатель после него.
106 Длина не указана, чтобы учесть тот факт, что реализации могут задавать членам массива различные выравнивания в зависимости от их длины.
Используя «гибкий элемент массива», ваш код может стать:
typedef
struct String {
int length;
int capacity;
char ptr[];
} String;
char* modelstrdup(char* src){
int length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
Этот код был принят GCC 4.0.1 как чистый, за исключением объявления функции (опции -Wall -Wextra
). Предыдущий код требует приведения к 'String *string = (String *)space;' чтобы сообщить компилятору, что я имел в виду то, что сказал; Я исправил это и оставил комментарий, чтобы показать оригинал.
Использование «взлома структуры»
До C99 люди часто использовали «структурный хак», чтобы справиться с этим. Это очень похоже на код, показанный в вопросе, за исключением того, что размер массива равен 1, а не 0. Стандарт C не допускает нулевого размера массива.
typedef struct String {
size_t length;
size_t capacity;
char ptr[1];
} String;
char* modelstrdup(char* src)
{
size_t length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
Код, использующий нестандартное расширение GCC для C89 и C99.
Обозначение массива нулевого размера принимается GCC, если только вы не нажмете на него сильно - укажите стандарт ISO C и запросите педантическую точность. Таким образом, этот код компилируется нормально, если вы не используете gcc -Wall -Wextra -std=c99 -pedantic
:
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef
struct String {
int length;
int capacity;
char ptr[0];
} String;
char* modelstrdup(char* src){
int length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
Однако вам не следует обучаться нестандартным расширениям языка C до тех пор, пока вы полностью не освоите основы стандартного C. Это просто несправедливо по отношению к вам; вы не можете сказать, разумно ли то, что вам говорят, но ваши наставники не должны вводить вас в заблуждение, заставляя использовать нестандартные вещи. Даже если вас предупредили о том, что это нестандартно, это нечестно по отношению к вам. C достаточно сложно изучить, не изучая хитрые вещи, которые в некоторой степени специфичны для компилятора.
person
Jonathan Leffler
schedule
26.10.2009