COBOL REDEFINES example: one memory area, two views of a date
REDEFINES overlays a second description on the same bytes: a date as text and as three numbers, record variants switched by type code, and a NUMERIC test first.
REDEFINES is COBOL's union: a second data description laid over the same bytes as a first one, so the program can look at one area of memory two ways. The commonest use is a date held as eight characters that a program also needs as year, month and day numbers; the second commonest is a record whose tail means different things depending on a type code. This tutorial does both, then adds the safety check that should accompany every numeric redefinition of text. The listing is desk-checked against the GnuCOBOL 3.2 Programmer's Guide; we could not compile it here, so the output shown is what the documented rules produce. It assumes the record model from group items and elementary items and ends with the class test that belongs in front of any arithmetic on redefined text.
The program
>>SOURCE FORMAT IS FREE
*> cobol-redefines-example.cob — one memory area, two views
*> Dialect: GnuCOBOL 3.x, free format
*> Status : desk-checked against the GnuCOBOL 3.2 Programmer's Guide;
*> NOT compiled (no cobc on the authoring machine)
*> Inputs : none
*> Outputs: DISPLAY lines on the console
*> Build : cobc -x cobol-redefines-example.cob
*> Run : ./cobol-redefines-example (Windows: cobol-redefines-example.exe)
IDENTIFICATION DIVISION.
PROGRAM-ID. REDEFINES-DEMO.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
*> View 1: eight characters of text. View 2: three numbers in the
*> very same eight bytes. Only the subject (Order-Date) has a VALUE.
01 Order-Date PIC X(8) VALUE "20260905".
01 Order-Date-Parts REDEFINES Order-Date.
05 OD-Year PIC 9(4).
05 OD-Month PIC 9(2).
05 OD-Day PIC 9(2).
*> A record whose tail means different things depending on a type
*> code. Both variants redefine TR-Body and are exactly 24 bytes.
01 Transaction-Record.
05 TR-Type PIC X.
88 TR-Is-Payment VALUE "P".
88 TR-Is-Refund VALUE "R".
05 TR-Body PIC X(24).
05 TR-Payment REDEFINES TR-Body.
10 PAY-Account PIC 9(8).
10 PAY-Amount PIC 9(7)V99.
10 PAY-Method PIC X(3).
10 FILLER PIC X(4).
05 TR-Refund REDEFINES TR-Body.
10 REF-Original-Ref PIC X(12).
10 REF-Amount PIC 9(7)V99.
10 REF-Reason PIC X(3).
*> Text that may or may not hold a number: test the X view, then
*> compute with the 9 view.
01 Input-Text PIC X(5) VALUE "00042".
01 Input-Number REDEFINES Input-Text PIC 9(5).
77 Next-Year PIC 9(4).
77 Doubled PIC 9(5).
77 Show-Amount PIC Z(6)9.99.
PROCEDURE DIVISION.
Main-Paragraph.
DISPLAY "Text view : " Order-Date
DISPLAY "Parts view : " OD-Year "/" OD-Month "/" OD-Day
ADD 1 TO OD-Year
DISPLAY "After ADD : " Order-Date
MOVE "20241231" TO Order-Date
COMPUTE Next-Year = OD-Year + 1
DISPLAY "Year after " OD-Year " is " Next-Year
*> Variant 1: a payment.
MOVE "P" TO TR-Type
MOVE SPACES TO TR-Body
MOVE 10442371 TO PAY-Account
MOVE 250.00 TO PAY-Amount
MOVE "CRD" TO PAY-Method
PERFORM Show-Transaction
*> Variant 2: a refund, written through the other view.
MOVE "R" TO TR-Type
MOVE SPACES TO TR-Body
MOVE "INV-2026-118" TO REF-Original-Ref
MOVE 75.50 TO REF-Amount
MOVE "DMG" TO REF-Reason
PERFORM Show-Transaction
*> Class test on the text view before arithmetic on the numeric view.
IF Input-Text IS NUMERIC
COMPUTE Doubled = Input-Number * 2
DISPLAY "Doubled : " Doubled
END-IF
MOVE "4x2" TO Input-Text
IF Input-Text IS NOT NUMERIC
DISPLAY "Not numeric: [" Input-Text "]"
END-IF
STOP RUN.
Show-Transaction.
DISPLAY "Raw : [" Transaction-Record "]"
EVALUATE TRUE
WHEN TR-Is-Payment
MOVE PAY-Amount TO Show-Amount
DISPLAY "Payment : account " PAY-Account
" amount " Show-Amount " via " PAY-Method
WHEN TR-Is-Refund
MOVE REF-Amount TO Show-Amount
DISPLAY "Refund : ref " REF-Original-Ref
" amount " Show-Amount " reason " REF-Reason
WHEN OTHER
DISPLAY "Unknown type " TR-Type
END-EVALUATE.
The rules
Section 6.9.37 of the Programmer's Guide calls the original item the subject and the item carrying the REDEFINES clause the object, and it lists the rules. The two must have the same level number. Nothing at that level, or at a lower (more senior) level, may sit between them, except further REDEFINES of the same subject, which is why TR-Payment and TR-Refund can both follow TR-Body. The sizes must match: "The total allocated size of the subject data item must be the same as the total allocated size of the object data item." Neither may carry an OCCURS at its own level, though either may be a group containing tables. And the object may not have a VALUE clause, nor may anything under it, with one exception the guide states explicitly: level-88 condition names. That is why only Order-Date and Input-Text carry values in the listing; the redefinitions inherit whatever bytes are there.
IBM's language reference for Enterprise COBOL agrees on the level rule and the VALUE prohibition, and adds that "multiple redefinitions of the same storage area are permitted." It is looser on size: for items that are not EXTERNAL, IBM does not require the redefining item to be the same size as the redefined one, only not larger in the external case. GnuCOBOL demands equality, so if you port a copybook where a short variant redefines a long body, pad the short one with FILLER to the full length, as TR-Payment does with its trailing FILLER PIC X(4).
Line by line
The date is the simplest case. Order-Date is eight bytes of text initialised to "20260905". Order-Date-Parts is the same eight bytes described as three unsigned numeric fields. The first two DISPLAYs print the same storage both ways. ADD 1 TO OD-Year then performs numeric arithmetic on bytes 1 to 4, and the text view immediately shows "20270905", because there is only one copy of the data. The MOVE that follows replaces all eight bytes through the text view, and the COMPUTE reads the year back through the numeric view. Nothing is converted at any point; the two names are two interpretations.
The transaction record is the variant-record pattern found in every batch system. Byte 1 is a type code with two condition names on it. Bytes 2 to 25 are TR-Body, a 24-byte area that the payment layout reads as account, amount, method and four bytes of filler, and the refund layout reads as a 12-character reference, an amount and a reason code. Both layouts are exactly 24 bytes, so the size rule is satisfied. The program clears the body with MOVE SPACES TO TR-Body, fills it through one layout, and then Show-Transaction prints the raw bytes and picks the layout to decode with EVALUATE TRUE. Section 7.8.15 describes EVALUATE as a way of "defining processing that should take place under any number of mutually-exclusive conditions"; with TRUE as the subject, each WHEN is a condition, and here each condition is one of the 88 names on the type code. The lesson in that paragraph is that the type code, not the layout, is the source of truth: the program decodes only through the view the code says is valid.
The third example is the safety habit. Input-Text is five characters that might contain digits. Input-Number is the same five bytes as PIC 9(5). Doing arithmetic through the numeric view when the bytes are not digits gives undefined results, so the program tests the text view first with a class condition. Section 2.2.5.2 says the NUMERIC test "considers only the characters '0', '1', ... , '9' to be numeric; only a data item containing nothing but digits will pass." IBM's class condition page says the same for unsigned items. When the field holds "4x2 " the test fails and the program says so instead of computing garbage.
Byte map of the transaction record
| Bytes | Payment view | Refund view |
|---|---|---|
| 1 | TR-Type "P" | TR-Type "R" |
| 2 to 9 | PAY-Account 9(8) | REF-Original-Ref X(12), bytes 2 to 13 |
| 10 to 18 | PAY-Amount 9(7)V99 | REF-Amount 9(7)V99, bytes 14 to 22 |
| 19 to 21 | PAY-Method X(3) | REF-Reason X(3), bytes 23 to 25 |
| 22 to 25 | FILLER X(4) | (covered above) |
The boundaries do not line up between the two views, and they do not have to. Only the total length must agree.
Compile and run
cobc -x cobol-redefines-example.cob
./cobol-redefines-example
The first line of the file is a >>SOURCE FORMAT IS FREE directive in column 8, so it compiles in cobc's default fixed mode or with -free.
Worked example
Expected output, derived by hand from the rules above (the COBOL was not run):
Text view : 20260905
Parts view : 2026/09/05
After ADD : 20270905
Year after 2024 is 2025
Raw : [P10442371000025000CRD ]
Payment : account 10442371 amount 250.00 via CRD
Raw : [RINV-2026-118000007550DMG]
Refund : ref INV-2026-118 amount 75.50 reason DMG
Doubled : 00084
Not numeric: [4x2 ]
Look at the two raw lines. The payment amount 250.00 is stored as the nine digits "000025000" because the V occupies no byte, and the refund amount 75.50 is "000007550". The reference "INV-2026-118" is twelve characters exactly, so it fills its field with no padding. Doubled prints as "00084" because it is an unedited five-digit field and DISPLAY shows the stored digits.
Limitations
REDEFINES describes bytes, so everything depends on the storage format of the redefined data. Overlaying a PIC 9(4) on four text bytes works because USAGE DISPLAY stores one digit per byte; overlaying a COMP or COMP-3 item on text does not give you digits, and reading a binary field through a text view gives you the raw binary bytes. The NUMERIC test also has a subtlety for signed fields: IBM's rule is that an item whose PICTURE has an S is numeric only if a valid operational sign is present, so a signed redefinition of plain text will fail the test even when the text is all digits; keep the numeric view unsigned, as this program does, unless the data really carries a sign. Finally, GnuCOBOL's equal-size requirement means an IBM copybook with a shorter redefining item needs FILLER padding before it compiles here; the compiler reports the mismatch, so this is a nuisance rather than a silent problem.
Index terms cobol, gnucobol, redefines, record variants, class condition