Как вернуть целое число из функции - PullRequest
1 голос
/ 19 апреля 2010

Какой стиль считается лучшим?

int set_int (int *source) {
   *source = 5;
   return 0;
}

int main(){
   int x;
   set_int (&x);
}

OR

int *set_int (void) {
    int *temp = NULL;
    temp = malloc(sizeof (int));
    *temp = 5;
    return temp;
}

int main (void) {
    int *x = set_int ();
}

Приходя на более высокий уровень программирования, должен сказать, что мне больше нравится вторая версия. Любые, советы будут очень полезны. Все еще учусь C.

Ответы [ 9 ]

6 голосов
/ 19 апреля 2010

Ни.

// "best" style for a function which sets an integer taken by pointer
void set_int(int *p) { *p = 5; }

int i;
set_int(&i);

Или:

// then again, minimise indirection
int an_interesting_int() { return 5; /* well, in real life more work */ }
int i = an_interesting_int();

То, что языки программирования более высокого уровня занимают много места под покровом, не означает, что ваш код на C станет легче писать / читать / отлаживать, если вы продолжите добавлять ненужные выделения: )

Если вам действительно нужен int, выделенный с помощью malloc, и использовать указатель на этот int, то я бы пошел с первым (но исправленным):

void set_int(int *p) { *p = 5; }

int *x = malloc(sizeof(*x));
if (x == 0) { do something about the error }
set_int(x);

Обратите внимание, что функция set_int одинакова в любом случае . Неважно, откуда пришло целочисленное значение, находится ли оно в стеке или в куче, кому оно принадлежит, существовало ли оно долгое время или является новым. Так что это гибко. Если затем вы захотите также написать функцию, которая делает две вещи (что-то выделяет и устанавливает значение), тогда, конечно, вы можете, используя set_int в качестве строительного блока, возможно, так:

int *allocate_and_set_int() {
    int *x = malloc(sizeof(*x));
    if (x != 0) set_int(x);
    return x;
}

В контексте реального приложения вы можете придумать более подходящее имя, чем allocate_and_set_int ...

2 голосов
/ 19 апреля 2010

Первый неверен (не считая синтаксической ошибки) - вы передаете неинициализированный указатель на set_int(). Правильный вызов будет:

int main()
{
    int x;
    set_int(&x);
}

Если они просто int с, и они не могут потерпеть неудачу, тогда обычным ответом будет «ни один» - вы обычно пишете так:

int get_int(void)
{
    return 5;
}

int main()
{
    int x;
    x = get_int();
}

Если, однако, это более сложный тип агрегата, то вторая версия довольно распространена:

struct somestruct *new_somestruct(int p1, const char *p2)
{
    struct somestruct *s = malloc(sizeof *s);

    if (s)
    {
        s->x = 0;
        s->j = p1;
        s->abc = p2;
    }

    return s;
}

int main()
{
    struct somestruct *foo = new_somestruct(10, "Phil Collins");

    free(foo);

    return 0;
}

Это позволяет struct somestruct * быть "непрозрачным указателем", когда полное определение типа struct somestruct не известно вызывающему коду. Стандартная библиотека использует это соглашение - например, FILE *.

2 голосов
/ 19 апреля 2010

Некоторые ошибки:

int main(){
 int x*;  //should be int* x; or int *x;
 set_int(x);
}

Кроме того, вы не выделяете память в первом примере кода.

int *x = malloc(sizeof(int));

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

1 голос
/ 19 апреля 2010

В правилах управления памятью обычно указывается, что распределитель блока памяти также должен освобождать его. Это невозможно, когда вы возвращаете выделенную память. Поэтому второе должно быть лучше.

Для более сложного типа, такого как структура, вы обычно получаете инициализирующую функцию и, возможно, функцию для ее удаления. Выделение и освобождение должны выполняться отдельно вами.

