Overcoming the Idiosyncrasies of C Programming in Windows

When I moved from the Unix-based platform of MacOS to using a Windows PC as my main platform, one of the questions I had to deal with was how I would continue doing C programming on the new system. Of course installing Cygwin and running Linux were two perfectly viable options, both of which I have utilized, but I have also had occasion to want to run my own C programs natively on Windows as well. For this I needed a solution other than the normal GNU toolchain. The solution I eventually settled on was Open Watcom. It’s a legacy application and only compiles C programs into 16-bit or 32-bit applications, but this isn’t that much of an issue since all 64-bit versions of Windows run 32-bit applications. I just wouldn’t be able to squeeze that extra margin of efficiency out of the system by taking full advantage of the x86-64 extension. In any case, Open Watcom is the only Windows C compiler I know of that is fully open source (I don’t consider freemium IDEs like Visual Studio to be true freeware solutions).

One of my hurdles in learning to use Open Watcom was in trying to learn the quirks and idiosyncrasies of C programming for Windows. Windows C is somewhat more strict/restrictive than C in Unix/Linux. You might say that Windows C is a subset of Unix C. I was forced to test for myself what all of these idiosyncrasies were when I was faced with two similar but unrelated tasks – one being that of importing my data sanitation program (detailed in this post) to Windows so I could use it as a backend for a batch file I had written, and the other being that of compiling some C programs in Windows that I could use to test my PHP abstraction layer that I’m writing (will probably talk about that project in a future post, once I’m further along with its development).

The first major idiosyncrasy of Windows C programming is that the main() function no longer has a return type of int, rather, it is a void function. This means that the return 0; at the end of the program gets deleted and int main( ... ) becomes void main( ... ).

The second major idiosyncrasy is that all variable declarations within any given C function must come before all other statements within that function. This one really threw me for a loop when I first encountered it, and it prevented me from being able to use Open Watcom for several months, until I had a flash of insight and realized what the problem was, based on previous coding I had done in Borland Turbo C in DOS. It’s one of those “putting 2 and 2 together” moments that gets delayed for a long time because you’re just not thinking about the problem in that context. This requirement also causes some difficulty when you’re faced with the problem of declaring an array whose size cannot be determined at the beginning of the function. I have, however, found a rather elegant solution to this. Instead of choosing an arbitrary value, say char array[100]; and hoping that 100 is large enough, you simply declare it as char *array; and then array = malloc( correct_size ); when you know exactly how many units you’ll need. It does run more slowly and it does require #includeing an extra header file (assuming you’re not already using stdlib.h), but it’s less of a hassle than having to deal with a terminal case of C Programmer’s Disease.

The third and final major idiosyncrasy – and this is largely a result of the previous one – is that there is no block scope in Windows C. Unix C has two local scopes: block and function, and two global scopes: static and external. Windows C, as far as I can tell, just has a single local scope and a single global scope. This means (obviously) that you can no longer use C code like for( int i = 0; i < max; i++ ) – as with other local variables, all loop iterators have function scope and are declared at the beginning of the function.

To illustrate, I will show both the Unix version and the Windows version of my DoD program from the previous entry so you can appreciate the differences:

C programming in Unix/Linux:


 1 /*******************************************
 2  * srm-dod v. 1.0                          *
 3  * Description: Implementation of the DoD  *
 4  * secure erase algorithm with a couple of *
 5  * modifications for extra obscurity       *
 6  * Author: Michael Warren                  *
 7  * Date: January 8 & 17 2019               *
 8  *******************************************/
 9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <time.h>
15 
16 int mainint argc, char **argv ){
17         FILE *fp;
18         forint i = 1; i < argc; i++ ){
19                 if( !(fp = fopen( argv[i], "r+" )) ){
20                         // Error handling section:
21                         char errorstring[strlen( argv[0] ) + strlen( argv[i] ) + 3];
22                         sprintf( errorstring, "%s%s", argv[0], argv[i] );
23                         perror( errorstring );
24                         exit( errno );
25                 }
26                 // Determine file length:
27                 fseek( fp, 0SEEK_END );
28                 long file_length = ftell( fp );
29                 rewind( fp );
30 
31                 // Proceed with DoD algorithm:
32                 srandtimeNULL ) );
33                 char c = rand() % 256;
34                 forint i = 0; i < file_length; i++ ){
35                         fputc( c, fp );
36                 }
37                 rewind( fp );
38                 c = ~c;
39                 forint i = 0; i < file_length; i++ ){
40                         fputc( c, fp );
41                 }
42                 rewind( fp );
43                 forint i = 0; i < file_length; i++ ){
44                         fputcrand() % 256, fp );
45                 }
46                 rewind( fp );
47 
48                 // Two extra zero passes to conceal the
49                 // fact that data was securely erased:
50                 forint i = 0; i < file_length; i++ ){
51                         fputc'\0', fp );
52                 }
53                 rewind( fp );
54                 forint i = 0; i < file_length; i++ ){
55                         fputc'\0', fp );
56                 }
57 
58                 // Truncate:
59                 fclose( fp );
60                 fp = fopen( argv[i], "w" );
61                 fclose( fp );
62 
63                 // Change filename and delete:
64                 char newname[TMP_MAX];
65                 forint i = 0; i < TMP_MAX; i++ ){
66                         newname[i] = rand() % 26 + 'A';
67                 }
68                 ifrename( argv[i], newname ) ){
69                         printf"%s: Error renaming file %s\n", argv[0], argv[i] );
70                         printf"Error code: %d\n", errno );
71                         exit( errno );
72                 }
73                 remove( newname );
74                 fclose( fp );
75         }
76         return 0;
77 }

