VALUE, INITIALIZE and Condition Names

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an edition 4 is upcoming, in DIS ballot), GnuCOBOL 3.2 (28 July 2023, the latest release) and IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026), and was checked on 2026-10-20. Every portable example below was compiled with cobc -x -Wall (GnuCOBOL 3.2.0, fixed reference format) without a single warning and run; the output and the diagnostics shown are real. The ISO standard is paywalled and was not read: the rules come from the Enterprise COBOL manuals and the GnuCOBOL manual, and where the two differ the page says so.

A COBOL program has no declaration-time default: the storage of a data item holds whatever the runtime or the compiler puts there, unless the programmer says otherwise. Three features cover "otherwise", and they are the subject of this page:

  • the VALUE clause gives a data item an initial value, or, on a level-88 entry, defines a condition-name;

  • the 88 level turns a value, a list of values or a range of values into a name that can be tested (IF BOOK-ACTIVE) and set (SET BOOK-ACTIVE TO TRUE);

  • the INITIALIZE statement sets a whole record, or selected categories of its fields, to values at run time.

The layouts used are those of the Circulation library back office: BOOKREC, MEMBREC and LOANREC, whose level-88 entries (BOOK-ACTIVE, MEMBER-SUSPENDED, LOAN-CHECKOUT) are the flags the nightly run tests. How the data division is organised and what the level numbers mean is covered in Data Division and Level Numbers; the PIC and USAGE clauses that decide what a literal may be are in PICTURE Clause and USAGE and Numeric Representation.

The VALUE clause on data items

In the WORKING-STORAGE SECTION and the LOCAL-STORAGE SECTION a VALUE clause gives a data item its initial contents (Enterprise COBOL Language Reference, VALUE clause). The syntax of this format is only VALUE [IS] literal, where a figurative constant (ZERO, SPACE, HIGH-VALUE, LOW-VALUE, QUOTE, ALL "ab") may replace the literal. Initialization is independent of any BLANK WHEN ZERO or JUSTIFIED clause. The rules that matter in practice:

  • Class of the literal. A numeric item takes a numeric literal, aligned as in a numeric MOVE (decimal point alignment), and the literal must not need truncation of nonzero digits. A literal with a sign needs a signed PICTURE (S). An alphanumeric, alphabetic, alphanumeric-edited or numeric-edited item described with USAGE DISPLAY takes an alphanumeric literal or a figurative constant, aligned left and padded with spaces, and it must not be longer than the item.

  • Editing characters are counted. The functions of the editing characters in a PICTURE are ignored when the initial value is determined, but they count towards the size. Enterprise COBOL’s own example is an item PICTURE +999.99 that must be given VALUE "+012.34", the text exactly as it will be displayed. COBOL 2023 adds a numeric literal for numeric-edited items: the portable form is the alphanumeric literal, and GnuCOBOL 3.2 with -std=ibm -Wall warns about the numeric literal (numeric literal in VALUE clause of numeric-edited item used [-Wdialect]).

  • Groups. VALUE may be coded on a group item. The group area is initialized as one alphanumeric string, without regard to the subordinate items, and the subordinate entries then must not have a VALUE clause of their own. All subordinate items of an alphanumeric group must be USAGE DISPLAY, none may be JUSTIFIED or SYNCHRONIZED, and the literal must not be longer than the group.

  • Tables. A VALUE on an item that is, or is subordinate to, an OCCURS entry gives every occurrence the same value. Different values per occurrence need the FILLER plus REDEFINES idiom, described in Tables and OCCURS. A structure with OCCURS DEPENDING ON is initialized as if it had its maximum number of occurrences.

  • Where it is not allowed. Not on an item that contains or is subordinate to an EXTERNAL or REDEFINES entry, not on an item that follows an OCCURS DEPENDING ON item, and not on external floating-point items. The Language Reference describes the item-level VALUE for WORKING-STORAGE and LOCAL-STORAGE; the contents of a FILE SECTION or LINKAGE SECTION item come from the file or the caller. USAGE POINTER and the function and procedure pointers take only VALUE NULL (see Pointers and Dynamic Storage).

Example: VALUE on elementary items, groups and tables

