【问题标题】:Are there subroutines in C similar to the ones in Fortran?C 中是否有类似于 Fortran 中的子程序?
【发布时间】:2016-06-02 15:06:22
【问题描述】:

长期以来,我一直在使用 Fortran,最近我决定学习 C。虽然我了解 C 中的函数是如何工作的,但我很难找到有关如何在 C 中实现与 Fortran 子例程等效的信息的信息。这个概念是否存在完全用 C 语言?

考虑一下这个 Fortran 代码:

module myMod
  implicit none
  contains

    function func(a, b)
      integer :: func
      integer, intent(IN) :: a, b
      func = a + b
    end function func

    subroutine sub(a, b, c)
      integer, intent(INOUT) :: a, b
      integer, intent(OUT) :: c
      a = a * b
      b = 3 * a
      c = a + b
    end subroutine sub

end module myMod

program example
  use myMod
  implicit none
  integer :: i, j, k

  i = 1
  j = 2

  k = func(i,j)
  print*, k

  call sub(i,j,k)
  print*, i, j, k

end program example

模块 myMod 中的函数翻译成 C 语言是微不足道的,但是子例程呢?有没有办法做类似的事情?


编辑:例如,下面的代码没有输出我所期望的:

#include <stdio.h>

void subroutine(int num1, int num2) {
    num1 -= 1;
    num2 *= 2;
    printf("this has been executed! \n");
}

int main () {

    int a = 10;
    int b = 10;

    subroutine(a,b);
    printf( "a = %d, b = %d \n", a, b);

    return 0;
}

它输出:

this has been executed!
a = 10, b = 10 

而不是预期的:

this has been executed!
a = 9, b = 20 

那么为什么不像 Fortran 子例程那样在该 void 函数中修改变量 a 和 b?

【问题讨论】:

  • 如果我理解正确的话,子程序和函数的区别是(至少在这个例子中),子程序可以返回多个值。在 C 语言中,您将为此使用指针,只需将指向 i,j,k 的指针传递给函数,函数就可以通过指针访问它们的内存位置来更改这些变量的值。
  • Fortran 中的子程序可以改变主程序中变量的值。请参阅我上面的更新以了解问题
  • 我决定学习 C。”在您的 C 入门中继续阅读“address-of”运算符 (&amp;) 和“de -reference"-operator (*),以及一般的指针。
  • @alk 好吧,我刚开始接触 C,当我遇到一般函数时,我意识到没有什么可以立即类似于 Fortran 的子例程,因此我提出了问题。
  • 您的问题是关于 C 的,但我想我要指出的是,在 C++ 中,您可以通过引用传递参数,例如 Fortran。我不认为它有intent(OUT) 的等价物,但它确实有intent(IN)intent(INOUT) 的等价物。在 C++ 中,您可以将子例程声明为 void subroutine(int &amp;a, int &amp;b)

标签: c fortran


【解决方案1】:

您可以将函数定义为返回类型为void,这意味着它不返回任何内容。

返回类型为void 的函数可以使用没有值的return 关键字,但不需要使用return

你可以把上面的子程序翻译成如下:

void sub(int *a, int *b, int *c)
{
    *a = (*a) * (*b);
    *b = 3 * (*a);
    *c = (*a) + (*b);
    printf("a=%d, b=%d, c=%d\n", *a, *b, *c);
}

然后这样称呼它:

sub(&i, &j, &k);

在 C 中,所有变量都是按值传递的,因此修改函数中的参数不会反映在调用者中。

由于您要修改传递给函数的值,因此您可以使用&amp; 运算符传入要修改的每个变量的地址,从而为您提供指向每个变量的指针。然后您取消引用带有一元 * 运算符的指针以读取/写入它指向的值。

【讨论】:

  • 嗯,所以这似乎是我所缺少的。 *&lt;variable&gt; 到底是什么意思?为什么我要使用&amp;&lt;variable&gt; 调用子程序?
  • 我还更新了我的 Fortran 代码以使示例更明显,从子例程中删除 print*, 并将其添加到主程序中。我想这与在上面的代码中移动printf 相同
  • @gilberto.agostinho.f :需要通过引用传递参数。
  • @sjsam:我不会在 C 的上下文中使用“pass by reference”这个短语,因为 C 是 always“pass by值”(相对于 C++)。
  • 现在一切都非常清楚了,一旦我到达那里,我会阅读更多关于 &amp;* 的信息,谢谢大家
【解决方案2】:

函数不需要返回值 - FORTRAN 子例程的 c 替代方法是将函数声明为返回类型为 void

void function(arguments){
...
}

这有效地使函数成为一个子例程 - 前提是通过引用将值传递给函数,以便函数可以更改其范围之外的变量。

在您的情况下,将函数声明为

void subroutine(int* num1, int* num2) { 
    /* num1 stores the address of a
     * and *num1 gives the value stored in that address
     */
    *num1 -= 1;
    *num2 *= 2;
    printf("this has been executed! \n");
}

并将其称为:

subroutine(&a,&b); // &a means the address of a

【讨论】:

    【解决方案3】:

    在某些情况下,您可以使用 C 预处理器来定义行为类似于子例程的宏。例如:

    #define subroutine(X,Y) do{ \
      X -= 1; \
      Y *= 2; \
      printf("this has been executed!\n"); \
    } while(0)
    
    int main(int argc, char**argv)
    {
      int a = 10;
      int b = 10;
    
      subroutine(a, b);
      printf("a = %d, b = %d\n", a,b);
    }
    

    结果:

    this has been executed!
    a = 9, b = 20
    

    C 宏是文字文本替换。外部do...while(0) 是一个非常标准的“技巧”,用于制作像函数一样“可调用”的多语句宏,因为while(0) 将吞噬尾随; 并被任何值得使用的编译器优化.尾随 \ 字符允许您定义多行宏。

    GCC 的另一个鲜为人知(并且很少使用)的特性(Gnu 扩展,而不是标准 C)在某些情况下可能会给您带来您想要的效果,那就是您可以在函数内部定义一个函数,然后内部函数可以访问外部函数的局部范围变量。

    Gnu C Reference manual sections on nested functions

    例如:

    #include <stdio.h>
    
    int main(int argc, char**argv)
    {
      int a = 10;
      int b = 10;
    
      void subroutine(){
        a -= 1;
        b *= 2;
        printf("this has been executed!\n");
      }
    
      subroutine();
      printf("a = %d, b = %d\n", a,b);
      subroutine();
      printf("a = %d, b = %d\n", a,b);
      subroutine();
      printf("a = %d, b = %d\n", a,b);
    }
    

    输出结果:

    this has been executed!
    a = 9, b = 20
    this has been executed!
    a = 8, b = 40
    this has been executed!
    a = 7, b = 80
    

    这两种技术都有很多缺陷,并且通常不属于 C 中的“最佳实践”领域,但其中任何一种都可以成为尝试将代码(和您的想法)从 fortran 逐步迁移到 C 的强大工具。

    【讨论】:

    • 这些很有趣!当然,在您的第二个示例中,变量 a 和 b 将被固定,因此调用 subroutine(); 将始终影响这两个特定的变量,而不是 Fortran 中的子例程。
    • 更正第一个示例中修复的变量。您可以像任何其他函数一样对其进行参数化,但随后您又回到了 dbush 的答案,因为 C 没有 real 传递引用。宏观方法可能更有用。
    • 标准 C 不支持嵌套函数。
    • @IanAbbott - 看来你是对的,先生。这是一个 GCC 扩展,我想这是它很少使用的更多原因。更新答案以反映这一点。
    猜你喜欢
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多