是的,您已经在堆上创建了一个结构体。你没有正确填充它,并且你将面临删除它的问题 - 我不确定家庭作业是否涵盖了这一点。就目前情况而言,与释放这些字符串之一相比,您更有可能出现内存损坏,或者如果幸运的话,出现内存泄漏。
适用于标准 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++ 注释除外)。您可能可以优化它以使用“struct hack”(在结构中保存指针 - 但前提是您有 C99 编译器)。断言是次优错误处理。该代码不会防御输入的空指针。在这种情况下,长度和容量都没有提供任何好处 - 套件中必须有其他功能能够利用该信息。
正如已经暗示的那样,当传回的值不是指向字符串的指针时,您将面临删除字符串结构的问题。您需要进行一些微妙的指针调整。
仅适用于标准 C99 的代码
在 C99 中,第 6.7.2.1 节第 16 段描述了“灵活数组成员”:
As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. With two
exceptions, the flexible array member is ignored. First, the size of the structure shall be
equal to the offset of the last element of an otherwise identical structure that replaces the
flexible array member with an array of unspecified length.106) Second, when a . (or ->)
operator has a left operand that is (a pointer to) a structure with a flexible array member
and the right operand names that member, it behaves as if that member were replaced
with the longest array (with the same element type) that would not make the structure
larger than the object being accessed; the offset of the array shall remain that of the
flexible array member, even if this would differ from that of the replacement array. If this
array would have no elements, it behaves as if it had one element but the behavior is
undefined if any attempt is made to access that element or to generate a pointer one past
it.
106 The length is unspecified to allow for the fact that implementations may give array members different
alignments according to their lengths.
使用“灵活数组成员”,您的代码可能会变成:
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;
}
除了函数声明(选项-Wall -Wextra
)。前面的代码需要对“String *string = (String *)space;”进行强制转换告诉编译器我的意思是我所说的;我现在已经修复了这个问题并留下了评论以显示原始内容。
使用“结构黑客”
在 C99 之前,人们经常使用“struct hack”来处理这个问题。它与问题中显示的代码非常相似,只是数组的维度是 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;
}
使用 C89 和 C99 的 GCC 非标准扩展的代码
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 就很难学了。