Subroutines in Popular Programming Languages

When I write programs, clarity is everything — and that’s where subroutines shine. They let me organize code into reusable, logical blocks. But to use them effectively, you must understand how to pass data in and retrieve results out. This can be tricky for beginners. In this article, I’ll explain how to work with subroutines across languages, showing real-world examples that reveal how different programming languages handle subroutines in practice.

Why Subroutines Need Inputs and Outputs

Let’s start with the basics. A subroutine—also called a function or method—isn’t useful unless I can feed it data. I also expect a result in return. That’s why every subroutine has a way to receive parameters and, optionally, return a value.

There are two main techniques:

  1. Register-based – used in low-level systems for speed.
  2. Stack-based – used in higher-level programming for flexibility.

Both approaches serve the same purpose: they let the subroutine perform its job using the right information and return the correct result.

Subroutines across popoular languages

C and C++: Power with Precision

In C and C++, call subroutines functions. They are defined with a return type, a name, and a list of parameters.

int add(int a, int b) {
return a + b;
}

By default, parameters are passed by value. If I want to pass something by reference—so the function can modify the original value—I use pointers in C or references in C++.

C and C++ use the stack to store return addresses and function frames. Libraries like <math.h> or <iostream> are full of subroutines I can call without writing them myself.


Python: Flexibility Without the Fuss

In Python, I use the def keyword to define a subroutine. I can pass arguments in many ways: positionally, by name, with default values, or even arbitrarily using *args and **kwargs.

def greet(name="world"):
return f"Hello, {name}"

The return statement sends the result back. Python handles parameter passing under the hood using references. Its standard library includes countless built-in subroutines like len(), open(), and sum().

Java: Object-Oriented and Strict

Java enforces structure. All subroutines must exist inside a class. These methods can accept parameters and return values.

public int multiply(int x, int y) {
return x * y;
}

Java always passes by value—but for objects, that means the reference is copied, not the object itself. This can cause confusion, so I stay alert.

Java libraries like java.util or java.lang provide extensive sets of ready-to-use subroutines.

Assembly: Manual but Powerful

In assembly, everything is manual. I pass data using CPU registers or the stack.

A typical subroutine call involves:

  • Saving the return address using CALL.
  • Passing parameters through registers or pushing them to the stack.
  • Returning using RET.

This method gives me full control, but I have to manage the memory and flow myself. Efficient and precise—but unforgiving if I make a mistake.


JavaScript: Dynamic and First-Class

JavaScript treats functions as first-class citizens. I can store them in variables, pass them around, and return them from other functions.

function square(n) {
return n * n;
}

JavaScript lets me pass any type of data—numbers, strings, arrays, even other functions. Libraries like Lodash and jQuery are packed with reusable subroutines that simplify frontend work.

Rust: Safety Meets Performance

In Rust, functions work just like in C or Python but with extra rules to ensure memory safety.

fn subtract(a: i32, b: i32) -> i32 {
a - b
}

Parameters are passed by value by default, but I can use references (&) for borrowing and mut for mutable references. This makes Rust both safe and fast.

The std library offers efficient subroutines for common tasks like string manipulation, file I/O, and data structures.

Go (Golang): Simple and Efficient

Go emphasizes clarity. I define subroutines with the func keyword. Parameters are always passed by value, but I can use pointers to simulate pass-by-reference.

func add(a int, b int) int {
return a + b
}

Go’s libraries—like fmt, math, and net/http—include many prebuilt subroutines that make web servers, formatting, and data handling a breeze.

C# and .NET: Structured and Powerful

In C#, methods live inside classes or structs. Parameters can be passed by value, by reference (ref), or as output-only (out). I can also use default and named parameters.

public int Divide(int x, int y) {
return x / y;
}

C# methods return values using return, and I can define them to return anything from a simple integer to complex objects. .NET libraries like System.IO or System.Text give me robust subroutines for everything from file handling to text encoding.

Other Languages Worth Mentioning

Let me not forget a few more:

  • MATLAB / Octave: Subroutines (called functions) return one or more results using [out1, out2] = func(inputs).
  • R: Everything is a function; arguments can be named, and return values are automatic.
  • Fortran: Supports FUNCTION and SUBROUTINE constructs with explicit parameter passing.
  • Pascal: Uses procedure and function for subroutines, with parameters passed by value or by reference.
  • Haskell: In a functional style, all operations are expressions (subroutines). Input and output are passed through function chaining.

Are Subroutines and Functions the Same?

This is a question I get all the time: Are subroutines and functions the same thing? The short answer is yes, but also no. Let me explain. In many contexts, the terms subroutine and function are used interchangeably. Both refer to a named block of code that can be called from another part of a program to perform a task.

However, there are subtle differences—mainly in terminology and usage across languages:

  • Subroutines usually emphasize the action being taken. They may or may not return a value. For example, in Fortran and Pascal, subroutines are defined to perform actions without necessarily returning results.
  • Functions, on the other hand, always return a value. That’s why languages like Python, C, or JavaScript only use the term “function”—because a return value is expected.

Final Thoughts

Subroutines are the heart of clean, modular code. But understanding how to pass values in and get results out is where their true power lies. Whether I’m coding in C, Python, Java, or Go, I always rely on parameter handling and return logic to make my subroutines do the right thing at the right time.

Every language offers a slightly different take. Some use strict typing. Others allow more flexibility. But they all follow the same principle: input goes in, work happens, and output comes out.

Now that you’ve seen the big picture, try writing subroutines in a few of these languages. You’ll not only get better at programming—you’ll also gain a deeper understanding of how your code really works.

What’s Next?!

Now that you’ve explored how subroutines work across languages and make code more efficient, it’s time to look at another core part of computing — memory. In my next article, “Computer Memory: A Clear and Simple Guide,” I’ll explain how data is stored, accessed, and managed inside your computer. Join me to discover how understanding computer memory helps you write faster, smarter, and more reliable programs.

Credits: Photo by Christina Morillo from Pexels


Scroll to Top
WordPress Cookie Plugin by Real Cookie Banner