Объявление всех переменных как указателей против использования адресных операторов в C - PullRequest
0 голосов
/ 08 марта 2019

Следующие два блока кода выполняют одно и то же:

void foo(int *num) {...}

int main(void)
{
    int num = 5;
    foo(&num);

    ...
}
void foo(int *num) {...}

int main(void)
{
    int *num;
    *num = 5;
    foo(num);

    ...
}

В обоих случаях foo ожидает аргумент указатель на int. В первом я объявляю int и использую адресный оператор (&), чтобы передать его в foo. Во втором я объявляю переменную-указатель и напрямую передаю ее в foo.

Есть ли причина использовать какой-либо из них, кроме стилистических предпочтений? Я предпочитаю объявлять все мои переменные как указатели, как во второй, но я вижу первую намного чаще.

Ответы [ 6 ]

3 голосов
/ 08 марта 2019

Нет, эти два фрагмента делают совершенно разные вещи.Первый фрагмент - это правильно сформированный код.Второй фрагмент - «Неопределенное поведение» из-за разыменования неинициализированного указателя.

Вторая версия может привести к сбою или выполнению каких-либо других нежелательных действий при выполнении.Кроме того, скомпилированный с предупреждениями на любом современном компиляторе, он будет отображать предупреждение, например:

предупреждение: переменная 'num' неинициализируется при использовании здесь [-Wuninitialized]

2 голосов
/ 08 марта 2019

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

int *num = malloc(sizeof(int));
*num = 5;

foo(num);

free(num);

Или:

int num = 5;
int *p = #
foo(p);

Главное, от чего нужно отказаться, это то, что первая форма самая простая.Допустимая переменная-указатель должна указывать на память в элементе управления вашей программы, и это требует больше работы, чем просто int *num; *num = 5;

1 голос
/ 08 марта 2019

Следующие два блока кода выполняют одно и то же:

Нет, нет. Только первое определило поведение вообще. Вторая разыменовывает дикий указатель, когда выполняет *num = 5, не назначив действительный указатель на num.

В обоих случаях foo ожидает аргумент указатель на int. В первом я объявляю int и использую адресный оператор (&), чтобы передать его в foo. Во втором я объявляю переменную-указатель и напрямую передаю ее в foo.

Вы, похоже, страдаете от того, что печально часто не можете различить указатель и то, на что он указывает. Они разные и во многом ортогональные. Объявление int *num не объявляет и не резервирует места для int. Он объявляет указатель, который может указывать на int, но значение этого указателя недействительно, пока вы не назначите тот, который указывает на int.

Есть ли причина использовать один, кроме стилистического предпочтение?

Да. Первый действителен, а второй нет.

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

Мне жаль слышать, что вы вообще видите вторую .

0 голосов
/ 08 марта 2019

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

Это утверждение не имеет большого смысла -конечно, не все , с которым вы работаете, является указателем.

Код

int *num;
*num = 5;
foo( num );

имеет неправильную форму, поскольку num ни на что не указывает (его значение равно неопределенное ).Вы пытаетесь присвоить 5 некоторому случайному адресу памяти, который может привести к segfault или повреждению других данных.Он может работать так, как задумано, без явных проблем, но это случайно, а не дизайн.

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

int num;
int *numptr = #
*numptr = 5;
foo( numptr )

, но это ничего не изменит с вашей простотой:

int num = 5;
foo( &num );

C требует нам нужно использовать указатели в 2 случаях:

  • когда функция должна обновить один из своих входных параметров;
  • когда нам нужно отслеживать динамически выделенную память;

Они также пригодятся для построения динамических структур данных, таких как списки, очереди, деревья и т. Д. Но вы не должны использовать их для всего .

0 голосов
/ 08 марта 2019

один

void one(void) {
    int n = 42; // initialization
    foo(&n);
    putchar(n);
}

два

void two(void) {
    int *n;
    n = malloc(sizeof *n);
    *n = 42; // no initialization
    foo(n);
    putchar(*n);
    free(n);
}

три

void three(void) {
    int n[1] = { 42 }; // initialization
    foo(n);
    putchar(*n); //putchar(n[0]);
}
0 голосов
/ 08 марта 2019

Конечно, как уже отмечали другие, во втором вам нужно инициализировать точку для правильной выделенной памяти.Так что это один пункт в пользу первого кода.

Для примера Джона Кугельмана

int num;
int *p = #
*p = 5;
foo(p);

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

Наконец, и я думаю, что в качестве общего итога, второй пример действительно не получает значениявсе мои переменные как указатели ".Вы должны иметь где-то реальную память, и первый пример проясняет это.Он передается как указатель, поэтому, за исключением кода, который объявил память, он является указателем.

...