Какая из этих опций является хорошей практикой для присвоения строкового значения переменной в C? - PullRequest
5 голосов
/ 29 сентября 2010

У меня есть этот фрагмент кода C:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct Date {
    int date;
    char* month;
    int year;

} Date_t;

typedef Date_t* pDate_t;

void assignMonth(pDate_t birth)
{
    //1)
    birth->month = "Nov";

    //2)
    //birth->month = malloc(sizeof(char) * 4);
    //birth->month = strcpy(birth->month, "Nov");

}

int main()
{
    Date_t birth;
    birth.date = 13;
    assignMonth(&birth);
    birth.year = 1969;


    printf("%d %s %d\n",birth.date, birth.month, birth.year);
    return 0;
}

В функции assignMonth У меня есть две возможности для назначения месяца.Оба дают мне одинаковый результат на выходе, так в чем же разница между ними?Я думаю, что второй вариант хороший, я не прав?Если да, то почему?Если нет, то почему?

Заранее спасибо за любую помощь.

PS Мне интересно, что происходит в памяти в обоих случаях.

Ответы [ 8 ]

9 голосов
/ 29 сентября 2010

Это зависит от того, что вы хотите сделать с birth.month позже.Если вы не собираетесь менять его, то первый лучше (быстрее, не требуется очистки памяти, и каждый объект Date_t использует одни и те же данные).Но если это так, я бы изменил определение month на const char *.Фактически, любая попытка записи в *birth.month вызовет неопределенное поведение.

Второй подход приведет к утечке памяти, если вы не забудете набрать free(birth.month) до того, как birth выйдет из области видимости.

6 голосов
/ 29 сентября 2010

Вы правы, второй вариант - "хороший".

Вот разница:

С 1 birth->month заканчивается указанием на строковый литерал "Nov". В этом случае попытка изменить содержимое birth->month является ошибкой, и поэтому birth->month действительно должно быть const char* (по этой причине многие современные компиляторы будут предупреждать о назначении).

При значении 2 birth->month заканчивается указанием на выделенный блок памяти, содержимое которого равно «Nov». После этого вы можете изменять содержимое birth->month, а тип char* является точным. Предостережение заключается в том, что теперь вам также необходимо free(birth->month), чтобы освободить эту память, когда вы закончите с ней.

Причина того, что 2 является правильным способом сделать это в общем, хотя 1 кажется более простым в этом случае, состоит в том, что 1 в целом вводит в заблуждение. В C нет строкового типа (только последовательности символов), и поэтому для строк не определена операция присваивания. Для двух char* s, s1 и s2, s1 = s2 не изменяет значение строки, на которое указывает s1, на значение, равное s2, оно делает s1 точку на точно та же строка , что и s2. Это означает, что любое изменение s1 повлияет на содержимое s2, и наоборот. Кроме того, теперь вы должны быть осторожны при освобождении этой строки, так как free(s1); free(s2); освободится дважды и вызовет ошибку.

Тем не менее, если в вашей программе birth->month будет когда-либо только одной из нескольких константных строк ("Jan", "Feb" и т. Д.), Вариант 1 приемлем, однако вы должны изменить тип birth->month на const char* для ясности и правильности.

3 голосов
/ 29 сентября 2010

Ни один из них не является правильным.Всем не хватает того факта, что эта структура по своей сути нарушена.Месяц должен быть целым числом от 1 до 12, используемым в качестве индекса в массиве строк static const, когда вам нужно вывести месяц как строку.

2 голосов
/ 29 сентября 2010

Я предлагаю:

const char* month;
...
birth->month = "Nov";

или:

char month[4];
...
strcpy(birth->month, "Nov");

, чтобы вообще избежать выделения памяти.

1 голос
/ 29 сентября 2010

С опцией 1 вы никогда не выделяете память для хранения «Nov», что нормально, поскольку это статическая строка. Фиксированный объем памяти был выделен для него автоматически. Это будет хорошо, если это строка, которая появляется буквально в источнике, и вы никогда не пытаетесь ее изменить. Если вы хотите прочитать значение от пользователя или из файла, то вам сначала нужно выделить его.

0 голосов
/ 29 сентября 2010

Для этого примера это не имеет большого значения.Если у вас много переменных Date_t (например, в базе данных в памяти), то первый метод приведет к меньшему использованию памяти в целом, с тем, что вы не должны ни при каких обстоятельствах изменять какие-либо символыв строках, поскольку все строки "Nov" будут "одной и той же" строкой (указатель на те же 4 символа).

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

0 голосов
/ 29 сентября 2010

Отделитесь от вашего вопроса;почему структура Date имеет определение типа?у него уже есть тип - "struct Date".

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

По моему опыту, люди, кажется, вводят def, потому что онидолжен - не задумываясь о том, как это сделать.

0 голосов
/ 29 сентября 2010

В первом случае вы не можете сделать что-то вроде birth->month[i]= 'c'.Другими словами, вы не можете изменить строковый литерал "Mov", на который указывает birth->month, потому что он хранится в разделе памяти только для чтения."Mov" находится в куче.Также вам необходимо освободить выделенную память, используя в этом случае free.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...