Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

Common Mistakes in C Programming

VIEWS: 26 PAGES: 21

Great presentation for web developers, programmers and other technology enthusiasts.

More Info
									Article: Common
Mistakes In C
Programming
By Khanh Ngo-Duy
Khanhnd@elarion.com
Seminar
Purpose
Common Mistakes
    struct and Memory Padding
    New line character
    Binary mode in fopen()
    strncpy()
    memset()
    fgets()
    Non-null-terminated string
    #include guard
    Get ID of a thread
    Buffer overflow, Stack overwrite
Purpose


Introduce common mistakes programmers
often gets into while writing C code
Get experiences to write better codes
Common Mistakes
1. struct and Memory Padding (1 of 5)

//sizeof() = 12
struct myStruct
{
   short s;
   int    i;
   char   c;
};
Common Mistakes
1. struct and Memory Padding (2 of 5)

//sizeof() = 8
struct myStruct
{
   int    i;
   short s;
   char   c;
};

//sizeof() = 8
struct myStruct
{
   char   c;
   short s;
   int    i;
};
Common Mistakes
1. struct and Memory Padding (3 of 5)


 Memory padding is done automatically by
 compiler
 Padding increases memory but makes app to
 run faster
 Re-order variables in struct (ascending or
 descending) → you can reduce padding ← your
 experience
Common Mistakes
1. struct and Memory Padding (4 of 5)

 Rules of padding:
  A variable of a specific type will be aligned at offset = multiple of size of
  that variable. If it is not so, padding will be added before it

  Total size of struct = multiple of size of largest variable in struct. If it is not
  so, padding will be added at the end of struct.

  Example:

     Variables of type int will be aligned at offset: 0, 4, 8, 12, 16 etc …

     Variables of type char will be aligned at offset: 0, 1, 2, 3, 4, 5 etc …

     Variables of type pointer will be aligned at offset: 0, 8, 16, 24, 32 etc ...
Common Mistakes
1. struct and Memory Padding (5 of 5)
 Sometimes, you want to avoid memory
 padding, you can use #pragma pack (1)
 directive
 It is useful in some specific situation
 Save memory but your app runs slower
#pragma pack(1)     /* set alignment to 1 byte boundary */
struct MyPackedData /* sizeof() = 10 → x64 architecture */
{
    char Data1;
    long Data2;
    char Data3;
};
#pragma pack(0)     /* Back to normal */
Common Mistakes
2. New line character
 New line character in Windows is different from
 Linux:
  In Windows, newline is denoted by 2 bytes: a combination of Carriage
  Return (ASCII value 13) and Line Feed (ASCII value 10)

  In Linux, newline is denoted by only 1 byte: the Line Feed character (ASCII
  value 10)
Common Mistakes
3. Binary mode in fopen()

 FILE *fopen(const char *path, const char *mode);

 In Windows, text-mode and binary-mode are
 differentiated. e.g “r”, “rb”, “w”, “wb” …
 In Linux, there is no text-mode. fopen() a lw a ys
 open file in bina ry-m o de . So, “r” and “rb” are
 the same. There is no error whether you pass
 “b” or not
   –   fopen(“myFile.txt”, “r”);    /* prefer to use this */
   –   fopen(“myFile.txt”, “rb”);       /* In Linux, both lines are same! */
Common Mistakes
4. strncpy() (1 of 2)

 char *strncpy(char *dest, const char *src, size_t n);

 strncpy() always tries to copy n character from
 src into dest. If (m<n) chars are copied → (n-m)
 number of zeros will be filled into dest →
 always copies n characters into dest
 So, the following codes might C R A S H !!!
      char str[5];
      strncpy(str, “abc”, 10); /* Will copy “abc” and 7 zeros into str */
Common Mistakes
4. strncpy() (2 of 2)




 The following codes is redundant
   –   char str[10];
   –   memset (str, 0, 10);     /* ← No need, strncpy() will do the thing */
   –   strncpy(str, “abc”, 10); /* Will copy “abc” and 7 zeros into str */
Common Mistakes
5. memset()


 O nly use memset() to initialize variables to
 ZERO
 N E V E R use memset() to initialize variables to
 any values rather than zero
 Since, memset() fills memory with units in byte
Common Mistakes
6. fgets()



 char *fgets(char *s, int size, FILE *stream);

 fgets() only reads at most (size -1) chars from
 stream into s and then adds '\0' at the end of s
 It reads only (size -1) characters
Common Mistakes
6. Non-null-terminated string


 When working with non-null-terminated string,
 do not use “%s”. Instead, use “%.*s”
   –   void display(char *msg)
   –   {
         • printf(“The msg: %.256s”, msg);
           •   printf(“The msg: %.*s”, 256, msg);   /* does the same thing */
   –   }
Common Mistakes
7. #include guard (1 of 3)
   P roblem :
     main.c                      file1.h                           file2.h                           mylib.h
#include “file1.h”   #include “mylib.h”               #include “mylib.h”                 extern int i;
#include “file2.h”
                     /* something belongs to file1 */ /* something belongs to file2 */




      When compiling main.c: mylib.h is included twice → declarations are
      overwritten

      mylib.h is opened tw ic e → compiler time definitely increases
Common Mistakes
7. #include guard (2 of 3)
   S o lution: #inc lude g ua rd
     main.c                      file1.h                           file2.h                           mylib.h
#include “file1.h”   #include “mylib.h”               #include “mylib.h”                 #ifndef MYLIB_H
#include “file2.h”                                                                       #define MYLIB_H
                     /* something belongs to file1 */ /* something belongs to file2 */
                                                                                         extern int i;

                                                                                         #endif




      When compiling main.c: mylib.h is included once!

      Depends on compiler (supports include guard optimisation or not): mylib.h
      is opened onc e or tw ic e → compiler time may reduce or not

      Most of compilers support “include guard optimisation feature”: the include
      guard is cached at the first call, later the file (mylib.h) will not be opened →
      compiler time is faster
Common Mistakes
7. #include guard (3 of 3)
   S o lution: #inc lude g ua rd (o ptim ized)
      main.c                    file1.h                          file2.h                         mylib.h
#include “file1.h”   #ifndef MYLIB_H                  #ifndef MYLIB_H                    #ifndef MYLIB_H
#include “file2.h”   #include “mylib.h”               #include “mylib.h”                 #define MYLIB_H
                     #endif                           #endif
                                                                                         extern int i;
                     /* something belongs to file1 */ /* something belongs to file2 */
                                                                                         #endif




      When compiling main.c: mylib.h is included once!

      mylib.h is opened onc e → faster

      Must insert #ifndef everywhere when calling #include → only use with very
      large project to reduce the compiler time

      U s eles s if the compiler supports “include guard optimisation feature”
Common Mistakes
8. Get ID of a thread



 Get ID of a process is easy (pid_t getpid(void);). How
 about thread?
  #include <sys/syscall.h>
  #define gettid()   syscall(__NR_gettid)
  printf ( "Thread ID: %d\n", gettid() );
Common Mistakes
9. Buffer Overflow, Stack Overwrite

 Don't write the following codes, it will overwrite
 some important data in your application:
  int GlobalBuffer[10][20];
  void InitializeBuffer()
  {
        int i, j;
        for (i = 0; I < 100; i++)                             /* You go out off the boundary */
            for (j = 0; j < 21; j++)                          /* Again, go out off the boundary */
                  GlobalBuffer[i][j] = 0;
  }


  You think you will never be silly like this? Yep, u'r rite, but sometimes you make mistake like this !
Thanks for watching
    If you see it useful → clap your hands :-)

								
To top