C дает вам свободу распределять память динамически или статически, а наличие функции, работающей только с одним из двух режимов (что было бы в случае, если у вас была функция, возвращающая динамически выделенную память), ограничивает вас.

typedef struct
{
    int x;
    float y;
} foo;

void foo_init(foo* object, int x, float y)
{
    object->x = x;
    object->y = y;
}

int main()
{
    foo myFoo;
    foo_init(&foo, 1, 3.1416);
}
1 голос
/ 19 апреля 2010

Определенно идите с первой версией. Обратите внимание, что это позволило вам пропустить динамическое выделение памяти, которое является МЕДЛЕННЫМ, и может стать источником ошибок, если вы забудете позднее освободить эту память.

Также, , если вы по какой-то причине решили использовать второй стиль, обратите внимание, что вам не нужно инициализировать указатель на NULL. Это значение в любом случае будет перезаписано при возврате malloc(). И если у вас недостаточно памяти, malloc() вернет NULL самостоятельно, без вашей помощи :-). Так что int *temp = malloc(sizeof(int)); достаточно.

0 голосов
/ 14 декабря 2012

В общем, вы спрашиваете, как вернуть значения из функции. Это отличный вопрос, потому что так сложно разобраться. Что вы можете узнать, так это некоторые практические правила, которые остановят вас при создании ужасного кода. Затем читайте хороший код, пока не усвоите различные шаблоны.

Вот мой совет:

  • Как правило, любая функция, которая возвращает новое значение, должна делать это с помощью оператора return. Очевидно, это относится к структурам, но также к массивам, строкам и целым числам. Поскольку целые числа являются простыми типами (они вписываются в одно машинное слово), вы можете передавать их напрямую, а не с помощью указателей.

  • Никогда не передавайте указатели на целые числа, это анти-шаблон. Всегда передавайте целые числа по значению.

  • Научитесь группировать функции по типу, чтобы вам не приходилось изучать (или объяснять) каждый случай отдельно. Хорошая модель - простая OO: функция _new, которая создает непрозрачную структуру и возвращает указатель на нее; набор функций, которые берут указатель на эту структуру и что-то с ней делают (устанавливают свойства, выполняют работу); набор функций, которые возвращают свойства этой структуры; деструктор, который берет указатель на структуру и освобождает ее. Эй, Presto, C становится намного лучше, как это.

  • Когда вы изменяете аргументы (только структуры или массивы), придерживайтесь соглашений, например, библиотеки stdc всегда копируют справа налево; модель ОО, которую я объяснил, всегда ставит указатель структуры первым.

  • Избегайте изменения более одного аргумента в одной функции. В противном случае вы получите сложные интерфейсы, которые вы не сможете запомнить, и в итоге вы ошибетесь.

  • Возвращает 0 для успеха, -1 для ошибок, когда функция делает что-то, что может пойти не так. В некоторых случаях вам может потребоваться вернуть -1 для ошибок, 0 или больше для успеха.

Стандартные API POSIX являются хорошим шаблоном, но не используют никаких шаблонов классов.

0 голосов
/ 19 апреля 2010

Первый вариант предпочтителен (при условии, что простые синтаксические ошибки исправлены), потому что именно так вы имитируете выходной параметр. Тем не менее, его можно использовать только тогда, когда вызывающая сторона может организовать все пространство, которое будет выделено для записи значения перед вызовом; когда вызывающей стороне не хватает этой информации, вы должны вернуть указатель на память (может быть malloc ed, может быть из пула и т. д.)

0 голосов
/ 19 апреля 2010

Лучше не возвращать часть выделенной памяти из функции, если кто-то не знает, как она работает, он может не освободить память. За освобождение памяти должна отвечать код, выделяющий память.

0 голосов
/ 19 апреля 2010

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

Я предпочитаю первый, в Си, но это зависит от того, что вы на самом деле делаете, так как сомневаюсь, что вы делаете что-то такое простое.

Сохраняйте свой код настолько простым, насколько это необходимо, принцип KISS остается в силе.

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