Ответы пользователя по тегу C
  • Как правильно работать с адресом в массиве?

    @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 при работе с динамической памятью.
    Ответ написан
    Комментировать