Использование realloc путем умножения целого числа указателя и sizeof (int) не работает

Код:

void main() {
    int *array = calloc(5, sizeof(int));
    int *amount = 9;
    array[0] = 1;
    array[1] = 2;
    array[3] = 5;

    int i = 0;
    while (i < 5) {
        printf("%d ", array[i]);
        i += 1;
    }

    printf("%d", amount); //Printing
    array = realloc(array, amount * sizeof(int)); //Problem is here
    printf("\n");
    i = 0;
    while (i < 9) {
        printf("%d ", array[i]);
        i += 1;
    }
    free(array);
}

Он говорит: «Недопустимые операнды для двоичного * (имеют« int * »и« unsigned int »), но когда я попытался напечатать« количество », это на самом деле 9? Я пытаюсь использовать целое число указателя, чтобы я мог передать его ссылка.


person Richard    schedule 13.06.2018    source источник
comment
Что должен делать int *amount = 9;? Это означает, что amount указывает на адрес 9, и, вероятно, там нет ничего полезного. Зачем делать amount указателем для начала? Какую проблему это должно решить?   -  person Some programmer dude    schedule 13.06.2018
comment
Любопытно, что вы присваиваете 9 указателю int.   -  person dbrank0    schedule 13.06.2018
comment
Ага, а зачем указатель? Избавься от этой звезды!   -  person Martin James    schedule 13.06.2018
comment
@Someprogrammerdude Насколько я знаю, вы можете сразу инициализировать значение, на которое указывает указатель, когда вы объявляете переменную, то есть int *amount = 9;. Вместо этого я попытался сделать это: int *amount и amount = 9;, и оказалось, что это одно и то же. Я предполагаю, что делаю правильно (поскольку я также помню, как мой лектор говорил мне об этом)? Кроме того, я пытаюсь передать его по ссылке позже в функцию. Есть ли другой способ сделать это?   -  person Richard    schedule 13.06.2018
comment
Если вы хотите эмулировать передачу по ссылке, вы передаете указатель на переменную с помощью оператора адреса &. Как и в int amount = 9; ...; some_function(&amount);, используйте оператор разыменования * для доступа к тому, на что указывает указатель внутри функции.   -  person Some programmer dude    schedule 13.06.2018
comment
@Someprogrammerdude Понятно, спасибо! Однако что я сделал не так в своем предыдущем коде? Разве это не должно быть правильно?   -  person Richard    schedule 13.06.2018
comment
Как я сказал в своем первом комментарии, int *amount = 9; заставляет amount указывать на адрес 9. Это не делает amount указанием на какое-то целое число, имеющее значение 9, это было бы что-то вроде int real_amount = 9; int *amount = &real_amount;   -  person Some programmer dude    schedule 13.06.2018
comment
@Someprogrammerdude Хм... Понятно. Еще раз спасибо за ответы :-D!   -  person Richard    schedule 13.06.2018


Ответы (4)


Несколько вещей:

Первый,

int *amount = 9;

не делает то же самое, что

*amount = 9;

В первом случае * указывает только на то, что amount имеет тип указателя, и мы инициализируем значение указателя (т. е. адрес) равным 9, что, скорее всего, не является допустимым значением указателя. , и попытка разыменовать его может привести к ошибке времени выполнения.

Во втором случае мы присваиваем целочисленное значение 9 объекту, на который указывает amount.

Почему это не сломалось, когда вы передали amount в printf? По сути, вы вызвали неопределенное поведение, передав аргумент неправильного типа (%d ожидает int, вы передали int *). Одним из возможных результатов неопределенного поведения является получение ожидаемого результата. По какой-то причине printf смог обработать это значение int * как int. Большинство компиляторов должны помечать это несоответствие типов, но вы можете поднять уровень предупреждения, чтобы увидеть это.

На двоичный оператор * наложено ограничение, согласно которому оба операнда имеют арифметический тип. int * не является арифметическим типом, отсюда и диагностика.

Основываясь на том, как вы на самом деле используете amount в своем коде, вы должны были объявить его не как указатель, а как обычный int:

int amount = 9;

Во-вторых, как правило, вы не хотите присваивать исходному указателю результат realloc. Если realloc не удастся, он вернет NULL и оставит исходный блок памяти как есть. Однако, если вы вернете этот NULL исходному указателю, вы потеряете доступ к этой памяти. Лучше всего присвоить результат realloc временному объекту, а затем убедиться, что временный объект действителен, прежде чем назначать его обратно оригиналу:

int *tmp = realloc( array, amount * sizeof *array );
if ( tmp )
{
  array = tmp;
}
else
{
  // handle realloc error
}

Обратите внимание на использование sizeof *array вместо sizeof (int). sizeof — это оператор, подобный унарному * или унарному +, и его операнд может быть либо именем типа в скобках, либо выражением. выражение *array имеет тип int, поэтому sizeof *array == sizeof (int). Это помогает сделать код немного легче для чтения, и если вы когда-нибудь измените тип array (скажем, на double *), вам не придется обновлять вызов realloc. Это также очень полезно при распределении типов многомерных массивов — вы бы предпочли написать

int (*arr)[10] = malloc( sizeof (int) * 10 * rows);

