OCCURS: Tables

COBOL GnuCOBOL 3.2.0 · ✓ verified by execution on 2026-08-16

The OCCURS clause in COBOL allows you to create data structures called tables (arrays), where entries repeat multiple times. This is how you define a collection of identical data items, rather than defining separate variables for each one.

Unlike modern languages like Python or JavaScript, COBOL arrays (tables) are fixed in size and are accessed using a 1-based index (subscript) enclosed in parentheses. Why did COBOL design it this way? Early mainframe memory was extremely constrained and expensive, so knowing the exact size of every data structure at compile-time was essential. By requiring a fixed size, the compiler can guarantee that the memory footprint of your program will never grow unexpectedly at runtime.

Defining a Table

To define a table, you use the OCCURS clause on a data item. You must specify exactly how many times the item will repeat.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. EX1.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 TABLE-TEST.
          05 WS-VAL OCCURS 3 TIMES PIC 99.
       PROCEDURE DIVISION.
           MOVE 10 TO WS-VAL(1)
           MOVE 20 TO WS-VAL(2)
           MOVE 30 TO WS-VAL(3)
           DISPLAY WS-VAL(1) " " WS-VAL(2) " " WS-VAL(3)
           STOP RUN.
Output
10 20 30

Notice two things about this code:

  1. We specify the size using OCCURS 3 TIMES. The word TIMES is optional, but it’s common practice to include it for readability. A reasonable programmer coming from dynamic languages might expect a syntax to append items to a list on the fly. In COBOL, such an intuition fails. If you need a flexible size, you still must declare a maximum limit using a slightly different clause (OCCURS DEPENDING ON), which we will cover in a later lesson.
  2. We access the elements using parentheses: WS-VAL(1), WS-VAL(2), etc.

What does it look like in memory?

When you use the OCCURS clause, COBOL allocates a contiguous block of memory.

10 WS-VAL(1) 20 WS-VAL(2) 30 WS-VAL(3)
A contiguous block of memory for WS-VAL OCCURS 3 TIMES PIC 99.

Every occurrence is exactly the size defined by its PIC clause. Because the memory is contiguous, accessing WS-VAL(2) simply calculates the offset based on the size of the elements. There is no hidden overhead or array pointer objects as you might find in higher-level languages.

Common Misconceptions

One of the most common mistakes is trying to use a 0-based index. In C-like languages, the first element is arr[0]. In COBOL, tables are always 1-indexed. The first element is WS-VAL(1). This stems from COBOL’s design goal of being readable to business professionals: when counting real-world items, humans start at one.

Another rule to remember is that the OCCURS clause cannot be used on an 01-level item. It must be subordinate to an 01-level record. An 01-level is treated as a complete record layout, whereas OCCURS defines repeating fields within a record.

What happens if you try to access an index out of bounds?

Predict the output cobol

Read the code. What exactly will it print? Commit to an answer before you look.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. EX5.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 TABLE-TEST.
          05 WS-VAL OCCURS 3 TIMES PIC 99.
       01 IDX PIC 9 VALUE 1.
       PROCEDURE DIVISION.
           MOVE 42 TO WS-VAL(IDX + 1)
           DISPLAY WS-VAL(2)
           STOP RUN.
Output
42

Wait, that worked perfectly! Subscripts can be numeric literals, identifiers, or even simple arithmetic expressions like IDX + 1. However, if you evaluate to an index greater than the table size, COBOL won’t typically stop you with an “IndexOutOfBoundsException”. Instead, it will silently overwrite adjacent memory depending on the compiler, leading to serious data corruption.

Group Level Tables

The OCCURS clause isn’t limited to elementary items. You can use it at the group level to repeat an entire structure of multiple fields.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. EX2.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 TABLE-TEST.
          05 GRP OCCURS 2 TIMES.
             10 A PIC X.
             10 B PIC X.
       PROCEDURE DIVISION.
           MOVE "1" TO A(1)
           MOVE "2" TO B(1)
           MOVE "3" TO A(2)
           MOVE "4" TO B(2)
           DISPLAY GRP(1) " " GRP(2)
           STOP RUN.
Output
12 34

Here, the entire GRP structure repeats twice. Each occurrence of GRP contains its own A and B. Notice that when you assign to A(1), you are specifying the first occurrence of A within the first GRP.

Multi-dimensional Tables

You can simulate multi-dimensional arrays by nesting OCCURS clauses. However, standard COBOL historically limited nesting to 7 levels deep, though modern compilers like GnuCOBOL are more forgiving.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. EX6.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 TABLE-TEST.
          05 T-ROW OCCURS 2 TIMES.
             10 T-COL OCCURS 2 TIMES PIC 9.
       PROCEDURE DIVISION.
           MOVE 1 TO T-COL(1, 1)
           MOVE 2 TO T-COL(1, 2)
           MOVE 3 TO T-COL(2, 1)
           MOVE 4 TO T-COL(2, 2)
           DISPLAY T-COL(2, 2)
           STOP RUN.
Output
4

When accessing an element in a nested table, you provide the subscripts in order from outer to inner, separated by commas. Each subscript can be a variable or arithmetic expression, providing powerful ways to traverse complex nested data models in business applications.

Check yourself

Which of the following is true regarding COBOL array indexing?

Reveal answer

COBOL arrays are 1-indexed, so the first element is accessed with index 1. — COBOL tables are 1-indexed. The OCCURS clause defines a fixed size, and you use parentheses for subscripts.

At which levels can the OCCURS clause be specified?

Reveal answer

It cannot be specified on an 01, 66, 77, or 88 level item. — OCCURS cannot be used on an 01-level item; it must be subordinate to one. It can be used on group items or elementary items.

What keyword is commonly used (though optional) along with OCCURS to define the size?

Reveal answer

TIMES — The keyword TIMES is optional but commonly used for readability, as in OCCURS 5 TIMES.

Challenges

Challenge 1 +20 XP

Create a table called `SCORES` that repeats 5 times, where each entry is a 2-digit number. Assign the value `99` to the third element and DISPLAY it.

cobol ✓ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real — it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 — expects "99\n"
Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. CHAL1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TEST-RECORD.
   05 SCORES PIC 99 OCCURS 5 TIMES.
PROCEDURE DIVISION.
    MOVE 99 TO SCORES(3)
    DISPLAY SCORES(3)
    STOP RUN.

🐞 Bug Hunt +20 XP

This code tries to display the name and score of the second team, but it fails to compile or outputs the wrong value. Find and fix the bug.

This code runs. It just does the wrong thing. Read it, find the defect, fix it — the tests below decide when you are right.

cobol ✓ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real — it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 — expects "BOB10\n"
Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. CHAL2.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LEAGUE.
   05 TEAM OCCURS 3 TIMES.
      10 P-NAME PIC X(3).
      10 P-SCORE PIC 99.
PROCEDURE DIVISION.
    MOVE "BOB" TO P-NAME(2)
    MOVE 10 TO P-SCORE(2)
    DISPLAY TEAM(2)
    STOP RUN.