删除动态数组中等于零的元素而不使用C语言中的辅助数组

Deleting elements that equal zero in dynamic array without using secondary array in C language

大家好,我想在不使用另一个中间动态数组的情况下从动态数组中删除空元素,在最终结果中,我的程序应该只打印非空元素。 举个例子 : 输入是:[ 2 3 0 3 0 6 ] 结果输出应该是:[2 3 3 6] 这是我的代码:

#include <stdio.h>
#include <stdlib.h>

int main()
{
int *T,i,n,*p,*k;

printf("entrez the size of the array\n");
scanf("%d",&n);

T=(int*)malloc(n*sizeof(int));

for (p=T;p<T+n;p++){printf("Enter element T[%d]=",p-T);
                    scanf("%d",p);}
printf("\n");
printf("[");
printf("\t");
for (p=T;p<T+n;p++){printf("%d\t",*p);}
printf("]");

for (p=T;p<T+n;p++){
                     if(*p==0){
                               k=p;
                               for(k=p;k<T+n-1;k++){*k=*(k+1);}
                                n--;}
printf("\n");
printf("[");
printf("\t");
for (p=T;p<T+n;p++){printf("%d\t",*p);}
printf("]");
}}

但不幸的是,我的程序没有显示没有空元素的结果数组;它保持不变。

正在格式化这部分:

for (p=T;p<T+n;p++)
{if(*p==0){k=p;
           for(k=p;k<T+n-1;k++){
              *k=*(k+1);}
              n--;

              }


printf("\n");
printf("[");
printf("\t");
for (p=T;p<T+n;p++)
{printf("%d\t",*p);

}
printf("]");
}}

代码将是这样的:

    for (p=T;p<T+n;p++)
    {
        if(*p==0)
        {
            k=p;
            for(k=p;k<T+n-1;k++)
            {
                *k=*(k+1);
            }
            n--;
        }

        printf("\n");
        printf("[");
        printf("\t");
        for (p=T;p<T+n;p++)
        {
            printf("%d\t",*p);
        }
        printf("]");
    }
}

现在很明显,打印部分在循环内以删除值为零的元素。

如果第一个元素为零,则可以正确去掉。

打印部分后,p会变成T+n,外层循环结束

打印部分应该是循环之后删除值为零的元素。

此外,当 *p==0 时,您不应该执行 p++ 以避免在零之后忽略零。

    for (p=T;p<T+n;)
    {
        if(*p==0)
        {
            k=p;
            for(k=p;k<T+n-1;k++)
            {
                *k=*(k+1);
            }
            n--;
        }
        else
        {
            p++;
        }
    }
    /* get this out of the loop */
    printf("\n");
    printf("[");
    printf("\t");
    for (p=T;p<T+n;p++)
    {
        printf("%d\t",*p);
    }
    printf("]");
}

您的思考方向是正确的,最大的问题是您无法验证任何内容,并且您只想在用户提供整数后在插入循环中递增 p。您还可以在进入删除循环时同时初始化 pk,例如

#include <stdio.h>
#include <stdlib.h>

/* simple function to empty stdin after matching-failure */
void empty_stdin (void)
{
    int c = getchar();
    
    while (c != '\n' && c != EOF)
        c = getchar();
}

int main (void)
{
    int *T, n, *p, *k;

    puts ("entrez the size of the array");
    if (scanf ("%d",&n) != 1) {                        /* validate EVERY input */
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }
    
    T = malloc (n * sizeof *T);
    if (T == NULL) {                                   /* validate EVERY allocation */
        perror ("malloc-T");
        return 1;
    }
    
    for (p = T; p < T + n;) {   /* only increment on valid inut */
        printf ("Enter element T[%ld] = ", p - T);
        if (scanf ("%d",p) != 1) {                      /* validate EVERY input */
            fputs ("  error: invalid integer input.\n", stderr);
            empty_stdin();                              /* remove non-digits */
            continue;                                   /* try again */
        }
        p++;                                            /* now increment pointer */
    }
    
    fputs ("\n[", stdout);                              /* no multiple printf req'd */
    for (p = T; p < T+n; p++)
        printf (" %3d", *p);
    puts (" ]");
    
    for (p = T, k = T; p < T + n; p++) {                /* initialize both p and k */
        if (*p != 0)
            *k++ = *p;              /* assign and increment k only when p not zero */
    }
    n = k - T;                      /* only reset n after loop completes */
    
    fputs ("[", stdout);
    for (p = T; p < T+n; p++)
        printf (" %3d", *p);
    puts (" ]");

    free (T);                       /* don't forget to free what you allocate */
}

在C中,不需要强制转换malloc的return,没有必要。参见:Do I cast the result of malloc?

例子Use/Output

无效输入是故意的。如果您的代码不会处理无效输入,则您正在邀请未定义的行为,例如

$ ./bin/array_rm_nul
entrez the size of the array
6
Enter element T[0] = 2
Enter element T[1] = 3
Enter element T[2] = bananas
  error: invalid integer input.
Enter element T[2] = 0
Enter element T[3] = 3
Enter element T[4] = 0
Enter element T[5] = 6

[   2   3   0   3   0   6 ]
[   2   3   3   6 ]

删除零的循环至少有两个问题。

第一个是内循环之后

for(k=p;k<T+n-1;k++){*k=*(k+1);

指针 p 现在指向数组中未处理的元素)下一个元素)。

但是在外循环中指针 p 递增

for (p=T;p<T+n;p++){
               ^^^ 

因此跳过未处理的元素。

第二个问题是,总是在删除当前等于0的元素后,再复制数组的所有元素,效率很低

始终尝试编写更通用的代码。

您可以定义一个从数组中“删除”指定值的函数。

这是一个演示程序。

#include <stdio.h>
#include <stdlib.h>

 size_t remove_item( int *a, size_t n, int item )
 {
    int *p = a;
    
    for ( int *q = a; q != a + n; ++q )
    {
        if ( *q != item )
        {
            if ( p != q )
            {
                *p = *q;
            }
            ++p;
        }
    }
    
    return ( size_t )( p - a );
 }
 
int main(void) 
{
    int a[] = {  2, 3, 0, 3, 0, 6 };
    size_t n = sizeof( a ) / sizeof( *a );
    
    n = remove_item( a, n, 0 );
    
    for ( const int *p = a; p != a + n; ++p )
    {
        printf( "%d ", *p );
    }
    
    putchar( '\n' );
    
    return 0;
}

程序输出为

2 3 3 6

函数returns删除具有指定值的元素后数组中实际元素的数量。

如果传递的数组是动态分配的,那么您可以在调用函数后重新分配它,例如

int *tmp = realloc( T, n * sizeof( int ) );
if ( tmp != NULL ) T = tmp;

注意当数组不再需要时,应该释放分配给数组的内存。