【问题标题】:Pointers in C, getting unintended results with arraysC中的指针,使用数组得到意想不到的结果
【发布时间】:2018-06-28 13:10:33
【问题描述】:

我想计算几个数字的阶乘并以数组的形式返回结果。这是我的程序:

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

int *fact(int arr[], int n) {
    int *facts, fac = 1, i, j;

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

    for (i = 0; i < n; i++) {
        for (j = 1; j <= *(arr + i); j++)
            fac = fac * j;

        *(facts + i) = fac;
    }

    return facts;
}

int main(void) {
    int *num, *facto, n, i;     //Initializing variables.

    printf("How many numbers to calculate factorial for :\t");
    scanf("%d", &n);

    num = (int*) malloc(n * sizeof(int));   //Dynamic allocation for array num.

    printf("Enter the elements separated by spaces :\t");

    for (i = 0; i < n; i++)
        scanf("%d", num + i);

    facto = fact(num, n);

    printf("\nFactorials are :\t");

    for (i = 0; i < n; i++)
        printf("%d\t", *(facto + i));

    printf("\n");

    return 0;
}

然而,当它打印返回数组的元素时,只有第一个值是正确的。其他的似乎是随机的。

这个程序有什么问题,我该如何解决?

【问题讨论】:

  • 仅供参考,对于任何指针数组facts和索引i,表达式*(facts + i)恰好等于facts[i]。后者通常更易于阅读和理解,并且还可以节省您编写几个字符的时间。另请阅读Do I cast the result of malloc?
  • 至于您的问题,请编辑它以显示您的输入、该输入的实际输出以及预期输出。当然,请learn how to debug your programs
  • OT:当调用任何scanf() 系列函数时,始终检查返回值(而不是参数值)以确保操作成功。
  • OT:当调用任何堆分配函数时:malloccallocrealloc` 1) 返回类型为void*,可以分配给任何其他指针。强制转换只会使代码混乱,使其更难以理解、调试等。2) 始终检查 (!=NULL) 返回值以确保操作成功

标签: c arrays function pointers dynamic-arrays


【解决方案1】:

您需要在j 上的每个 for 循环之前将 fac 重置为 1,否则在除第一个之外的每个计算中,第一个因素都不会从 1 开始:

for(i=0; i<n; i++) {   
   fac = 1; // HERE
   for(j=1; j<= *(arr+i); j++) {
        fac = fac*j;
   }
   *(facts+i) = fac;
}

【讨论】:

  • 是的,减少此类问题发生的一个好习惯是尽可能缩小变量的范围。在这种情况下,fac 仅在外循环内使用,其值不需要从一次迭代持续到下一次迭代,因此 fac 应该在循环内声明
  • @JohnBollinger fac变量没用,直接用facts[i]比较好。
  • 这是风格问题,@Tom's。我同意fac 从程序逻辑的角度来看是不必要的,但“无用”并不清楚。在不同的情况下,使用单独的标量变量甚至可以达到功能目的。
  • @JohnBollinger 我同意我应该使用不必要的而不是无用的,是的,在某些特定情况下,最好使用另一个变量。但是在这种情况下,“fac”变量将“复杂性”添加到代码中。通过复杂性,我的意思是有更多的代码要阅读,更多的变量要记住,等等。对于这个例子,这看起来很荒谬,但是这种添加不必要变量的习惯通常会导致添加相当多的变量,而这这才是真正的麻烦开始的地方,因为当你阅读这种代码时,你会怀疑这个变量是否有真正的目的。
  • 他的代码有几个样式问题,这对于初学者来说并不少见。我试图让我的回答直截了当,这就是为什么他的代码没有计算预期值的原因,并且提供了一个最小的修复,而没有彻底检查整个代码。
【解决方案2】:

如前所述,您的问题在于变量“fac”未重置为 1。

使用只做一件事的函数是一种保持代码简短且不易出错的好方法。

例如,您的事实函数分配并计算阶乘。 您可以像这样将此功能一分为二:

int factorial(int n)
{
    int result = 1;

    for (int i = 2; i <= n; ++i) {
        result *= i;
    }

    return (result);
}


int *fact(int arr[], int n)
{
    int *facts = NULL;

    if (!(facts = malloc(n * sizeof(*facts))) {
        // TODO Log (strerror(errno));
        return (NULL);
    }

    for (int i = 0; i < n ; ++i) {
        facts[i] = factorial(arr[i]);
    }

    return facts;
}

【讨论】:

    【解决方案3】:

    以下建议的代码:

    1. 检查 C 库函数错误
    2. 检查用户输入的有效性
    3. 元素数量不允许为负数或 0
    4. 元素值不允许使用负数
    5. 消除了 OP 代码中的“隐式转换”
    6. 消除不必要的堆分配
    7. 消除内存泄漏
    8. 最小化局部变量的“范围”
    9. 使用适当的水平和垂直间距以方便阅读和理解
    10. 遵循公理:每行只有一个语句,并且(最多)每个语句有一个变量声明。

    现在,建议的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    
    // prototypes
    void fact( size_t arr[], int n);
    
    
    void fact( size_t arr[], int n)
    {
    
        for ( int i = 0; i < n; i++)
        {
            if( !arr[i] )
            {
                puts( "factorial for 0 not calculated" );
            }
    
            else
            {
                size_t fac = 1;
                for ( size_t j = arr[i]; j > 1; j-- )
                {
                    fac = fac * j;
                }
                arr[i] = fac;
            }
        }
    }
    
    
    int main( void )
    {
        int n;
    
        printf("How many numbers to calculate factorial for :\t");
        if( scanf("%d", &n) != 1)
        {
            fprintf( stderr, "scanf for number of factorials failed\n" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, scanf successful
    
        if( n < 0 )
        {
            puts( "negative number of elements is invalid" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, scanf for number of elements successful
    
        if( !n )
        {
            puts( "nothing to do" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, valid number of elements
    
        size_t *num = malloc( (size_t)n * sizeof( size_t ) );
        if( !num )
        {
            perror( "malloc failed" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, malloc successful
    
        printf("Enter the elements separated by spaces :\t");
    
        for ( int i = 0; i < n; i++ )
        {
            if( scanf( "%lu", &num[i] ) != 1 )
            {
                fprintf( stderr, "scanf for element failed\n" );
                free( num );
                exit( EXIT_FAILURE );
            }
        }
    
        fact(num, n);
    
        printf("\nFactorials are :\t");
    
        for ( int i = 0; i < n; i++ )
            printf("%lu\t", num[i] );
    
        printf("\n");
    
        // cleanup
        free( num );
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-14
      相关资源
      最近更新 更多