Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
I originally wrote this article for a print magazine in 1986. I was 23 and had been doing COBOL for four years, originally on punched cards. Iâd started hearing about C and taught it to myself and rewrote a bit of COBOL into C (note, C is a terrible replacement for COBOL) and I wrote this as a primer to other COBOL programmers to get a taste of C. I updated the intro about 10 years ago for a blog post, but in the past 2 weeks Iâve ended up sharing it with a half dozen people who had no understanding of COBOL, simply so I could describe COBOL record structures to them, which I feel are quite powerful and not represented as well in most modern languages. This plays into a new language that Iâm currently designing, so enjoy this blast from the past and learn about what your Grandparents were doing âback in the dayâ :).
As hard as it is for the young programmer to believe, at least 65% of the world's code is still written in COBOL, with 5 Billion new lines a year being written. COBOL applications process 200 times more data per day than Google does. They donât even teach this in school anymore, and it is a shame because COBOL is a fantastic language for business, much better than anything to have existed with the possible exception of Python. This is because of its robust record structure and clean string variables and conditional logic. While this article is primarily meant for COBOL programmers to understand C, you can easily reverse the examples. At some point that old COBOL code is going to have to be rewritten or maintained, one reason it is still there and needs so little hand holding because of how stable and clean COBOLÂ is.
Remember that C and COBOL come from completely different roots, this is what makes comparisons very difficult.
Preprocessor
COBOL gives us a couple of options for sharing previously defined record layouts and variable declarations. The easiest and most straightforward is the $INCLUDE âfilenameâ. This will take a file and copy it into the source file at compile time. This works just fine if you arenât worried about modifying any of the statements and is analogous to the to the âimportâ in Python or the âincludeâ in C/C++/etc.
The more versatile option is to use COBEDIT and maintain a COPYLIB. The advantage to using the COPY command to retrieve members from a COPYLIB is that you can do dynamic text replacement. So if you want to define your customer master data set twice, once for the data set and once for a file, you could have set up a prefix that in the COPYLIB that you could change at compile time by using the command;
COPY textname REPLACING ==pseudo-text-1== BY ==pseudo-text-2==.
Thus eliminating the need to maintain two different definitions in your COPYLIB.
While C does have an âincludeâ statement, it doesnât have an equivalent to the COPY statement. However, by using the â#ifâ, â#ifdefâ, â#elifâ and â#ifndefâ commands you can control what files get included at compile time. Say for example you are coding some sort of portable application and you have defined different file I/O functions in different files to accommodate your different platforms. You could control it very easily with the following construct;
#define SYS "Windows"#if SYS == "Windows"#include "windows.h"#elif SYS == "MacOS"#include "macos.h"#elif SYS == "Linux"#include "linux.h"#endif
There are several important things here to learn; First off the #define command is actually almost the same as the $DEFINE in COBOL. They both allow you to define and name a macro, this macro will create âin-lineâ code, so it will generate more efficient if slightly larger code. However, the COBOL define statement can be as long as you want, whereas many C compilers restrict you to just the line that contains the #define statement. Passing parameters to a C macro is more confusing as well, just make sure to sprinkle liberally with parentheses to ensure the order of precedence.
The #define can also work rather like an 88 level variable in COBOL, for example;
#define FALSE 0#define TRUEÂ !FALSE
I could then say âif FALSEâ just like in COBOL with an 88. The advantage here is that I can define TRUE to be ânotâ FALSE by use of the ! (which means not) in the second #define statement. This doesnât really have an equivalent in COBOL.
Now that I have explained just enough about the #define statement to get us through our first example, why donât I finish explaining the example.
Since we have defined SYS to be equal to the character string HP3000 the first #if statement will be true and the file hp3kio.h will be copied into the program at compile time. The #elif is a really stupid way of saying âelse ifâ, only the assembler gods know why they chose to abbreviate it the way they did. So if we want to compile our code to run on a VAX we would only need to change the #define line, and then our code will run on a VAX if we have done our initial setup correctly.
Believe it or not, you could accomplish almost the same thing using the little known and rarely used compiler switches available in COBOL. By using the following commands you could accomplish a similar effect.
$SET X0=OFF$IF X0=OFFCOPY WINDOWS$IF X0=ONCOPY LINUX$IF
It is important to note that while COBOL is not case sensitive, C is. So myvar, Myvar, and MYVAR would actually be three different things. An informal standard that I have seen is that all your âregularâ C code is in lower case and any macros and sometimes functions, that you define are done in upper case to make it quick and easy to distinguish between what is part of C and what is yours. This isnât a bad idea at all.
One last note while we are talking about getting started, is how to put comments in your code. In COBOL you put a * in column 7 and then type whatever you want, or you can use the last 5 characters of each line to put something meaningful. In C our comments must be enclosed in matching /* ⊠*/, this can occur on a line with commands, by itself, or spread across lines. Here are a few examples;
/* here is my comment *//*here is another commentthat spans multiple lines*/
a=b; /* here is a comment on a line with a command */
VARIABLE DECLARATION
The most obvious next step is how to define variables in C. This is where C really has it over COBOL in some respects, as you can define global AND local variables. This would be the equivalent of having something like a state validation paragraph in a COBOL program that declared all the variables it needed at the top of the paragraph and then they went away when the paragraph returned to the calling process. Imagine, no more global variables getting declared that only got used once.
C differs significantly from COBOL in the way that variables are defined.
COBOL C---------------------------------------------------------
PIC S9(4) COMP shortPIC S9(9) COMP longPIC 9 char
An important note here on declaring simple integers in C, a standard declaration would be âint my_counterâ. Now the length of my_counter will be dependent on the native architecture of the machine it was declared on. So on a 16 bit system saying âintâ will declare a 16 bit integer or PIC S9(4) COMP. The problem is that if you move that code to a 32 bit system, that declaration will suddenly be 32 bit or PIC S9(9) COMP. We get around that by declaring them to be either a âshortâ int which is 16 bits, or a âlongâ int which is 32Â bits.
Our next variable type is the string or character array. C deals with strings in an extremely different way. Because everything is a single character you have to define an array of characters, this is also how you have to reference it. Here is an example of how you could do it in COBOL and how you would have to do it in COBOL if it worked like C;
char name[8]; /* a variable called name that is eight characters */01 NAME PICÂ X(08).
or if it was defined like C would have you do
01 NAME-ARRAY 05 NAME PIC X OCCURS 8 TIMES.
Actually, character arrays in C will always be null-terminated, so if you needed an eight character array you would need to make it nine to account for the null character at the end.
The only way to initialize a character array to spaces in C is to move single characters to each element of the array. Here would be your choices in the two languages;
01 NAME PIC X(08) VALUEÂ SPACES.
or
MOVE SPACES TOÂ NAME.
or
INITIALIZE NAME.
int i;char name[9];for (i=1; i<=9; i++)name[i] = â â;
Pretty nasty huh? I will explain all the things in the C statement that didnât make sense later. C also differentiates between a character and a string. Since the âcharâ type only really declares a single character those donât need to be null terminated, so these two declarations are different;
char switch;char switch2[1];
You would actually need to make âswitch2â an array of two characters for it to function the way you would expect it to because of the null terminator on strings. A null is defined in C with \0, and all of the string manipulation functions rely on the proper placement of the null character so that you will get the expected output.
The distinction between a character and a string in C takes a little getting used to, for example, if you want to initialize our character variable switch to Y you would enclose it in the single quote character â, i.e., âYâ. Single quotes denote that it is a single character, whereas for switch2 you would use double quotes â to enclose the string, i.e., âYâ. It is VITAL that you keep this straight, some compilers wonât complain if you use this incorrectly and you could get some really unpredictable results.
Now that we know how to declare simple variables in C, how would we declare a record structure analogous to the 01 variable declaration in COBOL? There is what is known as the âstructâ in C that is used for this exact purpose, although the implementation is a little bit odd. First letâs declare a simple layout in COBOL, then Iâll do the same in C;
01 CUST-MAST 03 CM-NUMBER PIC X(06). 03 CM-NAME PIC X(30). 03 CM-AMT-OWE PIC S9(9) COMP. 03 CM-YTD-BAL PIC S9(9) COMP OCCURS 12. 03 CM-PHONE PIC X(10).
struct customer_master { char cm_number[7]; char cm_name[31]; long cm_amt_owe; long cm_ytd_bal[12]; char cm_phone[10]; };
struct customer_master cust_mast;
The âstructâ verb declares a template of the record type that you are concerned with, once the template is declared you can then declare a variable that is a type of that structure. So the line: struct customer_master cust_mastdeclares a variable âcust_mastâ to be of a type âcustomer_masterâ. You would then reference the memberâs of the structure by specifying the variable name dot member, i.e., cust_mast.cm_name This can be especially handy if you are going to reuse a structure for a different purpose. The drawback here is that there is no convenient way to initialize the variable inside of a structure without addressing each member individually. COBOL has the very handy INITIALIZE verb to do this, you could write a general purpose initialization function in C that would serve the same purpose, however. You can name the structure at the same time as you declare if you donât want to reuse it. After the } and before the ; just put any old variable name that you want it to have. The last common verb used in the Working-Storage section is REDEFINES. The C version is the âunionâ verb. Redefines is mostly handy for working with a variable as either alpha or numeric. Since byte referencing was introduced in COBOL-85, you hardly ever see a REDEFINE statement used to get at various substrings within a variable anymore. Now letâs look at how you would declare a REDEFINE and a âunionâ.
01 CUST-MASTER 03 CM-DAYS-LATE PIC XXX. 03 CM-DL-REDEFINE REDEFINES CM-DAYS-LATE. 05 CM-DL-NUM PIC 999.
union redef { char days_late[4]; int dl_num; };
union redef days_late_test;
The setup and use of unions is very similar to structs, you can even put a union inside a struct, which is where you would want to use it most of the time anyway. We made âdays_lateâ a character array of 4 because we have to remember to account for the null character. You can do all sorts of strange things with unionâs if you care to, but that is really all I am going to touch on.
One other type that I want to touch on is the enumerated type. By using the âenumâ keyword, we can create a new âtypeâ and specify values it may have. (Actually, âenumâ is type âintâ, so we really create a new name for an existing type.) The purpose of the enumerated type is to help make a program more readable, like the COBOL 88 level. The syntax is similar to that used for structures;
enum ranges {min = 10, max = 100, mid = 55};enum ranges tester;tester = mid;if (tester >Â min)
The if statement would be true because tester would have a value of 55. I suggest that if you want to use enumerated types that you read up on them a heck of a lot more than what I just touched on here.
The last point I want to make about variable declaration is that C has very little facility for applying edit masks compared to COBOL. This makes it a less than convenient language for writing reports and such where date and dollar edit masks are used extensively.
OPERATORS
Letâs first go through the difference between = and == in C. In COBOL there are several ways to get data into a variable. A common way is the MOVE verb, the equivalent in C is =. This is confusing because if you set up a logical test and use = it will always evaluate to true because the value on the right-hand side of the equal sign will be assigned to the variable on the left side. You need to use == if you want to compare values. Here is a simple mistake to make that can cause all sorts of problems. If in C I were to say
if(my_int =Â 5)
what would happen is that the variable âmy_intâ would be assigned the value 5 and since the assignment went successfully then it would return a non-zero value which would indicate true. This would make the âifâ statement evaluate as true. So this âifâ statement would always be true. Simple mistake, major repercussions, and the compiler wonât complain about it because itâs a valid statement. Make sure you learn the distinction between = and == early and never forget it.
Our next boolean operator is âANDâ. COBOL makes it very easy to compare multiple values in an IF statement;
IF (VAR1 = VAL1) AND (VAR2 = VAL2 OR VAL3)Â THEN
The C representation for âANDâ is â&&â, âORâ is â||â, and âNOT EQUALâ is identified by â!=â.
None of these are particularly difficult to learn, but they are less than intuitive when first learning the language.
Being creative you could make use of the #define to make the operatorâs anything you want, for example;
#define AND &&#define OR ||#define NOTÂ !=#define EQUALSÂ ==
Now this may tick off the other C programmers, but if it helps you to make your code more readable and easier to maintain, whoâs to say itâs wrong?
ASSIGNMENT OPERATORS
Since I already talked about how the = sign in C is equivalent to the COBOL MOVE verb, I wonât talk about it again. However another option to using the = sign in C is to use the string function âstrcpyâ. This performs a âstring copyâ into a variable. This is a good way to initialize a character array and make sure that a null is properly placed (strcpy appends the null to the end of the string copied in), especially if you want to append data to the string later. The verb in C to append strings is âstrcatâ for âstring concatenationâ. In COBOL we have the âSTRINGâ verb, which gives you much finer control over how variables are concatenated. All the âstrcatâ does is copy one character until it hits a null into another character array, starting at the terminating null. You can see now why having a null in the right place is so important.
There are a couple of really cool increment operator shortcuts in C that I absolutely love. In COBOL if you want to increment a variable in a loop for instance you can do it a couple of ways;
ADD 1 TO KOUNTCOMPUTE KOUNT = KOUNT + 1ADD 1 TO KOUNT GIVING KOUNTPERFORM VARYING KOUNT FROM 1 BY 1 UNTIL KOUNT >=Â MAX
In C you could say the following;
kount = kount + 1kount += 1kount ++for (kount = 1; kount >= max;Â kount++)
The first example should be obvious, in the second example the += is a shortcut of the first example, it means to include the variable on the left side of the = sign in the computation on the right side. The third example means to increment the value by 1. Our first two examples could have had any value in the addition, but the third one simply increments by one. The last example is identical to the last COBOL example. What is interesting is the last parameter âkount++â, by putting the ++ on the right side of the variable we are saying to increment the variable after the test is made as to whether it is greater than or equal to âmaxâ. If the ++ is put to the left side, as in â++kountâ, it means to increment BEFORE the test is made. This is the same as the PERFORM directive âWITH TEST BEFOREâ or âWITH TEST AFTERâ. Theâââsign can be used to decrement in the exact same fashion, i.e.,= or â.
LOGICAL CONSTRUCTS
Hopefully, everyone here is familiar with the COBOL PERFORM statement. It has more variations than I am willing to get into, but it is the only looping construct it has unless you count using GO TO. C offers several different looping controls, there is;
whiledo..whilefor
Now COBOL nicely lumps the functionality of both the âwhileâ and âforâ loops into the PERFORM. The âdo..whileâ loop, however, is not explicitly the same, you can simulate it by controlling your variables correctly. In essence, the difference between âwhileâ and âdo..whileâ is that in the âdo..whileâ loop, it will âdoâ the loop at least once since the test isnât until AFTER the loop has been executed once. In the âwhileâ loop your test may not be valid the first time you do it so you may never actually go through the loop.
int i = 21;while(i++ < 20) { printf("%d\n",i);}/* the above line will display nothing since i is already greater than 20 */do{ printf("%d\n",i);} while(i++ < 20);/* the above line will display 21 */
Letâs talk about the âifâ statement, here is an example of how confusing a string comparison operation can be. In COBOL the following statement is very straightforward
IF STRING1 IS EQUAL TO STRING2 or IF STRING1 =Â STRING2
You cannot compare strings that way in C. There is a function in the âstring.hâ header file that will compare two strings, however, to get the same result it would have to be worded as follows;
if (!strncmp(string1,string2)) {
Let me explain, first off âstrncmpâ is a string compare function, if string1 is less than string2 then a value less than zero will be returned (sort of like using Condition Code in COBOL). If string1 is equal to string2 then a value of zero is returned. If string1 is greater than string2 then a value greater than zero is returned. The problem is that if you use strncmp in an IF statement and the strings are equal then zero is returned, zero indicates that the IF statement is false, that is why we prefaced the âstrncmpâ with â!â which means not. This has the net result of returning a non-zero value, which is TRUE for the IF statement, if string1 and string2 are equal. This also further illustrates how logical expressions can be embedded in the âifâ statement.
I/O
C is blessed with having no I/O facilities built into the language at all. So how do you do any sort of terminal or file I/O? Fortunately somebody back in the dark ages of C programming wrote the Standard I/O header file. So if you want to do any I/O you must include <stdio.h>. I will talk about some of the more basic features and functions included in stdio.
The most commonly used functions from stdio are âprintfâ and âscanfâ. âprintfâ is used to display information to STDLIST and âscanfâ is used to read information from STDIN. Both of these functions have extensive formatting capabilities included in their usage. COBOL is nice because you can use DISPLAY and ACCEPT to read or write virtually anything you want. You canât however do type conversion or variable formatting in the statements themselves, you would have to declare a formatting variable in working storage first. While C gives very little to no capability for declaring formatting variables, it does give you extensive control over formatting your output. A simple example would be displaying an integer that has two decimal points.
01 EDIT-INT PIC ZZ9.9901 MY-INT PIC S9(3)V99.MOVE MY-INT TO EDIT-INT.DISLAY EDIT-INT.
float my_int;printf(â%3.2f\nâ,my_int);
As you can see, itâs simpler to format in C, if somewhat less intuitive. The way âprintfâ works is that it takes a literal and/or formatting string in the first parameter, which is the part inside the quotes. And then takes variable substitution the second parameter. Then \n means issue a new line at that point. Here is an example of embedded text;
printf("I have %d apples and %d oranges",count_apple, count_orange);
This would substitute âcount_appleâ into the first parameter and âcount_orangeâ into the second. There are almost a dozen different formatters available in the âprintfâ.
Another interesting feature of âprintfâ is itâs ability to do type conversion, for this example you need to know that %d means print an integer and %c means print a character;
printf("%c %d\n", 'A', 'A');
what do you think the output of this would be? Odds are you guessed wrong, you would see âA 65â because by specifying %d for the alpha character âAâ it would format it to the decimal ASCII code for âAâ, which is 65.
Like âprintf()â, âscanf()â uses a control string followed by a list of arguments. The main difference is in the argument list. Printf() uses variable names, constants, and expressions. Scanf() uses pointers to variables. Fortunately, we donât have to know anything about pointers to use the function. Just remember these two rules:
1. If you want to read a value for a basic variable type, precede the variable name with an &.2. If you want to read a string variable, donât use an &.
Here is a short example of displaying output and prompting for input:
main(){int age;float assets;char pet[30];printf("enter your age, assets, and favorite pet.\n");scanf("%d %f", &age, &assets);scanf("%s", pet); /* no & for char array */printf("%d $%.0f %s\n ", age, assets, pet);}
Which would look something like this if you were to run it.
enter your age, assets, and favorite pet.3210507.32penquin32 $10507Â penguin
An interesting point here is that the scanf() can read more than one variable in at a time, unlike the COBOL ACCEPT verb. One last point on printf(), the following 2 statements work identically:
printf("Enter your option ");DISPLAY "Enter your option " NO ADVANCING.
As I mentioned earlier, C is geared towards single characters, not strings. So there is a whole set of functions in stdio that are geared towards reading and writing single characters. Since COBOL cares very little about how big an array you use to read or write I am not going to get into the specifics, just remember these four functions names;
getcharputchargetcputc
I am going to cover just two more COBOL verb comparisons before I get into showing some small program shells. Two of my favorite verbs are STRING and UNSTRING, they are used to concatenate variables and literals and to parse strings based on user defined tokens. In general they are very easy to use, their C counterparts however arenât. I will just run through the COBOL example and then show the exact same code in C.
01 FULL-NAME. 03 FN-FILE PIC X(08) VALUE "MYFILE". 03 FN-GROUP PIC X(08) VALUE "MYGROUP". 03 FN-ACCOUNT PIC X(08) VALUE "MYACCT".01 WS-FULL-NAME PIC X(26) VALUE SPACES.PROCEDURE DIVISION. A1000-START. DISPLAY FULL-NAME. * displays "MYFILE MYGROUP MYACCT" STRING FN-FILE DELIMITED BY SPACES "." DELIMITED BY SIZE FN-GROUP DELIMITED BY SPACES "." DELIMITED BY SIZE FN-ACCT DELIMITED BY SPACES INTO WS-FULL-NAME. DISPLAY WS-FULL-NAME. * displays "MYFILE.MYGROUP.MYACCT" MOVE SPACES TO FULL-NAME. UNSTRING WS-FULL-NAME DELIMITED BY "." INTO FN-FILE FN-GROUP FN-ACCT. DISPLAY FULL-NAME. * displays "MYFILE MYGROUP MYACCT"
#include <stdio> #include <string> main() { char ws_full_name[27]; struct full_file_name { char file[9]; char group[9]; char acct[9]; } fn;
strcpy(fn.file,"MYFILE"); strcpy(fn.group,"MYGROUP"); strcpy(fn.acct,"MYACCT"); strcpy(ws_full_name,fn_file); strcat(ws_full_name,"."); strcat(ws_full_name,fn_group); strcat(ws_full_name,"."); strcat(ws_full_name,fn_acct); printf("%s\n",ws_full_name); /* displays "MYFILE.MYGROUP.MYACCT" */
You know, I donât think I am going to show the equivalent of UNSTRING, it is just too confusing if you are just getting started with C. The function is called âstrtokâ and it requires that you use pointers to strings, and since I didnât really get into pointers at all I donât want to confuse the issue. Keep in mind however that you MUST understand how pointers in C work or you will never be able to use the language effectively. Itâs just that a full discussion of pointers is beyond the scope of this paper.
Anyway, in our C example we used the âstrcpyâ command to copy a string into a variable. The function also adds the null terminator and essentially initializes our character array. Using the âstrcatâ function, concatenates the string in the second parameter to the variable named in the first parameter. It looks for the null terminator and then starts writing the string onto it.
As you can see, it is more difficult and roundabout to deal with strings in C than COBOL. Now donât get the wrong idea, I think C is great for some things, itâs just that if you are coding a standard business type application a lot of those things arenât necessary.
CODE SKELETONS
OK, so just what do you have to do to write a program in C? As you know, COBOL has itâs four divisions that must be used;
IDENTIFICATION DIVISIONENVIRONMENT DIVISIONDATA DIVISIONPROCEDURE DIVISION
Each of this has itâs own purpose in life as to what it describes. In C everything is based on the function. As you saw in my previous example I had âmain()â towards the top of my program. You can think of main() as the PROCEDURE DIVISION. It is a function just like everything else in your C program will be, and it is usually the only function you MUST have. I say usually because if you are writing a series of subroutines that are going to go into an RBM you donât need to name function main().
So to write the classic âHello Worldâ program in C, this is all you would need to do.
#include <stdio.h>main();{printf("Hello World\n");}
That is a lot less code than it would take under COBOL, but itâs still more than you would have to do in BASIC. The problem that I see here is that having to do the include of stdio.h just for the one printf() statement, will cause your program to be fairly large for something that is so trivial.
CODING FOR PORTABILITY ORÂ SPEED
You hear a lot about how portable C is, and how you should code for portability. There are just so many things that change from platform to platform that it could be really tedious to code for portability. I know there are many toolkits that help with that, like Qt, but weâre just talking raw code here.
If you code to take advantage of your CPUâs native architecture you will see a great increase in speed and reduced code size. If you code for portability by using the languages native constructs you will have a larger, slower program. If you want the best of both worlds you will want to code your own intermediate include files that allow you to switch between architectures.
SUMMARY
So what do I conclude from all this? Before I tell you, let me tell you about a letter I read that someone had sent into to a Computer Language magazine. The gentleman was outraged that they had bothered to include COBOL in their magazine, and if they were going to do that they might as well include RPG as well since neither one was a real language. His criteria for a real language was pretty much comprised of the few things you can do in C that you canât do in COBOL. Such as using pointers and declaring local variables. So why does this gentleman love a language so much that has no built in I/O facility, and deals with strings as an array of bytes?
Now this had to have been one of the more asinine statements I have ever seen. COBOL is one of the most popular languages for business applications, and while I donât care for RPG, IBM has made sure that it has maintained a huge installed base as well. So why do I bother to mention this? Because as I said in my opener, there is still a ton of COBOL out there, and it is probably managing your paycheck and investments, so donât be too quick to discount it.
C From a COBOL Perspective was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.