язык С, адресная арифметика, указатель и malloc
Коллеги, не могу сообразить где проблема.
Есть задачка:
Принять int n ; - кол-во вещестенных чисел в последовательности, вводятся с консоли.
Вывести на экран все числа с двумя десятичными знаками.
Пример:
ввод - 3 4.123 7.011 5.1 // 3 - количество чисел (<100), остальное - сами числа.
вывод - 4.12 7.01 5.10
Вот такой код получился:
#include <stdio.h>
#include <stdlib.h>
main(){
int n=0,i=0;
double *pn;
scanf("%d",&n); // Принимаю кол-во чисел в последовательности
pn=(double *)malloc(n*sizeof(double)); // Присваиваю указатель на первый элемент блока
// из n-ячеек памяти размером double
while(i<n) // Гоняю цикл n-раз
scanf("%f",pn+i++); // После присваивания инкрементирую i для
// смещения в следующем цикле, по блоку памяти
// выделенным malloc
while(i>0){
printf("%.2f\n",*(pn + n - i--)); // Вывод чисел n-раз с 2-мя десятичными знаками
}
free(pn); // Освобождаю память, хотя, она и так
// освободится после выхода из программы,
// если я правильно понимаю.
}
Вот результат:
Если я использую указатель типа int * и ввожу int числа, формат меняю с "%f" на "%d", то как будто работает:
не могу понять - почему же не работает с вещественными числами?
Мне бы хотелось разобраться с указателями и адресной арифметикой, поэтому такое решение выбрал.
Ответы (2 шт):
Формат не тот (для scanf важно)
while(i<n) // Гоняю цикл n-раз
scanf("%lf",pn+i++); // После присваивания инрементируюю i для
// смещения в следующем цикле, по блоку памяти
// выделенным malloc
while(i>0){
printf("%.2lf\n",*(pn + n - i--)); // Вывод чисел n-раз с 2-мя десятичными знаками
}
Компилирую ваш код:
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion temp.c temp.c:7:1: error: return type defaults to ‘int’ [-Werror=implicit-int] 7 | main(){ | ^~~~ temp.c: In function ‘main’: temp.c:12:26: error: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Werror=sign-conversion] 12 | pn=(double *)malloc(n*sizeof(double)); // Присваиваю указатель на первый элемент блока | ^ temp.c:15:17: error: format ‘%f’ expects argument of type ‘float *’, but argument 2 has type ‘double *’ [-Werror=format=] 15 | scanf("%f",pn+i++); // После присваивания инрементируюю i для | ~^ ~~~~~~ | | | | | double * | float * | %lf cc1: all warnings being treated as errors
- Нет
intпередmain. На максимальную скорость не влияет. - В произведении
n*sizeof(double)смешана знаковая и беззнаковая арифметика. На максимальную скорость не влияет. scanfчитаетfloatи пишет его вdouble. Это реальная проблема, типы перепутаны.
Если исправить все три ошибки то будет:
#include <stdio.h>
#include <stdlib.h>
int main(){
int n=0,i=0;
double *pn;
scanf("%d",&n); // Принимаю кол-во чисел в последовательности
pn=(double *)malloc((unsigned)n*sizeof(double)); // Присваиваю указатель на первый элемент блока
// из n-ячеек памяти размером double
while(i<n) // Гоняю цикл n-раз
scanf("%lf",pn+i++); // После присваивания инрементируюю i для
// смещения в следующем цикле, по блоку памяти
// выделенным malloc
while(i>0){
printf("%.2f\n",*(pn + n - i--)); // Вывод чисел n-раз с 2-мя десятичными знаками
}
free(pn); // Освобождаю память, хотя, она и так
// освободится после выхода из программы,
// если я правильно понимаю.
}
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion temp.c $ ./a.out 3 4.123 7.011 5.1 4.12 7.01 5.10
Ехидный вопрос: тяжело, наверно, без компилятора программировать?
Дальше мелочи:
- в
mallocне надо писатьsizeof(double). Напишитеsizeof(*pn), так вы не дублируете тип в двух местах. - результат
mallocне надо приводить,void *приводится кdouble *автоматически. - выражения
pn+i++и&pn[i++]равносильны. Но первое обычно означает массив начинающийся с адресаpn+i++, а второе адрес одного элемента. Если вы придерживаетесь этого соглашения, программисту, читающему код, легче понять это указатель на один элемент или на массив. - C индексами можно играть как угодно, но лучше перебирать массива канонически, через
for(int i = 0; i < n; ++i). nможно сделать беззнаковым, будет меньше приведений.- переменные объявляются как можно позже. Индексы цикла объявляются в цикле.
Вот это канонический код, который стандартному программисту на С читать и понимать легче:
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned n;
scanf("%u", &n); // Принимаю кол-во чисел в последовательности
double *pn = malloc(n * sizeof(*pn)); // Присваиваю указатель на первый элемент блока
// из n-ячеек памяти размером double
for (unsigned i = 0; i < n; ++i) { // Гоняю цикл n-раз, тут вся работа с i
scanf("%lf", &pn[i]);
}
for (unsigned i = 0; i < n; ++i) { // Гоняю цикл n-раз, тут вся работа с i
printf("%.2f\n", pn[i]); // Вывод чисел n-раз с 2-мя десятичными знаками
}
free(pn); // Освобождаю память, хотя, она и так
// освободится после выхода из программы,
// если я правильно понимаю.
}