The program shows counters starting at ZERO, VALUE ALL with a two-character pattern, a literal written on its own line, a group-level VALUE whose subordinate items have none, a table built from FILLER items and REDEFINES, and a VALUE that applies to every occurrence. The last line displays items copied from BOOKREC, which has no VALUE clauses at all.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALUEDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY BOOKREC.
       01  WS-COUNTERS.
           05  WS-READ-COUNT       PIC 9(5)    VALUE ZERO.
           05  WS-REJECT-COUNT     PIC 9(5)    VALUE ZERO.
           05  WS-FINE-TOTAL       PIC S9(7)V99 VALUE ZERO.
       01  WS-BANNER               PIC X(20)   VALUE ALL "=-".
       01  WS-RUN-TITLE            PIC X(24)
               VALUE "CIRCULATION NIGHTLY RUN".
       01  WS-DEFAULT-LOAN-DAYS    PIC 99      VALUE 14.
       01  WS-DEFAULT-STATUS.
           05  WS-DEF-CODE         PIC X       VALUE "A".
           05  WS-DEF-NOTE         PIC X(9)    VALUE "NEW MEMBR".
       01  WS-RUN-DATE             VALUE "20261020".
           05  WS-RUN-YEAR         PIC 9(4).
           05  WS-RUN-MONTH        PIC 99.
           05  WS-RUN-DAY          PIC 99.
       01  WS-DAY-NAMES.
           05  FILLER              PIC X(3)    VALUE "MON".
           05  FILLER              PIC X(3)    VALUE "TUE".
           05  FILLER              PIC X(3)    VALUE "WED".
       01  WS-DAY-TABLE REDEFINES WS-DAY-NAMES.
           05  WS-DAY              PIC X(3) OCCURS 3 TIMES.
       01  WS-LIMITS.
           05  WS-LIMIT            PIC 9(2) OCCURS 3 TIMES VALUE 5.
       PROCEDURE DIVISION.
       MAIN-PARA.
           DISPLAY WS-BANNER
           DISPLAY WS-RUN-TITLE
           DISPLAY "DAYS=" WS-DEFAULT-LOAN-DAYS
           DISPLAY "DEF=" WS-DEFAULT-STATUS
           DISPLAY "RUN DATE=" WS-RUN-YEAR "/" WS-RUN-MONTH "/"
                   WS-RUN-DAY
           DISPLAY "DAY2=" WS-DAY (2)
           DISPLAY "LIMITS=" WS-LIMITS
           DISPLAY "COUNTERS=" WS-COUNTERS
           DISPLAY "COPIES=" BOOK-COPIES " STATUS=[" BOOK-STATUS "]"
           STOP RUN.
=-=-=-=-=-=-=-=-=-=-
CIRCULATION NIGHTLY RUN
DAYS=14
DEF=ANEW MEMBR
RUN DATE=2026/10/20
DAY2=TUE
LIMITS=050505
COUNTERS=0000000000000000000
COPIES=000 STATUS=[ ]

WS-BANNER repeats the pattern =- until the 20 bytes are full. WS-RUN-DATE is a group initialized by one literal, "20261020", which is sliced into the year, month and day items below it, and which is why those items must not carry their own VALUE. WS-DEFAULT-STATUS is the opposite arrangement, a group without a VALUE whose members each have one, so its display ANEW MEMBR is the two members one after the other. WS-DAY (2) is TUE because the three three-letter FILLER items occupy nine contiguous bytes that REDEFINES reads as a table; and LIMITS=050505 shows VALUE 5 applied to all three occurrences of WS-LIMIT.

Pitfalls, with the compiler’s own words

The next program is deliberately wrong. Each line shows one rule from the list above, and the diagnostics are the first line of what cobc -x -Wall printed (the compiler also prints the source lines around each one):

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BADSET.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FOUND-SW             PIC X       VALUE "N".
           88  RECORD-FOUND        VALUE "Y".
       01  WS-MODE                 PIC X       VALUE "A".
           88  MODE-ANY            VALUE "A" "B" "C".
       01  WS-COUNT                PIC 9(3)    VALUE "ABC".
       01  WS-SHORT                PIC X(3)    VALUE "TOO LONG".
       01  WS-AMT                  PIC 9(3)    VALUE 1234.
       01  WS-NEG                  PIC 9(3)    VALUE -5.
       PROCEDURE DIVISION.
       MAIN-PARA.
           SET RECORD-FOUND TO FALSE
           SET MODE-ANY TO TRUE
           DISPLAY WS-MODE
           STOP RUN.
badset.cob:9: warning: numeric value is expected [-Wothers]
badset.cob:10: warning: value size exceeds data size [-Wothers]
badset.cob:10: note: value size is 8 [-Wothers]
badset.cob:11: warning: value size exceeds data size [-Wothers]
badset.cob:12: error: data item not signed
badset.cob: in paragraph 'MAIN-PARA':
badset.cob:15: error: field does not have a FALSE clause

The literal "ABC" in a numeric item and the over-long literals in WS-SHORT and WS-AMT are warnings, not errors: the compiler truncates and carries on, so a run with a warning still writes a wrong initial value. The Enterprise COBOL manual states the same rules without a "truncate and warn" outcome (the literal must not need truncation of nonzero digits and must not be longer than the item), so treat every GnuCOBOL VALUE warning as an error in code that must also compile on z/OS. A negative literal in an unsigned item is a hard error (data item not signed), and SET RECORD-FOUND TO FALSE needs a WHEN SET TO FALSE phrase (next sections).

A second set of mistakes concerns REDEFINES and OCCURS DEPENDING ON:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BAD2.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SCORE                PIC 9(3).
           88  SCORE-BACKWARDS     VALUE 100 THRU 50.
       01  WS-RAW                  PIC X(3)    VALUE "ABC".
       01  WS-NUM REDEFINES WS-RAW PIC 9(3)    VALUE 123.
       01  WS-LIST-HOLDER.
           05  WS-COUNT            PIC 9.
           05  WS-ITEM             PIC X OCCURS 1 TO 5 TIMES
                                   DEPENDING ON WS-COUNT.
           05  WS-AFTER            PIC X       VALUE "X".
       PROCEDURE DIVISION.
           STOP RUN.
bad2.cob:8: warning: initial VALUE clause ignored for REDEFINES item 'WS-NUM' [-Winitial-value]
bad2.cob:11: error: 'WS-ITEM' cannot have OCCURS DEPENDING because of 'WS-AFTER'

A VALUE on WS-NUM is only a warning in GnuCOBOL and the value is ignored; the Enterprise COBOL manual does not allow it at all. A backwards range in a level-88 entry (SCORE-BACKWARDS, below) produced no diagnostic at all. The OCCURS DEPENDING ON item cannot be followed by anything in the same record, which also explains why a VALUE after one is rejected.

COBOL 2002 and later describe a VALUE per occurrence of a table. The Enterprise COBOL 6.5 manual refers to a "table format VALUE clause" in its description of INITIALIZE, but the Language Reference’s VALUE clause page defines only the data-item, condition-name and NULL formats, so this page does not give the syntax. GnuCOBOL 3.2.0 does not accept the form OCCURS 3 TIMES VALUE 5 10 15:

tableval.cob:7: error: syntax error, unexpected .
    5 |        01  WS-LIMITS.
    6 |            05  WS-LIMIT            PIC 9(2) OCCURS 3 TIMES
    7 >                                    VALUE 5 10 15.
    8 |        PROCEDURE DIVISION.
    9 |            DISPLAY WS-LIMITS

Use the FILLER/REDEFINES table or a loop (or INITIALIZE) instead.

What a data item holds when it has no VALUE

On z/OS the answer is in the manual: when no value is specified the contents are unpredictable (undefined), for both WORKING-STORAGE and LOCAL-STORAGE items. A program must not read an item it has not set. INITIALIZE, a MOVE, a READ or a VALUE clause are the ways to set it, and the compiler option INITCHECK (below) warns about the omissions it can see.

GnuCOBOL makes a choice that hides this class of bug. By default (-fdefaultbyte=init, the setting of the default dialect) every WORKING-STORAGE item without a VALUE is initialized according to its picture and usage, as if the compiler had executed INITIALIZE ... ALL TO VALUE THEN TO DEFAULT on it: numeric items hold zero and alphanumeric items hold spaces. That is why BOOK-COPIES shows 000 and BOOK-STATUS a space in the output above, although no VALUE clause was written. The option -fdefaultbyte=value replaces that with one byte, written as a character in quotes or a decimal 0 to 255. With -fdefaultbyte=42 the same program printed COPIES=*** STATUS=[*]. The dialects differ: the configuration files for cobol85, cobol2002 and cobol2014 set defaultbyte to none ("initial storage is undefined"), and with -std=cobol2014 the program printed NUL bytes for the status; with -std=ibm an alphanumeric item without a VALUE also holds a NUL byte (STATUS=[ followed by the byte 00 and ]), where the default dialect holds a space. The manual lists the value none for -fdefaultbyte; on the command line cobc 3.2.0 rejected it with unsupported value 'none' for configuration tag 'defaultbyte'.

The consequence for portable code: never rely on the default. Write VALUE on every flag and counter, as VALUEDEMO does, and INITIALIZE every record before it is built field by field.

When is the initial state restored

Where an item lives decides how often its VALUE is applied (Enterprise COBOL Programming Guide, Comparison of WORKING-STORAGE and LOCAL-STORAGE and Setting a program to an initial state):

Storage Allocated and initialized Persists

WORKING-STORAGE

When the run unit starts. Items with a VALUE get it then.

For the whole run unit, in the last-used state. Exceptions: a program with IS INITIAL (or the INITIAL compiler option) is reinitialized on every call; a dynamically called subprogram is reinitialized on the first reentry after a CANCEL.

LOCAL-STORAGE

On every call or invocation. Items with a VALUE get it each time; others are undefined.

Freed on return. Each thread or recursive level has its own copy.

EXTERNAL items

VALUE is not allowed.

Shared across programs of the run unit.

The next source file contains a main program and two subprograms. COUNTER has one WORKING-STORAGE and one LOCAL-STORAGE counter, both starting at zero; ICOUNT is declared IS INITIAL:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. STORAGE.
       PROCEDURE DIVISION.
       MAIN-PARA.
           CALL "COUNTER" CALL "COUNTER" CALL "COUNTER"
           CALL "ICOUNT" CALL "ICOUNT"
           STOP RUN.
       END PROGRAM STORAGE.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. COUNTER.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CALLS                PIC 9(3)    VALUE ZERO.
       LOCAL-STORAGE SECTION.
       01  LS-CALLS                PIC 9(3)    VALUE ZERO.
       PROCEDURE DIVISION.
           ADD 1 TO WS-CALLS LS-CALLS
           DISPLAY "COUNTER WS=" WS-CALLS " LS=" LS-CALLS
           GOBACK.
       END PROGRAM COUNTER.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ICOUNT IS INITIAL.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CALLS                PIC 9(3)    VALUE ZERO.
       PROCEDURE DIVISION.
           ADD 1 TO WS-CALLS
           DISPLAY "ICOUNT WS=" WS-CALLS
           GOBACK.
       END PROGRAM ICOUNT.
COUNTER WS=001 LS=001
COUNTER WS=002 LS=001
COUNTER WS=003 LS=001
ICOUNT WS=001
ICOUNT WS=001

Three calls to COUNTER count up in WS-CALLS (1, 2, 3) because the WORKING-STORAGE item remembers; LS-CALLS is 1 every time because a fresh copy is made on each call. ICOUNT prints WS=001 twice, because IS INITIAL puts its WORKING-STORAGE back into its initial state at every entry. The Enterprise COBOL manual adds that the INITIAL option has no effect on items without a VALUE clause. Passing the same program through -std=ibm gave identical output. The mechanics of CALL and CANCEL are in Subprograms, CALL and Linkage.

Level-88 condition-names

A condition-name is a level-88 entry that names the true values of the data item immediately above it, its conditional variable. The entry has no PICTURE: it takes the characteristics of the conditional variable, it occupies no storage, and its only clause is VALUE:

88  condition-name  VALUE IS literal-1 [THROUGH literal-2] ...
                    [WHEN SET TO FALSE IS literal-3]

(VALUE or VALUES, IS or ARE, THRU or THROUGH are interchangeable, as Enterprise COBOL puts it.) The rules in Enterprise COBOL’s Rules for condition-name entries:

  • The VALUE clause is required and is the only clause of the entry. Level-88 entries must follow the conditional variable (or another 88 of the same variable) immediately.

  • Each value can be a single literal, a list of literals separated by spaces (commas and semicolons are also separators), a range literal-1 THRU literal-2 where literal-1 must be less than literal-2, or any mixture of these; the literal class must be one the conditional variable can be assigned. Ranges follow the collating sequence for alphanumeric data and are numeric for numeric items.

  • The conditional variable may be any elementary item, or a group item, except another condition-name, a RENAMES (level-66) item, a USAGE INDEX item, or an item with USAGE POINTER, PROCEDURE-POINTER, FUNCTION-POINTER or object reference. For a group the literals must be alphanumeric and no longer than the group; no subordinate item may be JUSTIFIED or SYNCHRONIZED.

  • If the conditional variable is a table element, every reference to the condition-name needs the subscripts or indexes the variable needs.

  • A condition-name is tested like a condition: IF MEMBER-ACTIVE. It is true when the variable equals any listed value or lies in any listed range, and false for every other value, including values that are not valid for the picture.

  • The condition-name may be qualified like any data name (LOAN-CHECKOUT OF LOAN-RECORD).

Example: lists, ranges, subscripts and qualification

The program uses the Circulation flags from the copybooks and adds a status/reason pair, a table of month lengths whose element has two condition-names, a multi-condition SET and a qualified condition-name:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CIRC88.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY LOANREC.
       COPY MEMBREC.
       01  WS-RUN-FLAGS.
           05  WS-VALID-SW         PIC X       VALUE "Y".
               88  LOAN-VALID      VALUE "Y".
               88  LOAN-REJECTED   VALUE "N".
           05  WS-REASON           PIC 99      VALUE ZERO.
               88  NO-REASON       VALUE ZERO.
               88  BAD-MEMBER      VALUE 1 THRU 9.
               88  BAD-BOOK        VALUE 10 THRU 19.
       01  WS-MONTH-TABLE.
           05  WS-MONTH-ENTRY OCCURS 3 TIMES.
               10  WS-DAYS         PIC 99      VALUE 31.
                   88  LONG-MONTH  VALUE 31.
                   88  SHORT-MONTH VALUE 28 THRU 30.
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE "O" TO LOAN-TYPE
           MOVE "S" TO MEMBER-STATUS
           IF LOAN-CHECKOUT AND NOT MEMBER-ACTIVE
              SET LOAN-REJECTED BAD-MEMBER TO TRUE
           END-IF
           DISPLAY "VALID=" WS-VALID-SW " REASON=" WS-REASON
           IF BAD-MEMBER
              DISPLAY "REJECT: MEMBER NOT ACTIVE"
           END-IF
           IF LOAN-CHECKOUT OF LOAN-RECORD
              DISPLAY "QUALIFIED NAME TESTED"
           END-IF
           MOVE 30 TO WS-DAYS (2)
           IF SHORT-MONTH (2) AND LONG-MONTH (1)
              DISPLAY "MONTH 2 IS SHORT, MONTH 1 IS LONG"
           END-IF
           SET LONG-MONTH (2) TO TRUE
           DISPLAY "MONTH 2 NOW " WS-DAYS (2)
           SET LOAN-VALID NO-REASON TO TRUE
           DISPLAY "VALID=" WS-VALID-SW " REASON=" WS-REASON
           STOP RUN.
VALID=N REASON=01
REJECT: MEMBER NOT ACTIVE
QUALIFIED NAME TESTED
MONTH 2 IS SHORT, MONTH 1 IS LONG
MONTH 2 NOW 31
VALID=Y REASON=00

