@Deckers

Как правильно работать с адресом в массиве?

Всем хай, объясните в чем разница этих написаний? Конкретно я хочу работать с адресом ячейки а не просто ее значением, менять местами и т.п.
#include <stdio.h>

int main()
{
int n, a[n], i;
int min, max;
printf("Введите n: ");
scanf("%d", &n);
for(i = 0; i < n; i++) {
    printf("a[%d] = ", i);
    scanf("%d", &a[i]); 
    max = a[0], min = a[0];}

for(i = 0; i < n; i++){
    if(a[i] > max) { max = a[i]; }
    if(a[i] <= min) { min = a[i]; } }
printf("\nMax: [%d] \nMin: [%d] \n", max, min);
printf("%d %d", a[max], a[min]);
}
  • Вопрос задан
  • 78 просмотров
Пригласить эксперта
Ответы на вопрос 1
@gscdlr
Человек?
int n, a[n], i;
Лучше так не делать. Компилятор и не даст. Значение n должно быть известно при создании массива, а еще лучше быть предопределено в коде через define или const. И еще при объявлении переменной нужно сразу инициализировать ее через значение. Например, занулить все int.
Чтобы работало, объявление массива размером n, заданным пользователем, должно происходить после получения значения n:
int n = 0, i = 0, min = 0, max = 0;
printf("Введите n: ");
scanf("%d", &n);
int a[n];   /* а вот здесь компилятор не дает инициализировать статический массив с какими-либо значениями. */
/* Последующий код без изменений */


Еще б не помешала проверка на корректное введенное значение n:
int breakflag = 0;
printf("Введите n: ");
while (!breakflag) {  // пока переменная, отвечающая за конец цикла == false
    if (scanf("%d", &n) != 1) {  // если сканф получил не целое число в пределах int
        puts("Не целочисленное значение. Я устал, я мухожук.");
        breakflag = 2; // некий код ошибки
    } else if (n < 2 && n != -1) {
        printf("Введите значение n > 2 или -1 для выхода: ");
    } else {
        breakflag = 1;
    } 
}
if (breakflag == 1) {
    breakflag = 0;  // для нулевого ретерна об успешной работе программы.
/* Код объявления и обработки массива*/
}
return breakflag;
}


Если заранее не известен размер массива, лучше бы пользоваться динамикой...
#include <stdlib.h>

/* код до создания массива*/
int* a = (int*)calloc(0, n * sizeof(int));  // создаем диамический массив размера n, заполняя его нулями.
// либо можно выделить память малоком и сделать memset();
if (a) {
/* Работаем с массивом как обычно, после проверки, что память выделилась. */
/* Либо можно работать по указателю, как в последнем примере. В таком случае */
/* понадобится простой указатель, который будет хранить нулевую ячейку а: */
//   int *start_a = a;
//   везде, где понадобится сброс указателя в начало, просто присваиваем
//   a = start_a; 
//   в конце нужно чистить только память по указателю a.
}
// Когда память больше не нужна, чистим во избежание утечки, с проверкой, что указатель не NULL:
if (a) {
    free(a);
}
// Память чистится только тогда, как была выделена память через одну из функций аллокации.


С динамическим массивом можно работать также, как со статическим, используя обращение имя массива[адрес элемента], но с многомерными массивами уже чуть сложнее и нужна внимательность.
указатель на динамический массив можно менять, например, через инкремент или декремент, но тогда желательно возвращать его на начальный элемент после обработки массива, если в дальнейшем этот массив еще будет обрабатываться.
Можно взять значение из ячейки динамического массива через унарный оператор *arr, это == arr[0] после создания массива. Можно использовать такую адресацию во время обработки: *(array+i) в качестве lvalue или rvalue.
При указании адреса в квадратных скобках внутри скобок можно производить любые действия для получения нужного адреса типа unsigned . Для изменения указателя динамического массива можно использовать только операции сложения и вычитания. например: array -= countA; или array += countB;
Просто так менять значения местами нельзя, нужна функция, которая это будет делать, либо писать ручками это каждый раз. Но в случае с указателями можно создать указатель и дать ему значение на любую другую переменную или значение элемента какого-то другого массива.
int a = 10, b = 20, *tmp_pointer = NULL;
tmp_pointer = &a;  // теперь через (*tmp_pointer) можно получить актуальное 
// значение a в момент обращения к указателю.
tmp_pointer = &b;  // а теперь (*tmp_pointer) даст текущее значение b, либо 
// запишет значение в переменную b 
// при использовании (*tmp_pointer) в L-value.


Для примера чуть отредактирую код программы с использованием указателя. Указатель будет указывать на массив, все значения записывать, читать и сравнивать будем через указатель.
Если коротко, *ptr - работа со значением по указателю, ptr - сам указатель.
/* Код после получения значения n: */
int arr[n];
int *a = arr;
for(i = 0; i < n; i++) {
    printf("a[%d] = ", i);
    scanf("%d", a++); }
// после цикла с инкрементом указателя *a == arr[n-1]
    a = arr;  // сбрасываем на нулевую ячейку массива.
max = *a, min = *a;  // здесь *a == arr[0]

for(i = 0; i < n; i++) {
    if(*a > max) { max = *a; }
    if(*a <= min) { min = *a; } 
    ++a;
}
a = arr;
/* Вывод результатов */
/* чистить указатель не надо: он указывает на статический массив, а не динамически-выделенную память из кучи */


Напоследок еще стоит сказать: никогда не выходите за границы массива. ни для чтения, ни для записи. Ничего хорошего не получится из этого.
И напоминаю: Не забывайте возвращать указатель на начальное положение, очищать память на выходе, контролировать границы своей памяти и !NULL при работе с динамической памятью.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы