Random C things – part I

Hi There ! It’s been a while since I posted – I have just been busy with life  (read  “lazy” or occupied with random things). But, am back now.

This is not really a blog post per se. I just felt like putting up a refresher to people who are familiar with C programming. It’s in a concise question and answer format ; won’t take too long ! Go ahead … 🙂

01. Can two different type of variables be declared with same name ?

Example : int a ; and enum a { b = 2, c};

The answer to this is : Yes – if they are not in same scope (local / global scope) and variable name is not used for a “#define” constant. 

If both are in local or both are declared in global space – then there will be a clash in the symbol name space.

02. What happens to uninitialized const variable ?

It gets initialized to zero. Example : const int a; declaration will set “a” to zero and cannot be modified. Keyword “const” makes the variable a “read-only”.

Even when we use keyword “static” the variables are by default initialized to zero.

03. Can we have a structure with bit fields ? How to achieve this ?

Yes, we can!

For example :  If you want a structure that holds two variables whose values can range from 0 to 15 then , all we would need is two fields with 4 bits each. This can be achieved using a declaration as below :

struct sample{
uint8_t var1:4; // first variable 4 bits
uint8_t var2:4; // second variable 4 bits

04. What is structure packing?

We would want to make sure we are packing the structure variables “efficiently” – specially on embedded memory limited systems.

Example :
struct noPack{
char b; // one byte + one byte  implicit padding
uint16_t data; // two bytes
char c; // one byte + one byte implicit padding
a total of "6 bytes"

But, if we re-arrange this :
struct withPack{
uint16_t data; // 2 bytes
char b; // one byte
char c; // one byte
a total of "4 bytes" 

Yes, we saved 2 extra byte assignment just by rearranging and packing 
efficiently ! 
Usually the trick is to pack the structure starting with the biggest 
size data type (in this case (sizeof(uint16_t)) > sizeof(char)) 
to lowest size

05. What is integer overflow  ? How can we handle this ?

Yes, there can be problems when you cast a larger data type to smaller one -i.e., loss of information. Consider the example below :

uint16_t func1(uint8_t a ){
//do some operation and return
return (a+100);
Suppose, this function is used as below :
uint8_t temp = func1(10); // result returned is 16-bit
//but, implicitly gets truncated to 8-bit 
//(depending on the compiler's behavior)

Suppose input variable “a” was set to 0xFF  (max value for an 8-bit integer), adding a value of 100 requires more than 8 bits to hold the result of addition – resulting in an overflow when the value returned is stored in an 8 bit integer – “temp” in this example.

And, trust me when I say these can result in hard bugs to catch when they happen in production code. So, please take care of scenarios involving implicit conversions such as this ! Make sure you have fixed all the warnings your compiler spits out or at least take a look at the warning messages.

06. Why will I ever use a union?

Unions allow data members that are mutually exclusive to share memory. This is important specially in the embedded system cases where memory is a scarce resource.

For example : If you implementing protocol handler with different kinds of messages that can be exchanged, we could handle the different message types and their allocations as below on an embedded system :

// Declare a union with all types of different messages to be handled.
// All message types in the union shall share same memory.
// Only one of these messages would need to be handled by protocol implementation at 
// a given time -this means they are mutually exclusive and hence would be a good idea 
// to share the memory.

typedef union
  protocol_req_identity_type      identity;
  protocol_req_challenge_type     challenge;
  protocol_req_notification_type  notification;
} protocol_req_msg_union_type;

// Generic format to decipher a protocol message
// check mnessage type and cast to appropriate message type in structure.
typedef struct 
  protocol_msg_type_enum         msg_type; // can be a identity , challenge or a notification
  protocol_req_msg_union_type    msg_union;

07. Why do we need Enum ?

Well! One reason I can think of is – it’s not only important that we write efficient code but also readable code. Enum(s) provide us a way to add readability to our code.

Suppose you are writing some api and want to return custom status codes -1 , 1 and 2 – to mean different return status, to the caller. We can write human readable code as below, instead of using the status codes as numbers (-1, 1 ,2) directly.

//custom verbose return codes
typedef  enum codes{

// function / api that returns a status code 
cust_status func1(){
// Do some operation and return status codes to caller
// set error as default status
cust_status status = CUST_ERROR;

//you want the user to retry later
status = CUST_RETRY;
//do some processing 
//everything went fine
status = CUST_SUCCESS;
return status;

//caller checks the returned status code 
int main(){
if (CUST_SUCCESS == func1()){
else if(CUST_RETRY == func1()){
printf("Retry the operation later\n");

08. What is the difference between “#define” of a constant and declaring it “const int” ?

As a novice programmer, when I started with C , I wondered why were there two options to do the same thing – which one is to be used ?

#define TEST_VARIABLE 100      // pre-processor directive
int const TEST_VARIABLE = 100; // constant variable

#define is essentially a preprocessor directive. The C preprocessor shall replace all occurrences of the given variable in the code with the value before compiler compiles the code.

Whereas const is acted upon by compiler. It only indicates that the variable is “read only” , all rules of a variable apply to it as well. But, if a compiler doesn’t optimize and replace these occurrences, then there will be an overhead.

When you don’t really care about the types  -for example : a variable that holds a fixed constant could be #defined !   Also, if you are designing in a large team : #defines may be a good option as they throw a compile error when we redefine a local variable with same name, as in the example below.

//Global variable 
int const trials =10;

int func(){
// Someone decides to use same name for a local in the function but
// you intended to use global definition at a later point.
int trials= 20; 
// defining variable "trials" with a #define would have 
// thrown an error here.

// few 100 lines of code later

//suppose you intended to look up the global variable "trials"
int number_trials = trials; // this has 20, not the 10 that you wanted!!

09.  Can I assign a pointer to constant int to a pointer to an integer ?

You can ! But, don’t !!

Example :

const int * constptr; // pointer to constant integer

int* ptr; // pointer to an integer

const int var1 =10; // constant integer

constptr = &var1; // assign a const integer (const => read only)

ptr = (int*) constptr; //valid , but don’t !

*ptr = 15; // change value pointer – run time error !!

10. Compiler and volatile qualifier in C

Compilers with optimization enabled try to make code smaller and faster based on some rules.  Volatile keyword is used to indicate to compiler to “not optimize”.

There are cases where a variable may be updated in ways unknown to the compiler – such as :

  • Memory mapped input/output – peripheral registers,
  • Modification via signal interrupt – Global modified/accessed by other functions or ISR.
  • Shared global variable in multi-threaded programming

basically, where the updates made to the variable are not in the local scope of the code block. When this keyword is not used, the variables are cached to a CPU register or optimized out by the compiler.

“Volatile” ensures that the variable reads and writes are not optimized away.Consider the example for volatile usage below :

If you have a global variable (named value_to_write) that is shared between the main program that writes value from this variable to a port and an ISR that writes to this variable. We can have three cases that will have different behavior based on compiler optimization state and volatile qualifier is used or not.

  1. No compiler optimization : In this case , the main program code is not optimized and the ISR updated value gets written to the portX in main program
  2. With compiler optimization : In this case, the compiler looks at two different code blocks, each with variable named value_to_write as shown below. Since the compiler optimization is ON, compiler looks at main code block and unable to find updates/writes to the variable “value_to_write” , decides to optimize it out. Due to this optimization, only one value gets written to portX – 0, not the one updated by the ISR, which you wanted. This now calls for “volatile” usage.
  3. With compiler optimization enabled and volatile qualifier – now, if the variable value_to_write has a qualifier called “volatile” , although the compiler does not see a write to this variable in main code segment , it does NOT optimize it out. In this case portX will have the value updated by ISR, as expected.



But, there are some things to consider before using Volatile qualifier and they are listed here. Do check this link.

Yes, each of these topics deserve a separate blog. But, that’s all for now in this blog. Keep decoding !~

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