Да, вы создали структуру в куче. Вы не заполнили это правильно, и вы столкнетесь с проблемами, удаляя это - я не уверен, покрывала ли домашняя работа это или нет. В нынешнем виде вы, скорее всего, получите повреждение памяти или, если вам повезет, утечку памяти, чем выпустить одну из этих строк.
Код, который работает со стандартными 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;' сказать компилятору, что я имел в виду то, что сказал; Я исправил это и оставил комментарий, чтобы показать оригинал.
Использование struct hack
До 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 достаточно сложно выучить, не изучая хитрых вещей, которые в какой-то степени зависят от компилятора.