Вы хотите выделить место для длины и строки в одном и том же блоке памяти. Это может быть тем, что вы хотели использовать в своей структуре, но вы зарезервировали место только для указателя на строку.
Должно быть выделено место для символов строки.
Например:
typedef struct
{
int num_chars;
char string[];
} my_string_t;
my_string_t * alloc_my_string(char *src)
{
my_string_t * p = NULL;
int N_chars = strlen(src) + 1;
p = malloc( N_chars + sizeof(my_string_t));
if (p)
{
p->num_chars = N_chars;
strcpy(p->string, src);
}
return p;
}
В моем примере, чтобы получить доступ к указателю на вашу строку, вы обращаетесь к string
члену my_string_t
:
my_string_t * p = alloc_my_string("hello free store.");
printf("String of %d bytes is '%s'\n", p->num_chars, p->string);
Будьте осторожны, чтобы понять, что вы получаете указатель на строку в результате выделения места для хранения символов. Ресурс, который вы выделяете, является хранилищем символов, полученный указатель является ссылкой на выделенное хранилище.
В моем примере выделенная память распределяется последовательно следующим образом:
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| 00 | 00 | 00 | 11 | 'h'| 'e'| 'l'| 'l'| 'o'| 20 | 'f'| 'r'| 'e'| 'e'| 20 | 's'| 't'| 'o'| 'r'| 'e'| '.'| 00 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
^^ ^
|| |
p| |
p->num_chars p->string
Обратите внимание, что значение p->string
не сохраняется в выделенной памяти, оно находится в четырех байтах от начала выделенной памяти, непосредственно после (предполагаемого 32-разрядного, четырехбайтового) целого числа.
Ваш компилятор может потребовать, чтобы вы объявили гибкий C-массив следующим образом:
typedef struct
{
int num_chars;
char string[0];
} my_string_t;
но версия без нуля предположительно совместима с C99.
Вы можете выполнить эквивалентную вещь без элемента массива следующим образом:
typedef struct
{
int num_chars;
} mystr2;
char * str_of_mystr2(mystr2 * ms)
{
return (char *)(ms + 1);
}
mystr2 * alloc_mystr2(char *src)
{
mystr2* p = NULL;
size_t N_chars = strlen(src) + 1;
if (N_chars num_chars = (int)N_chars;
strcpy(str_of_mystr2(p), src);
}
return p;
}
printf("String of %d bytes is '%s'\n", p->num_chars, str_of_mystr2 (p));
Во втором примере значение, эквивалентное p->string
, рассчитывается по str_of_mystr2()
. Он будет иметь примерно то же значение, что и в первом примере, в зависимости от того, как конец структур будет упакован вашими настройками компилятора.
Хотя некоторые предлагали бы отслеживать длину в size_t
, я бы посмотрел статью старого доктора Добба о том, почему я не согласен. Поддержка значений, превышающих INT_MAX, сомнительна для правильности вашей программы. Используя int, вы можете написать assert(p->num_chars >= 0);
и что-то проверить. В случае без знака вы бы написали эквивалентный тест, например, assert(p->num_chars < UINT_MAX / 2);
Пока вы пишете код, который содержит проверки данных во время выполнения, может быть полезным использование подписанного типа.
С другой стороны, если вы пишете библиотеку, которая обрабатывает строки, превышающие символы UINT_MAX / 2, я приветствую вас.