SET LOAN-REJECTED BAD-MEMBER TO TRUE is the same as two separate SET statements in the order given. BAD-MEMBER is the range 1 to 9 and SET stores its first value, 1, which is why REASON=01. LONG-MONTH (1) and SHORT-MONTH (2) are subscripted exactly like the variable WS-DAYS.

Ranges, lists and the FALSE value

The next program tests a score range, a list of letters, a letter range, and the switch idiom with WHEN SET TO FALSE, and it also changes the copybook’s MEMBER-STATUS through SET MEMBER-SUSPENDED TO TRUE:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CONDNAMES.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY MEMBREC.
       01  WS-SCORE                PIC 9(3).
           88  SCORE-FAIL          VALUE 0 THRU 49.
           88  SCORE-PASS          VALUE 50 THRU 100.
       01  WS-CHAR                 PIC X.
           88  CHAR-VOWEL          VALUE "A" "E" "I" "O" "U".
           88  CHAR-DIGIT          VALUE "0" THRU "9".
       01  WS-SWITCHES.
           05  WS-EOF-SW           PIC X       VALUE "N".
               88  END-OF-FILE     VALUE "Y"
                                   WHEN SET TO FALSE "N".
           05  WS-FOUND-SW         PIC X       VALUE "N".
               88  RECORD-FOUND    VALUE "Y".
               88  RECORD-MISSING  VALUE "N".
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE "A" TO MEMBER-STATUS
           IF MEMBER-ACTIVE
              DISPLAY "ACTIVE: " MEMBER-STATUS
           END-IF
           SET MEMBER-SUSPENDED TO TRUE
           DISPLAY "STATUS NOW: " MEMBER-STATUS
           IF NOT MEMBER-ACTIVE
              DISPLAY "NOT ACTIVE"
           END-IF
           MOVE 49 TO WS-SCORE
           IF SCORE-FAIL DISPLAY "49 FAILS" END-IF
           MOVE 50 TO WS-SCORE
           IF SCORE-PASS DISPLAY "50 PASSES" END-IF
           MOVE "E" TO WS-CHAR
           IF CHAR-VOWEL DISPLAY "E IS A VOWEL" END-IF
           MOVE "7" TO WS-CHAR
           IF CHAR-DIGIT DISPLAY "7 IS A DIGIT" END-IF
           SET END-OF-FILE TO TRUE
           DISPLAY "EOF SWITCH: " WS-EOF-SW
           SET END-OF-FILE TO FALSE
           DISPLAY "EOF SWITCH: " WS-EOF-SW
           SET RECORD-FOUND TO TRUE
           DISPLAY "FOUND SWITCH: " WS-FOUND-SW
           SET RECORD-MISSING TO TRUE
           DISPLAY "FOUND SWITCH: " WS-FOUND-SW
           STOP RUN.
ACTIVE: A
STATUS NOW: S
NOT ACTIVE
49 FAILS
50 PASSES
E IS A VOWEL
7 IS A DIGIT
EOF SWITCH: Y
EOF SWITCH: N
FOUND SWITCH: Y
FOUND SWITCH: N

The same output was produced with -std=ibm. END-OF-FILE was defined with VALUE "Y" WHEN SET TO FALSE "N", so SET END-OF-FILE TO FALSE stores N. The WHEN SET TO FALSE literal is only the value SET ... TO FALSE stores, not the only value for which the condition is false: testing END-OF-FILE is false for every value other than Y. The Enterprise COBOL syntax diagram writes it WHEN SET TO FALSE [IS] literal-3 (the manual’s sample code spells it WHEN FALSE IS 99, which does not match the diagram); this page follows the diagram, and cobc 3.2.0 accepted it with and without IS. A second flag, RECORD-FOUND and RECORD-MISSING, shows the alternative that needs no FALSE phrase: two names for the two values, each set with SET ... TO TRUE.

A backwards range such as VALUE 100 THRU 50 is invalid in Enterprise COBOL (literal-1 must be less than literal-2). GnuCOBOL 3.2.0 compiled the program below with -Wall and no diagnostic, and the condition was never true, not even for 100 and 50:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BACK.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SCORE                PIC 9(3).
           88  SCORE-BACKWARDS     VALUE 100 THRU 50.
       PROCEDURE DIVISION.
       MAIN-PARA.
           PERFORM VARYING WS-SCORE FROM 40 BY 30 UNTIL WS-SCORE > 110
              IF SCORE-BACKWARDS
                 DISPLAY WS-SCORE " TRUE"
              ELSE
                 DISPLAY WS-SCORE " FALSE"
              END-IF
           END-PERFORM
           STOP RUN.
040 FALSE
070 FALSE
100 FALSE

SET condition-name TO TRUE or FALSE

Format 4 of the SET statement moves a condition-name’s value to its conditional variable, following the rules of the VALUE clause (Enterprise COBOL Language Reference, SET statement):

