Subroutines in Popular Programming Languages

When I write programs, one thing matters more than most: clarity. That’s where subroutines come into play. They allow me to structure code into reusable blocks. But using them correctly means knowing how to pass data in—and how to get results out. This is where many beginners get stuck. So in this article, I’ll walk you through how subroutines are done in several real-world programming languages.

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.

Credits: Photo by Christina Morillo from Pexels

Read more about UML

Simple UML Modeling Relationships (Modeling Relationships)

Heuristics for Determining Data Types

Understanding UML Data Types: Simplifying Complex Concepts

Heuristics for Determining Attributes

What Are UML Class Attributes? A Quick Guide
Read more about Jira and How to

Use Shortcuts in Jira to Boost Your Productivity

Create a Project in Jira

Create a Jira Issue: A Step-by-Step Guide

Access Confluence and Jira for free

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top
WordPress Cookie Plugin by Real Cookie Banner