or

int (*arr)[10] = malloc( sizeof *arr * rows );

?

person John Bode    schedule 13.06.2018
comment
Я вижу, спасибо за очень четкий ответ! Некоторые вопросы, однако, как или когда realloc не работает? Это очень техническая и сложная проблема, и я должен просто игнорировать ее сейчас? И спасибо за подсказку об использовании sizeof(pointervariable) вместо явного ввода типа переменной :-D - person Richard; 15.06.2018
comment
О, и как мне распечатать адрес памяти, на который указывает указатель (т.е. какой тип я должен использовать, поскольку %d неверен)? - person Richard; 15.06.2018
comment
@WealthyPlayer: используйте %p для вывода значений указателя. realloc завершится ошибкой, если для удовлетворения запроса недостаточно большого фрагмента доступной памяти. Этого не произойдет, если только вы не пытаетесь выделить очень большие участки памяти или если вы произвели очень большое количество небольших выделений, так что куча сильно фрагментирована (т.е. может иметь так много доступных МБ, но не в одном непрерывном блоке). Я никогда не сталкивался с этим на практике, но это то, что нужно знать. - person John Bode; 15.06.2018
comment
Хорошо, спасибо! Я тоже это читал (и ответ тоже был от вас :-D): stackoverflow.com/questions/18217525/, и я нашел это очень полезным при попытке понять для с какой целью динамическое выделение памяти. - person Richard; 15.06.2018

Вы просто используете тот факт, что ваша реализация допускает безопасное преобразование между указателями и целыми числами, но это:

int *amount = 9;      // only use that for memory mapped hardware registers
printf("%d", amount); //re-interpreting the pointer value as an int
array = realloc(array, amount * sizeof(int));   // hopefully you got a diagnostic

ужасно. Указатель должен быть только нулевым указателем на действительный объект. Полная остановка. А арифметика указателей имеет смысл только внутри массива.

Если переменная должна содержать целочисленные значения, то она должна быть целочисленного типа:

int amount = 9;
printf("%d", amount); //Printing
array = realloc(array, amount * sizeof(int)); //Problem is here

Если вам нужен указатель на него, просто объявите его и используйте как указатель:

int amount = 9;
int *p_amount = &amount;
printf("%d - %d\n", amount, *p_amount); //Printing
array = realloc(array, (*p_amount) * sizeof(int)); //No problem now
person Serge Ballesta    schedule 13.06.2018
comment
Я не совсем понимаю, что вы имели в виду под указателем. Указатель должен быть только нулевым указателем на действительный объект и использовать его только для аппаратных регистров с отображением памяти. Не могли бы вы объяснить их более простыми словами? - person Richard; 13.06.2018
comment
@WealthyPlayer: указателю должны быть назначены только адреса. int a; int *pt = &a, null, int *pt = NULL; значений других указателей int a; int *pa = &a; int *pb = pa;. int *amount = xxx;, где xxx — целое число, — это запах кода. Единственный известный мне правильный вариант использования — это прямой доступ к оборудованию (в режиме ядра или во встроенных системах). В этом случае у вас могут быть порты ввода-вывода или отображение памяти на хорошо известные адреса. Например, в MS/DOS текстовый экран отображался по адресу 0xB0000. - person Serge Ballesta; 13.06.2018
comment
Ах! Ясно, я думаю, вы имели в виду или вместо? Спасибо! - person Richard; 13.06.2018
comment
@WealthyPlayer: Да, извините за это. Клавиши F и R для меня слишком близки :-( - person Serge Ballesta; 13.06.2018
comment
Нет проблем :-) Вы ответили на мои вопросы, я должен поблагодарить вас. - person Richard; 15.06.2018

amount имеет тип int *. Причина, по которой printf печатает 9, не в том, что amount указывает на значение 9, а в том, что значение 9 приводится к указателю.

Теперь в вашем заявлении array = realloc(array, amount * sizeof(int));. Вы пытаетесь умножить указатель (а не значение, на которое указывает amount). Спросите себя, какой должна быть семантика этого. Вместо int *amount = 9; вы, вероятно, хотите

int *amount = calloc(1, sizeof(int));
*amount = 9;

который объявляет указатель и выделяет место для одного целого числа, а вместо array = realloc(array, amount * sizeof(int)); вы, вероятно, захотите

array = realloc(array, *amount * sizeof(int));

вы должны попытаться изучить концепцию указателей и арифметику указателей. Простое объявление указателя не резервирует место в конце.

person Iead    schedule 13.06.2018
comment
Мм понятно. Так можно ли с уверенностью сказать, что с самого начала (то есть int *amount = 9) я уже неверно истолковал значение этой строки? Кажется, теперь я знаю, как это работает, я также пробовал читать gribblelab.org/CBootCamp/8_Pointers. .html в ожидании ответа. Спасибо! - person Richard; 13.06.2018
comment
да. Вы хотели что-то, что указывает на значение 9, но получили что-то, что указывает на позицию 9. Иногда C и C++ могут быть довольно сложными в отношении того, что именно означает строка. - person Iead; 13.06.2018

amount следует определять как int, а не int *.

int amount = 9;
person eyalm    schedule 13.06.2018