SET condition-name-1 [condition-name-2] ... TO TRUE
SET condition-name-1 [condition-name-2] ... TO FALSE
  • TO TRUE stores the condition-name’s value. When there is a list or a range, the first literal is stored: the low end of a THRU range written first, the first literal of a list. Order the literals so that the one that should be set comes first.

  • TO FALSE requires a WHEN SET TO FALSE phrase on the 88; without it the program does not compile (see the diagnostic above: field does not have a FALSE clause).

  • Several condition-names in one SET behave as separate SET statements, left to right.

  • SET MEMBER-SUSPENDED TO TRUE is the safe way to change a status: the program never repeats the code "S", and a change of the stored value touches only the copybook. A MOVE "S" TO MEMBER-STATUS compiles whether or not "S" is a status that exists.

  • SET does not test anything. It stores a value even if it contradicts other fields. The program that sets LOAN-REJECTED is also responsible for clearing NO-REASON.

SET has other formats (indexes, pointers, ADDRESS OF, external switches); the table and pointer ones are in Tables and OCCURS and Pointers and Dynamic Storage. How a condition-name behaves inside IF, EVALUATE ... WHEN condition-name and with NOT is the subject of Conditions, IF and EVALUATE.

INITIALIZE

INITIALIZE sets a record, a group or an elementary item to predetermined values. It is defined as "functionally equivalent to one or more MOVE statements": it behaves as if one implicit MOVE were executed for every elementary item that qualifies, with the sending operand SPACE or ZERO by default, according to the category. The general form (Enterprise COBOL Language Reference, INITIALIZE statement):

INITIALIZE identifier-1 [WITH FILLER]
    [ {ALL | category-name} TO VALUE ]
    [ THEN REPLACING {category-name DATA BY {identifier-2 | literal-1}} ... ]
    [ THEN TO DEFAULT ]

with category-name one of ALPHABETIC, ALPHANUMERIC, ALPHANUMERIC-EDITED, DBCS, EGCS, NATIONAL, NATIONAL-EDITED, NUMERIC, NUMERIC-EDITED or UTF-8. The receiving item is an alphanumeric, national or UTF-8 group, or an elementary item of a category the statement can set, or a special register that is a valid MOVE receiver. A subscripted item may be named, but a whole table is initialized only by naming the group that contains it. The item must not contain a RENAMES clause.

The rules for choosing the items that are set, applied in this order:

  1. Always excluded: items that cannot be MOVE receivers; elementary items that are FILLER (explicit or implicit) unless WITH FILLER is coded; items that are or sit under a REDEFINES or RENAMES. The named identifier may itself be redefined or inside a redefined area.

  2. Possible receivers: the named elementary item, or every elementary item in the named group, and for a table every occurrence.

  3. Actually set: an item is set when at least one of these is true. The VALUE phrase applies and the item’s category is covered and it has a VALUE clause. The REPLACING phrase names the item’s category. THEN TO DEFAULT is present. None of VALUE and REPLACING is coded.

The value stored is the VALUE clause literal for items chosen by VALUE, the REPLACING sender for items chosen by REPLACING, and otherwise SPACE (alphabetic, alphanumeric, alphanumeric-edited, DBCS, national, national-edited, UTF-8) or ZERO (numeric, numeric-edited). A category may appear only once in VALUE and once in REPLACING; ALL in the VALUE phrase stands for all categories at once.

Example: the INITIALIZE forms

The member record below has a numeric id, a name, a fine, a status flag, an unnamed FILLER that has a VALUE, and a trailing note that has none. The program prints the whole group in brackets after every form so that the bytes can be compared. The FILLER is two bytes at offset 24 (6 + 10 + 7 + 1 bytes before it), which the program fills with "XX" by reference modification to show that WITH FILLER clears it.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. INITDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-MEMBER.
           05  WS-M-ID             PIC 9(6)    VALUE 100.
           05  WS-M-NAME           PIC X(10)   VALUE "ADA".
           05  WS-M-FINES          PIC 9(5)V99 VALUE 12.50.
           05  WS-M-STATUS         PIC X       VALUE "A".
           05  FILLER              PIC X(2)    VALUE "ZZ".
           05  WS-M-NOTE           PIC X(4).
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE "TEST" TO WS-M-NOTE
           PERFORM SHOW-IT
           INITIALIZE WS-MEMBER
           DISPLAY "INITIALIZE"
           PERFORM SHOW-IT
           MOVE "XX" TO WS-MEMBER (24:2)
           INITIALIZE WS-MEMBER WITH FILLER
           DISPLAY "WITH FILLER"
           PERFORM SHOW-IT
           INITIALIZE WS-MEMBER
               REPLACING ALPHANUMERIC DATA BY "?"
                         NUMERIC DATA BY 9
           DISPLAY "REPLACING ALPHANUMERIC BY ?, NUMERIC BY 9"
           PERFORM SHOW-IT
           INITIALIZE WS-MEMBER ALL TO VALUE
           DISPLAY "ALL TO VALUE"
           PERFORM SHOW-IT
           INITIALIZE WS-MEMBER
               REPLACING NUMERIC DATA BY 5
               THEN TO DEFAULT
           DISPLAY "REPLACING NUMERIC BY 5 THEN TO DEFAULT"
           PERFORM SHOW-IT
           INITIALIZE WS-MEMBER ALL TO VALUE THEN TO DEFAULT
           DISPLAY "ALL TO VALUE THEN TO DEFAULT"
           PERFORM SHOW-IT
           STOP RUN.
       SHOW-IT.
           DISPLAY "  [" WS-MEMBER "]".
  [000100ADA       0001250AZZTEST]
