Fundamental C - Low Down Data
Written by Harry Fairhead   
Monday, 21 November 2016
Article Index
Fundamental C - Low Down Data
Sizing Variables

Declaring Variables

Before you can use a variable you have to declare it as being of a fixed type. This lets the compiler know what the variable is called and how much memory to set aside for it. It also tells the compiler how to implement operations that involve the variable. For example,

int myinteger;

creates a variable that is suitable for storing an integer and if you use myinteger in an arithmetic expression the compiler knows that it has to use integer operations. 

Notice that you have to declare a variable before you use it. This generally means that C programmers gather their declarations to be at the start of the program.  This isn't a hard and fast rule, however, and some programmers follow the convention of declaring variables where they are first used. 

Also notice that variable names are case sensitive and even though you can write long variable names only the first 31 characters are used by the compiler. 

We now come to the question of initialization. This is one of those many things that makes C dangerous in some programmers opinion.

What does a variable contain when you declare it?

The answer is often whatever was in the memory that is used to store it. C doesn't do anything that isn't essential to make sure that you can write a fast and efficient program. When you write:

int myVariable;

the compiler simply sets myVariable as a reference to some area of memory. When the program is run, nothing is done to initialize the memory location and this means there are no overheads at all.  Of course, if you use the variable without storing something in it the result is that you will find whatever was stored in the memory before. This is why it is dangerous - uninitialized variables can cause very difficult to find bugs. 

Notice that the system will initialize to zero global and static variables for you - see later. 

You can initialize a variable by storing something in it:

int myVariable;
myVariable=0;

which does give your program something to do when it is first loaded i.e. myVariable=0 is an instruction that has to be carried out.

As an alternative you can use an initialization:

int myVariable=0;

In general you should initialize variables unless you have a really good reason not to. 

Literals

If you are going to initialize variables you have to know how to write values that are suitable. C has quite a range of ways of letting you specify a value and its type. 

For integers you can write in decimal, octal or hexadecimal. An octal constant is signified by a leading 0 - do don't write unnecessary leading zeros on decimal literals. Hex literals are much safer because they start with 0x, which isn't likely to be entered by accident. 

So 77 is 77 in decimal but 077 is  63 in octal and 0xFF is 255 in hex. 

A floating point literal is signified by having a decimal point - 1 is an integer and 1.0 is a float. You can also write an exponent as in 1E3 which is float with the value 1000. 

You can also use a suffix to specify the type of a literal:

1.0f is a float 

2u is an unsigned int

3L is a long

4Lu is an unsigned long

5LL is a long long

6LLu is an unsigned long long

Often the compiler will work out what the literal should be. For example there is no suffix for a short literal because the compiler simply attempts to fit the int literal into the short variable and if it fits it doesn't complain. When you write a literal the compiler tries to fit it into an int, if it doesn't fit it try a long and then a long long. 

Most of the time you don't have to specify a type for a literal, but sometimes it is essential to avoid a compiler error. For example, if you try: 

long long y = 2147483647 * 100;

you will most likely find that y is  -100, which is not the correct answer. The reason for this is that the literals are treated as ints as they are within the range of a four-byte int, but the arithmetic overflows and this erroneous result is stored in y. The solution is to explicitly set the constants to LL:

long long y = 2147483647LL * 100LL;

which now gives the correct result. 

Exact Size Variables

Most of the time you can work with C's strange approach to variable types because you are targeting a particular machine. The solution to the problem of how to work with variables that have a definite number of bits is to use the stdint.h header file. This is a library that was introduced in C99 to provide a set of types that are fixed in size irrespective of the machine in use. Of course the implementation might not be the most efficient possible on the machine. 

The library introduces new types of the form:

intN_t

uintN_t

for signed and unsigned integers with N bits. The only values of N that have to be implemented in the library are 8,16,32 and 64. The signed types are implemented as two's complement. Note that the implementations have to be exact and no padding bits are allowed. 

To use the library you have to add:

#include <stdint.h>

to the other automatically generated includes.

So, for example:

int8_t just8bits;

is guaranteed to be an 8-bit variable and

uint16_t just2bytes;

is guaranteed to be a two-byte unsigned int. 

This all works well, but if the machine doesn't support int16_t as a native two-byte int the results could be very slow.

There is an alternative which lets you specify either the minimum width that can be used:

int_leastN_t or uint_leastN_t

or the fastest minimum width:

int_fastN_t or uint_fastN_t.

For example:

int_least8_t mybyte;

will create a variable that is as small as possible but still greater than or equal to 8 bits. The fast version gives you a variable that is as small as possible but with the extra condition that it is fast. So, for example:

int_fast8_t mybyte;

will be at least 8-bits but it might be larger if it is faster to use a bigger variable type. Exactly how these are implemented is left up to the compiler writer. 

In practice using stdint.h is a good idea, even if you are targeting a fixed machine because it makes clear the exact size of the variables that are in use. Specifically, if you write:

int myvar;

then a programmer unfamiliar with the machine is left wondering what the size of the variable is, but if you use:

int16_t myvar;

then, even though this might map to int, it makes it clear that this is a 16-bit int. 

There are also macros that will create literals in the correct type:

INTN_C(value) 

UINTN_C(value)

create signed and unsigned literals with N bits. For example:

INT16_C(255)

creates a 16-bit signed constant for 255.  

 

Fundamental C: Getting Closer To The Machine

Now available as a paperback and ebook from Amazon.

  1. About C
      Extract Dependent v Independent
                  & Undefined Behavio
  2. Getting Started With C Using NetBeans
  3. Control Structures and Data
  4. Variables
      Extract Variables
  5. Arithmetic  and Representation
      Extract Arithmetic and Representation
  6. Operators and Expression
      Extract: Expressions
      Extract Side Effects, Sequence Points And Lazy Evaluation
      First Draft of Chapter: Low Down Data
  7. Functions Scope and Lifetime
  8. Arrays
      Extract  Simple Arrays
      Extract  Ennumerations
  9. Strings
      Extract  Simple Strings
     
    Extract: String I/O ***NEW!!
  10. Pointers
      Extract  Starting Pointers
      Extract  Pointers, Cast & Type Punning
  11. Structs
      Extract Basic Structs
      Extract Typedef
  12. Bit Manipulation
      Extract Basic Bits
      Extract Shifts And Rotates 
  13. Files
     Extract Files
     
    Extract Random Access Files 
  14. Compiling C – Preprocessor, Compiler, Linker
     Extract Compilation & Preprocessor

Also see the companion volume: Applying C

<ASIN:1871962609>

<ASIN:1871962463>

<ASIN:1871962617>

<ASIN:1871962455>

 

Related Articles

Getting Started With C Using NetBeans

Remote C/C++ Development With NetBeans

Raspberry Pi And The IoT In C

Getting Started With C/C++ On The Micro:bit

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on, Twitter, FacebookGoogle+ or Linkedin

Banner


Crazy Clocks
10/03/2024

It's that time again when the clocks change and  time is of the essence and I indulge my interest in crazy clocks. I am always surprised that there are still new ideas for how to display the time [ ... ]



Apache Shiro 2.0 Released
21/03/2024

Apache Shiro 2.0 has been released. The Java security framework now requires at least Java 11, and has added support for Jakarta EE 10.


More News

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info

 

 

 



Last Updated ( Tuesday, 11 September 2018 )