tags:

views:

187

answers:

6

Another newbie question; Is it considered bad taste to use a lot of global variables in C ? I'm guessing the answer is probably yes, on the other hand, using main or some other function as a "base-function" to store pointers/values with global scope creates a whole mess with pointers to pointers etc... Any thoughts?

+2  A: 

One problem with global variables is that wherever they are used, you may not have referential transparency. And it is a good thing to know that given certain input a function will produce certain output for several reasons.

This page is a good resource for several other reasons why global variables are bad though too.

Brian R. Bondy
A: 

Although C gives you complete freedom to declare everything global it will help you with your coding and maintenance if you think in terms of "abstract data type" which can be implemented in procedural languages as well.

Essentially, instead of making all data as static global to all modules, you can make data global in a particular module.

kartheek
A: 

Use global variables if its extremely needed. If you have something that's needed on a global scope, then feel free.

Ruel
+1  A: 

Usually most programmers expect the functions to be pure, meaning that the output of a function will be completely determined by its input. By giving them states you can introduce much confusion for others that will be using your code. This reffers to the static variables declared in functions.

And for real global variables (those declared outside of functions), it's what Ruel said. Be careful although about multithreading.

ruslik
A: 

Rectifying the situation caused by a yucky mess of globals is like normalizing the design of a relational database schema.

There is always the option of lumping all the globals into one big struct and pass that struct around. But then, what's the point?

You would like to group them into structs and then you start discovering, "hey I need this info in the other struct too because one silly function needs it." Then you thought you solved the problem by merging the two structs. Then you found that finally, you're still stuck with a big piece of struct.

Why? Because like database normalisation, your struct normalization has to be done in concert, in conspiracy with the processes (aka your functions). You should construct an entity relationship about your information. Even though you are not writing using an oop language, you should still use systems-analysis methods bordering on oop. The difference between a programmer-analyst and a plain vanilla programmer is that a programmer-analyst performs rigorous data and process analysis of the project. Whereas, the plain vanilla programmer does happy-go-lucky programming.

For example, I would like to write a routine to control the printers in the company. There are

  • old ribbon-impact teletypes (holy cow!),
  • dot-matrix (do they still use those?),
  • colour-jets,
  • grey-jets,
  • grey-lasers and,
  • colour-lasers
  • fax/scan combos of jets and lasers.

And these are the functionality

  • find an appropriate printer for a job request
  • reroute a job from a failed printer
  • a job request can from a schedule, a desktop or a fax server
  • route a scanner output to a fax server
  • scan for ink, pigment levels for replacement
  • for obsolete ribbon types, predict ribbon replacement
  • maintain maintenance schedule

The simple approach is to have all the information placed into a huge pool of globals and then extern them all over the place. The advantage is you have the freedom to write functions unrestricted - restricted only by your ability to remember where all the variables are. Or the ability of someone else to find them.

Or, you could simplify your life and learn the simple tricks of entity normalization. Remember, entity is not just the data but the process/functions as well. At the end of such an exercise, you would begin to wonder in amazement how you could have survived thus far in life without an oop language like c++.

Let me exemplify below with a simplistic (perhaps, incomplete) implementation. When you plan your project in ER, and document it, it makes your programming and enhancement a lot easier. During this age of agile engineering, you have to be able to release usable partial implementation of a project. e.g., you would tell your project manager the following milestones

  • Week1 : Usable Printer routing for inkjet and laser printers
  • Week2 : Usable Printer routing for archaic impact printers
  • week3 : Usable scan jobs
  • week5-7 : Usable fax jobs
  • week8 : Half-baked job rerouting
  • week9 : 90% baked job rerouting
  • week9-11 : half-baked maintenance

With the schema of your information and processes drawn out, you will have a clear idea what constitutes a usable release and what to do on next iteration. With a well-planned schema, you would find it emotionally impossible to use globals. You would even then have a schema of globals rather than have them unstructured. Reserve globalising a structure for information that absolutely and necessarily require a single point of access - like a database pool, start-up flags to avoid starting up processes that are already started, etc.

Then, unless you are writing embedded or kernel code, you would very soon be moving to C++, guaranteed.

struct Printer{
  id,
  printType,
  location,
  *maintenance,
  *driver
}

struct Job{
  type,
  requestor,
  location
}

struct Location{
  building,
  floor,
  suite,
  lane
}

struct PrintType{
  ink,
  colour
}

struct JobStatus{
  job,
  location,
  endStatus
}

struct Maintenance{
  inkLevel,
  schedule
}

union InkLevel{
  HPDeskJetInk,
  HPLaserJetInk,
  OlivettiFabric,
  BrotherCellophane,
  etc, etc
}

enum Ink{
  FABRICImpact,
  CELLOPHANEImpact,
  INKJet,
  LASER
}

enum Colour{
  GREY,
  COLOUR
}

enum EndStatus{
  PRINTING,
  COMPLETED,
  FAILED,
  REROUTED
}

/* print will look at Job to determine the appropriate location */
JobStatus* print(job);

/* a callback to requestor indicating completion status */
JobStatus* printCallback();

/* a fax send event detected and fax function will find the appropriate server for the Job */
JobStatus* fax(job);

/* a photocopy to file job */
JobStatus* scan(job);

Maintenance* maintain(Printer);
Blessed Geek
A: 

It's bad to use any global variables in any language.

R..
omg lawlz!!!!!!
Robert Karl
Wow, never knew I'd get so much backlash from anonymous global-variable lovers.
R..