Условие программы: удалить все нули в одномерном массиве (тема: массивы и указатели)

На массив нужно динамически выделить и перевыделить память, к элементам массива нужно обращаться через указатели. Проблема с реаллоком - работает коряво. Помогите разобраться, как мне уменьшить (или увеличить, например, если по условию нужно будет добавлять элементы) размер массива в моём цикле, заранее спасибо.

#include "stdio.h"
#include "time.h"
#include "stdlib.h"

int main() 
{
    int i, j, n, c = 0, * a, *A, k = 0; 
    printf("N: "); 
    scanf_s("%d", &n);
    srand(time(NULL));
    a = (int*)malloc(n * sizeof(int));
    if (!a) return -1;
    for (i = 0; i < n; i++)
        printf("%d ", *(a + i) = rand() % 9 - 6); 
    printf("\n");
    for (i = j = 0; i + j < n; i++) 
    { 
        while (c == *(a + i + j))
        {
            j++; 
            *(a + i) = *(a + i + j);
            k++;
        }
    }
    i -= *(a + n - 1) == c;
    A = (int*)realloc(a, (n-k) * sizeof(int));
    if (!A) return -1;
    for (j = 0; j < i; j++)
    {
        printf("%d ", *(a + j));
    }
    free(A);
    return 0;
}

Примеры:

1 пример:
1)входные данные:   2 1 -6 -5 2 2 0 -1 -1 -2; 
2)итоговые данные:  2 1 -6 -5 2 2 -1 -1 -1;
3)ожидаемые данные: 2 1 -6 -5 2 2 -1 -1 -2;

2 пример:
1)входные данные:   -1 0 0 -5 -3 -2 -5 -1; 
2)итоговые данные:  -1 -5 0 -5 -3 -2;
3)ожидаемые данные: -1 -5 -3 -2 -5 -1;

Ответы (3 шт):

Автор решения: Павел Ериков

Объясню на примере в чем ошибка вашего кода:

1 0 -4 3

На моменте когда вы доходите до 0 вы заменяете его на -4 и увеличиваете k до 1.

Получаете вот такую картину:

1 -4 -4 3

И перевыделяете память удаляя последний элемент. Получаете вот это:

1 -4 -4

Вот пример кода с комментариями:

//2 итератора i и j
//i - итератор замены, j - итератор сравнения
//то есть *(a + i) вы будете заменять на *(a + j)
//алгоритм основывается на том чтобы *(a + j) было ближайшее справа число != 0
for (i = j = 0; i < n && j < n; i++, j++)
{
    //Сдвигаем итератор j вправо пока не дойдем до числа != 0
    //увеличивая k
    while (j < n && *(a + j) == c) {
        j++;
        k++;
    }
    //Если i != j то мы нашли как минимум один 0
    //производим замену
    if (j < n && i != j)
        *(a + i) = *(a + j);
}

Например возьмем те же числа, как в примере выше:

1 0 -4 3

Дойдя до 0 мы будем иметь i = 1, j = 1, k = 0 после выполнения цикла получим i = 1, j = 2, k = 1 и следующее что мы делаем это заменяем *(a + 1) на *(a + 2), то есть 0 на -4. И теперь каждую итерацию мы будет заменять i элемент на i + 1 (в нашем случае т.к. у нас один 0).

В итоге получим:

1 -4 3 3

И т.к. k = 1 обрежем последний элемент, получив:

1 -4 3
→ Ссылка
Автор решения: LShadow77

Предложу ещё модифицированный вариант программы, с некоторыми комментариями. На мой взгляд, он более понятный и тут лучше раскрыта тема работы с указателями:

#include "stdio.h"
#include "time.h"
#include "stdlib.h"

int main() 
{
    int i, n, k;
    int c = 0;
    int *a, *A, *a_end, *p, *p_next; 

    printf("Enter n: "); 
    scanf("%d", &n);
    
    a = (int*)malloc(n*sizeof(int));
    if (!a) return -1;
    a_end = a+n;    //указатель за конец масива

    srand(time(NULL));
    for (p = a; p < a_end; ++p)
        printf("%d ", *p = rand()%9-6); 
    printf("\n");

    //ищем первый ноль
    for (p = a; p<a_end; ++p) 
        if (*p==c) break;

    //удаляем нули
    p_next = p;
    while ((++p_next)<a_end)
    {
        if (*p_next==c) continue;
        *(p++) = *p_next;
    }

    k = p-a; //новый размер масива

    if (k)
    {
        A = (int*)realloc(a, k*sizeof(int));
        if (!A)
        {
            free(a);  //функция realloc() сама не освобождает память при неудаче
            return -1;
        }
        a_end = A+k;

        for (p = A; p<a_end; ++p)
            printf("%d ", *p);
        printf("\n");

        free(A);
        return 0;
    }

    printf("Empty array!\n");
    free(a);
    return 0;
}
→ Ссылка
Автор решения: avp

Проще всего выкинуть из массива нули -- это тупо переписать массив сам в себя, пропуская нули.

Если хочется сделать это с указателями (а не индексами массива), то вот возможный фрагмент кода

  int *end = a + n;  // `a` -- это исходный масив из `n` элементов, выделенный по `malloc()`
  int *to = a;

  for (int *f = a; f < end; f++)  // вот и весь цикл, выбрасывающий нули
    if (*f)
      *to++ = *f;

  printf("now %d elements\n", n = to - a);
  a = (int *)realloc(a, n * sizeof(int));
→ Ссылка