INITIALIZE
  [000000          0000000 ZZ    ]
WITH FILLER
  [000000          0000000       ]
REPLACING ALPHANUMERIC BY ?, NUMERIC BY 9
  [000009?         0000900?  ?   ]
ALL TO VALUE
  [000100ADA       0001250A  ?   ]
REPLACING NUMERIC BY 5 THEN TO DEFAULT
  [000005          0000500       ]
ALL TO VALUE THEN TO DEFAULT
  [000100ADA       0001250A      ]

Reading the lines:

Form Effect on the member record

INITIALIZE WS-MEMBER

Numeric items to zero, alphanumeric items to spaces, FILLER untouched (ZZ stays), the VALUE clauses ignored.

... WITH FILLER

As above, and the FILLER is cleared too (the XX written there is gone).

... REPLACING ALPHANUMERIC DATA BY "?" NUMERIC DATA BY 9

Only the named categories are set. The id becomes 000009 and the fine 0000900 (the 9 is moved like any numeric literal, so it scales by the implied decimal point), the name "?" padded with spaces. The FILLER stays as it was.

... ALL TO VALUE

Items that have a VALUE clause get it back (100, "ADA", 12.50, "A"). WS-M-NOTE, which has none, is left as it is (?), and the FILLER is not touched.

... REPLACING NUMERIC DATA BY 5 THEN TO DEFAULT

The numeric items take 5. THEN TO DEFAULT sets everything else not already set to SPACE or ZERO, so the name, status and note are blank.

... ALL TO VALUE THEN TO DEFAULT

Every item with a VALUE gets it and every other item gets its default, a complete reset to the "as defined" state. This is the combination Enterprise COBOL uses to describe ALLOCATE ... INITIALIZED and that a comment in GnuCOBOL’s default dialect file gives as the meaning of defaultbyte: init.

The statement is "as if" a series of MOVE statements, so the usual MOVE rules apply to every implicit move: alignment of the numeric literal, truncation, space padding of the alphanumeric literal. This also means that an item such as a COMP-3 field gets a valid packed zero, because the move is done per elementary item. The statement cannot be applied to a variably located or variable-length item (a record after an OCCURS DEPENDING ON item).

What INITIALIZE skips: REDEFINES, tables, pointers

       IDENTIFICATION DIVISION.
       PROGRAM-ID. INITSHAPE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-AREA.
           05  WS-N                PIC 9(3)    VALUE 5.
           05  WS-A                PIC X(3)    VALUE "ABC".
           05  WS-REDEF REDEFINES WS-A PIC 9(3).
           05  WS-TAB OCCURS 3 TIMES.
               10  WS-TN           PIC 99      VALUE 7.
               10  WS-TA           PIC X       VALUE "Z".
           05  WS-I                USAGE INDEX.
           05  WS-P                USAGE POINTER.
       PROCEDURE DIVISION.
       MAIN-PARA.
           INITIALIZE WS-AREA
           DISPLAY "[" WS-N "][" WS-A "][" WS-TAB (1) "]["
                   WS-TAB (3) "]"
           IF WS-P = NULL
              DISPLAY "POINTER IS NULL"
           END-IF
           MOVE "XYZ" TO WS-A
           INITIALIZE WS-REDEF
           DISPLAY "[" WS-A "]"
           INITIALIZE WS-TAB (2)
           STOP RUN.
[000][   ][00 ][00 ]
POINTER IS NULL
[000]

The group WS-AREA holds a number, a text with a REDEFINES view, a three-occurrence table and a pointer. INITIALIZE WS-AREA sets the number to 000, the text to spaces and every occurrence of the table (WS-TAB (1) and WS-TAB (3) both show 00 and a space). The pointer compares equal to NULL afterwards, but that is GnuCOBOL’s default initialization at work: the Enterprise COBOL manual lists no pointer or index category among the receiving items of INITIALIZE, so a pointer is cleared with SET ... TO NULL and an index is set with SET. The redefining item WS-REDEF was skipped, because it is subordinate to a REDEFINES; naming it directly (INITIALIZE WS-REDEF) is allowed, and then it set the bytes of WS-A to 000. The statement also works on a single subscripted occurrence (INITIALIZE WS-TAB (2)), and this program ran with -std=ibm unchanged.

GnuCOBOL 3.2 deviation: the category before TO VALUE