C programming in Windows:


 1 /*******************************************
 2  * srm-dod v. 1.0                          *
 3  * Description: Implementation of the DoD  *
 4  * secure erase algorithm with a couple of *
 5  * modifications for extra obscurity       *
 6  * Author: Michael Warren                  *
 7  * Date: January 8 & 17 2019               *
 8  * Windows version created Feb. 15 2019    *
 9  *******************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <time.h>
16 
17 void mainint argc, char **argv ){
18         FILE *fp;
19         int i;
20         long file_length;
21         char c;
22         char *errorstring;
23         char newname[256];
24         errorstring = (char *) malloc0 );
25         for( i = 1; i < argc; i++ ){
26                 if( !(fp = fopen( argv[i], "r+" )) ){
27                         // Error handling section:
28                         errorstring = (char *) realloc( errorstring, strlen( argv[0] ) + strlen( argv[i] + 3 ) );
29                         sprintf( errorstring, "%s%s", argv[0], argv[i] );
30                         perror( errorstring );
31                         exit( errno );
32                 }
33                 // Determine file length:
34                 fseek( fp, 0SEEK_END );
35                 file_length = ftell( fp );
36                 rewind( fp );
37 
38                 // Proceed with DoD algorithm:
39                 srandtimeNULL ) );
40                 c = rand() % 256;
41                 for( i = 0; i < file_length; i++ ){
42                         fputc( c, fp );
43                 }
44                 rewind( fp );
45                 c = ~c;
46                 for( i = 0; i < file_length; i++ ){
47                         fputc( c, fp );
48                 }
49                 rewind( fp );
50                 for( i = 0; i < file_length; i++ ){
51                         fputcrand() % 256, fp );
52                 }
53                 rewind( fp );
54 
55                 // Two extra zero passes to conceal the
56                 // fact that data was securely erased:
57                 for( i = 0; i < file_length; i++ ){
58                         fputc'\0', fp );
59                 }
60                 rewind( fp );
61                 for( i = 0; i < file_length; i++ ){
62                         fputc'\0', fp );
63                 }
64 
65                 // Truncate:
66                 fclose( fp );
67                 fp = fopen( argv[i], "w" );
68                 fclose( fp );
69 
70                 // Change filename and delete:
71                 for( i = 0; i < 256; i++ ){
72                         newname[i] = rand() % 26 + 'A';
73                 }
74                 ifrename( argv[i], newname ) ){
75                         printf"%s: Error renaming file %s\n", argv[0], argv[i] );
76                         printf"Error code: %d\n", errno );
77                         exit( errno );
78                 }
79                 remove( newname );
80                 fclose( fp );
81         }
82         free( errorstring );
83 }

Just as a final disclaimer, I may be completely wrong about all of this, and these idiosyncrasies may in fact be the result of using legacy software and not of using Windows. I don’t know of any easy way of determining this, and in any case, it made for an interesting and informative article in my opinion, so I’m not going to sweat it.

2 thoughts on “Overcoming the Idiosyncrasies of C Programming in Windows

  1. I think with Windows today, the expectation is that all C-family programming done on the platform is actually C++ (or C#, but that’s obviously not applicable here). Mainly because I get the feeling C programming on Windows is mainly graphics or games programming with things like OpenGL or DirectX – which require C++. In C++ terms, I’ve never really noticed a big difference between programming C++ on Windows with VC++ or C++ on Linux with Clang or GCC (apart from platform dependent things like system calls). Like I’m currently in the middle of porting and improving a text adventure game “engine” I started last year from Windows to Linux (and eventually platform independent), and it’s mostly been smooth sailing from VC++ to Clang.

    I’m still intrigued by some of those idiosyncrasies though, so I might try some of the things you mentioned on Windows under VC++ for lulz and see if it works fine.

    Liked by 1 person

    1. There is definitely more integration between the graphics and the underlying operating system in Windows than there is in Unix/Linux. And your comment about C++ indicates what is probably the reason why the only full-fledged free C compiler I could find for Windows was a legacy 32-bit IDE.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s