C从函数传递双指针

时间:2014-10-30 12:55:00

标签: c function pointers double

我正在处理一个非功课问题,无论我尝试什么,我都无法解决。

问题是来自Project Euler的问题,即涉及求解甚至Fibonacci数并将它们加在一起,我选择这个作为一个机会来学习更多关于函数,指针和使用大数字的机会,最好不要复制他们的价值,而是传递一个记忆地址。

我目前有以下内容:

/*Second attempt at fibo 4mil
problem.*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>

//MAX is 20 for testing reasons
//Actual value is MAX 0X3D0900
#define MAX 20 

//Function will accept even fibo numbers
//then sum them together for output later
void evenCount (double* evenPoint);

int main(void){
    double firstVar = 0;
    double secondVar = 1;
    double thirdVar;
    double modVar = 2;
    double sumVar;
    double count;

    for(count = 0; count < MAX; count++){

        thirdVar = firstVar + secondVar;
        secondVar = firstVar;
        firstVar = thirdVar;
        if(fmod(firstVar, modVar) == 0){
            evenCount(&firstVar);
        }
        printf("Currently: %.2f\n", firstVar);      
    }
    sumVar = &evenCount();

    printf("Final even sum is: %f\n", sumVar);

    return 0;
}

void evenCount (double* evenPoint){
    double tempOne, tempTwo, tempThree;

    tempOne = *evenPoint;

    tempThree = tempOne + tempTwo;
    tempTwo = tempOne;
    tempOne = tempThree;

    evenPoint = &tempOne;
}

我无法确定来自main()的数据是否正确传递给evenCount函数以便对它们进行求和并将其值更新为在{{1}的末尾打印}。

我的问题是:

  1. 我是否需要main()中的第二个双指针来传递最终值,或者我可以引用一个值,在它循环时更新它?
  2. evenCount是否需要一个指针,以便指针可以引用main()指针?
  3. 我真的很感激任何帮助,因为我已经购买了Safari在线订阅,坐在我旁边的“C A参考手册”但我无法理解这一点。另外我读了这个问题并且它回答了我的问题,但是这个人正在使用多个函数原型。

    too few arguments to function and can't be used as a function---- beginning C

    感谢任何看起来像

    的人

3 个答案:

答案 0 :(得分:1)

我并不完全清楚evenCount()函数应该做什么。

事实上你是以错误的方式调用它 - sumVar = &evenCount();甚至两次错误,因为它缺少一个参数 &没有意义 - 并且它不会做你想要的。

我们来看看:

void evenCount (double* evenPoint){
    double tempOne, tempTwo, tempThree;

这里定义了三个自动变量,但它们还没有值。

    tempOne = *evenPoint;

    tempThree = tempOne + tempTwo;

您希望tempTwo在这里做什么?

    tempTwo = tempOne;
    tempOne = tempThree;

    evenPoint = &tempOne;

可能在这里表示*evenPoint = tempOne,但我不确定。

}

我想你想要一种根据斐波那契数字做出“步骤”的方法。让我们看看:

为了创建“下一个”Fib数,您需要前两个并将它们加在一起。所以“步骤”可以在像

这样的函数中完成
void fibStep(double * curr, double *prev) {
    double new = *curr + *prev;
    *prev = *curr;
    *curr = new;
}

然后

int main(void){
    double firstVar = 0;
    double secondVar = 1;
    double sumVar = 0;
    int count; // no need to have this as a double...

    for(count = 0; count < MAX; count++){

        fibStep(&secondVar, &firstVar);

        if(fmod(secondVar, 2) == 0){
            sumVar += secondVar);
        }

        printf("Currently: %.2f\n", secondVar);

    }

    printf("Final even sum is: %f\n", sumVar);
    return 0;
}

答案 1 :(得分:0)

传递值是正确的,但您不能evenPoint = &tempOne;从函数返回值。这样做有两个问题:第一个是C只支持按值传递,所以当你传递一个指针时,你实际上正在为被调用者创建一个指针的副本。调用者可以看到对数据指向的任何修改,但不会修改指针参数本身。当您修改指针参数时,实际上是在修改调用者无权访问的堆栈变量。

What's the difference between passing by reference vs. passing by value?

您可以通过以下方式更改代码:

void evenCount (double** evenPoint){
    double tempOne, tempTwo, tempThree;

    tempOne = **evenPoint;

    tempThree = tempOne + tempTwo;
    tempTwo = tempOne;
    tempOne = tempThree;

    *evenPoint = &tempOne;

}

但这意味着* evenPoint指向堆栈上分配的变量,恰好位于evenCount的帧上。当evenCount返回时,框架会弹出stack。在该变量位于堆栈之外后访问该变量将导致未定义的行为。 请考虑以下示例,在使用evenPoint 之前,在evenCount之后调用另一个函数A()。一个()函数框架将被放置在内存中与evenCount框架所在的位置相同的位置,并且其局部变量可能会覆盖evenPoint的值。当您随后阅读evenPoint时,您会发现其值已更改。

C++ Returning reference to local variable

最后,你读取变量tempTwo这是一个未初始化的自动变量,所以你最终会读垃圾。

答案 2 :(得分:0)

我不是相当确定evenCount的意图是什么。 编辑:见下文。

根据您对问题的描述,您似乎可以这样做:

int isEven(unsigned int number)
{
    return !(number%2);
}

int main()
{
    unsigned int first = 1, second = 1, next, sum = 0;

    //we already have the first two numbers so start at 2
    for(count = 2; count < MAX; count++) 
    {
        next = first+second;
        first = second;
        second = next;

        //we know the starting values are odd (1 & 1) and won't need to be summed so we can test the new value - 
        if (isEven(second))  //if even (no remainder when dividing by 2)
        {   sum+=first;}
    }
    printf("Final even sum is: %f\n", sum);
}

注意,此处(尚未)double不需要。 The sum (at n=20) is still far too low to exceed what int can store.(虽然此时它正在快速增长)

至于你的实际问题:

注意:当你不需要指针时,建议你不要使用指针,因为你要做的就是让代码比需要的更复杂

  

我是否需要在evenCount中使用第二个双指针来传递最终值?

如果该函数是为了跟踪总和,那么我会这样做:

unsigned int evenSum(unsigned int num = 0)
{
    static unsigned int sum = 0;    //initialised on first use of function. Value is retained between function calls.
    //we test for even here - no longer need to test in calling code 
    // - making the algorithm simpler
    if (isEven(num))
        sum += num;
    return sum;
}
然后可以像这样调用

//adding values:
evenSum( new_value );
//retrieving sum
sum = evenSum();
//or do both:
sum = evenSum( new_value );

如果你想存储'本地'的总和(即作为main中的变量,但在evenSum()函数中修改它),那么是的,你需要将它传递给函数指针:

void evenSum(unsigned int num, unsigned int * sum)
{
    if (isEven(num))
        *sum += num;
}

它将被称为:

sum = 0;
num = 56;
evenSum(num, &sum); //sum is now sum+num

当您传递sum的地址时,当函数取消引用它时,它会修改值而不是副本。传入的数字不需要作为指针传入,因为它是一个(在这里忘记了正确的单词,但它意味着'基本')类型,并且按值传递它实际上略微更高效在运行时它可以只加载到寄存器中,而不依赖于内存查找。此外,它更容易阅读。

  

main()是否需要一个指针,以便指针可以引用evenCount指针?

说实话,我不是百分之百确定你在这里问的是什么。

如果你问:

  

我是否需要在main()中存储指向总和的指针才能传入evenSum()函数?

然后没有。

您可以使用“地址”运算符&将指针传递给'thing'。我在上面的答案的第二个调用示例中使用了它:

unsigned int * sum_pointer = &sum;//    <--- not needed
evenSum(num, &sum); //sum is now sum+num
              ^
              This passes a pointer to sum

编辑:再次查看您的代码,evenCount是否意味着找到下一个fibonachi号码?

如果是这样,你可以这样做:

void next_fib(unsigned int *previous, unsigned int *current)
{
    unsigned int next = *previous+*current;
    *previous = *current;
    *current = next;
}

你会这样称呼:

unsigned int val1 = 1, val2 = 1;
next_fib(&val1, &val2);        //val2 is now the 3rd fib. #
next_fib(&val1, &val2);        //val2 is now the 4th fib. #

要将此代码添加到上面的代码中,程序将变为:

int isEven(double number)
{
    return !(number%2);
}

unsigned int evenSum(double num = 0)
{
    static double sum = 0;
    //we test for even here - no longer need to test in calling code 
    // - making the algorithm simpler
    if (isEven(num))
        sum += num;
    return sum;
}

void next_fib(unsigned int *previous, unsigned int *current)
{
    unsigned int next = *previous+*current;
    *previous = *current;
    *current = next;
}

int main()
{
    unsigned int first = 1, second = 1;
    //we already have the first two numbers so start at 2
    for(count = 2; count < MAX; count++) 
    {
        next_fib(&first, &second);
        evenSum(second);
    }
    printf("Final even sum is: %f\n", evenSum());
}

编辑2:

阅读完您的编辑内容和一些评论后,再看一下actual task,您就错误地解释了这个问题。
该问题要求在fibbonachi序列中所有偶数的总和,其中数量小于4×10 6 。值得庆幸的是,比直到4x10 6 的所有偶数Fibonacci数的总和更容易和更快。

显然,我们需要改变算法。幸运的是,我们已经将主要部分拆分为函数,所以它非常简单,主要只是循环的变化,尽管我做了一些更改:

bool isEven(unsigned long number)
{
    return !(number%2);
}

void next_even_fib(unsigned long *previous, unsigned long *current)
{
    do
    {
        unsigned int next = *previous+*current;
        *previous = *current;
        *current = next;
    } while (!isEven( *current));
    //we could just do 3 passes here without needing the isEven() function
    //as the Fibonacci sequence is always O,O,E,O,O,E...
    //but this is a more general form
}

int main()
{
    //as there is less constraint on knowing which term we are on, we can skip straight to the first even number.
    unsigned long first = 1, second = 2;
    unsigned long sum = 0;
    do
    {
        //with the sum calculation first, we can break out of the loop before the sum
        //we've changed the algorithm so when we get here, second is always even
        sum += second;
        next_even_fib(&first, &second);
    } while (second < 4000000);
    printf("Final even sum is: %d\n", sum);
}

请注意,我将类型更改为unsigned long。这仍然是一个整数值,但保证long(: - P)足以存储我们需要的数字。