Enterprise COBOL lets ALPHANUMERIC TO VALUE or NUMERIC TO VALUE restore only that category. GnuCOBOL 3.2.0 compiled the statements without warnings and ignored the category: both forms restored every item that has a VALUE clause, as ALL TO VALUE does. The program changes both fields, runs each form, and shows the whole group:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CATVALUE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-MEMBER.
           05  WS-M-ID             PIC 9(6)    VALUE 100.
           05  WS-M-NAME           PIC X(10)   VALUE "ADA".
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE 9 TO WS-M-ID
           MOVE "CHANGED" TO WS-M-NAME
           INITIALIZE WS-MEMBER ALPHANUMERIC TO VALUE
           DISPLAY "[" WS-MEMBER "]"
           MOVE 9 TO WS-M-ID
           MOVE "CHANGED" TO WS-M-NAME
           INITIALIZE WS-MEMBER NUMERIC TO VALUE
           DISPLAY "[" WS-MEMBER "]"
           STOP RUN.
[000100ADA       ]
[000100ADA       ]

Under Enterprise COBOL the first line would show the name restored and the id left as 000009, and the second the id restored and the name CHANGED. Code that must behave the same on both compilers should use ALL TO VALUE or REPLACING, which behave as documented.

INITIALIZE or MOVE

  • INITIALIZE group is the right way to blank a record before building it field by field: it knows each field’s category, so numeric fields get numeric zeros. MOVE SPACES TO group leaves non-numeric spaces in numeric fields, so a later arithmetic statement on such a field is working on invalid data (see MOVE and Data Movement).

  • INITIALIZE is a sequence of moves, one per elementary item. A one-time initial state for a large work area is better expressed with VALUE clauses.

  • A FILLER that must hold a constant (the ZZ in the example) is lost by INITIALIZE WITH FILLER and not touched without it. Keep constants in named items, or restore them after a WITH FILLER initialization.

The INITCHECK option (Enterprise COBOL; z/OS, not run here)

Enterprise COBOL 6.3 added the INITCHECK compiler option, which makes the compiler check at compile time for items used before they are initialized, and issue warning messages. It is off by default (NOINITCHECK; abbreviations IC and NOIC), and INITCHECK alone means INITCHECK(LAX).

Setting Behaviour

NOINITCHECK

No warning for uninitialized items.

INITCHECK(LAX)

Warn when an item is used with no initialization on any path to the statement. An item set on at least one path to the statement raises no warning.

INITCHECK(STRICT)

Warn unless the item is initialized on all paths to the statement.

The manual’s own illustration has two items, Y and Z, with no VALUE. In Circulation terms (z/OS; not run here), with CBL INITCHECK(STRICT) on the first line:

       CBL INITCHECK(STRICT)
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINECHK.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FINE-LIMIT           PIC 9(3).
       01  WS-FINE-DUE             PIC 9(3).
       PROCEDURE DIVISION.
       MAIN-PARA.
           IF WS-FINE-LIMIT > 5
              MOVE 2 TO WS-FINE-DUE
           END-IF
           DISPLAY WS-FINE-DUE
           STOP RUN.

WS-FINE-LIMIT is read before any value is set, so both LAX and STRICT warn about it. WS-FINE-DUE is set on one path only: LAX stays quiet and STRICT warns about the DISPLAY. The exact message numbers are not quoted here because the code was not run. The restrictions in the manual are worth knowing:

  • Only WORKING-STORAGE and LOCAL-STORAGE items are analysed; not LINKAGE or FILE items, nor EXTERNAL or GLOBAL items.

  • Table elements are not tracked individually: setting one element counts as setting all. Items set through a pointer (for example after ADDRESS OF) can be reported wrongly, a reference-modified item counts as initialized, and only the first uninitialized item of a group is reported.

  • Items passed BY REFERENCE are not warned about; BY CONTENT and BY VALUE are.

  • The analysis is purely at compile time and does not change the object program. It is more accurate with OPTIMIZE(1) or OPTIMIZE(2) than with OPT(0), and may lengthen the compile.

GnuCOBOL 3.2.0 has no equivalent. The same two-item program compiled with cobc -x -Wall printed no diagnostic at all and displayed 000, because the default -fdefaultbyte=init quietly provides the zero (see above). A COBOL test that passes under GnuCOBOL therefore does not show that an item is initialized on z/OS.

Where the book is outdated

The book consulted for this section (Taulli, Modern Mainframe Development, 2022) was written around Enterprise COBOL 6.3 and, as a survey of the mainframe, treats the level numbers and the VALUE clause as basics; nothing is copied from it. It does not cover INITIALIZE or the SET statement, and what it could not cover is below:

  • INITCHECK arrived with 6.3, the release the book calls the latest, and the Programming Guide for 6.5 is the place for its suboptions and restrictions; the rest of this page (VALUE with WHEN SET TO FALSE, the INITIALIZE phrases) is not covered by the book as far as the section outline records.

  • COBOL 2023 allows VALUE for numeric-edited items with a numeric literal, while the Enterprise COBOL 6.5 manual still describes the alphanumeric literal with every editing character spelt out; code that must compile on both uses the alphanumeric form.

  • The book’s examples were written with GnuCOBOL (OpenCobolIDE), whose default of initializing every item hides uninitialized-data bugs that show up on z/OS, as demonstrated above.