Common mistakes in C programming

21
Article: Common Article: Common Mistakes In C Mistakes In C Programming Programming By Khanh Ngo-Duy By Khanh Ngo-Duy [email protected] [email protected]

description

Describe some basic mistakes when programming with C

Transcript of Common mistakes in C programming

Page 1: Common mistakes in C programming

Article: Common Article: Common Mistakes In C Mistakes In C ProgrammingProgrammingBy Khanh Ngo-DuyBy Khanh [email protected]@elarion.com

Page 2: Common mistakes in C programming

SeminarSeminarPurposePurpose

Common MistakesCommon Mistakesstruct and Memory Paddingstruct and Memory PaddingNew line characterNew line characterBinary mode in fopen()Binary mode in fopen()strncpy()strncpy()memset()memset()fgets()fgets()Non-null-terminated stringNon-null-terminated string#include guard#include guardGet ID of a threadGet ID of a threadBuffer overflow, Stack overwriteBuffer overflow, Stack overwrite

Page 3: Common mistakes in C programming

PurposePurpose

Introduce common mistakes programmers Introduce common mistakes programmers often gets into while writing C codeoften gets into while writing C codeGet experiences to write better codesGet experiences to write better codes

Page 4: Common mistakes in C programming

Common MistakesCommon Mistakes1. struct and Memory Padding1. struct and Memory Padding (1 of 5) (1 of 5)

//sizeof() = 12struct myStruct{

short s;int i;char c;

};

Page 5: Common mistakes in C programming

Common MistakesCommon Mistakes1. struct and Memory Padding1. struct and Memory Padding (2 of 5) (2 of 5)

//sizeof() = 8struct myStruct{

int i;short s;char c;

};

//sizeof() = 8struct myStruct{

char c;short s;int i;

};

Page 6: Common mistakes in C programming

Common MistakesCommon Mistakes1. struct and Memory Padding1. struct and Memory Padding (3 of 5) (3 of 5)

Memory padding is done automatically by Memory padding is done automatically by compilercompiler

Padding increases memory Padding increases memory but makes app to but makes app to run fasterrun faster

Re-order variables in struct (ascending or Re-order variables in struct (ascending or descending) → you can reduce padding descending) → you can reduce padding ← your ← your experienceexperience

Page 7: Common mistakes in C programming

Common MistakesCommon Mistakes1. struct and Memory Padding1. struct and Memory Padding (4 of 5) (4 of 5)

Rules of padding:Rules of padding:A variable of a specific type will be aligned at offset = multiple of size of 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 itthat 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 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.so, padding will be added at the end of struct.

Example:Example:

Variables of type int will be aligned at offset: 0, 4, 8, 12, 16 etc …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 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 ...Variables of type pointer will be aligned at offset: 0, 8, 16, 24, 32 etc ...

Page 8: Common mistakes in C programming

Common MistakesCommon Mistakes1. struct and Memory Padding1. struct and Memory Padding (5 of 5) (5 of 5)

Sometimes, you want to avoid memory Sometimes, you want to avoid memory padding, you can use padding, you can use #pragma pack (1)#pragma pack (1) directivedirective

It is useful in some specific situationIt is useful in some specific situation

Save memory Save memory but your app runs slowerbut 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 */

Page 9: Common mistakes in C programming

Common MistakesCommon Mistakes2. New line character2. New line character

New line character in Windows is different from New line character in Windows is different from Linux:Linux:

In Windows, newline is denoted by 2 bytes: a combination of Carriage In Windows, newline is denoted by 2 bytes: a combination of Carriage Return (ASCII value 13) and Line Feed (ASCII value 10)Return (ASCII value 13) and Line Feed (ASCII value 10)

In Linux, newline is denoted by only 1 byte: the Line Feed character (ASCII In Linux, newline is denoted by only 1 byte: the Line Feed character (ASCII value 10)value 10)

Page 10: Common mistakes in C programming

Common MistakesCommon Mistakes3. Binary mode in fopen()3. Binary mode in fopen()

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

In Windows, In Windows, text-modetext-mode and and binary-modebinary-mode are are differentiated. e.g differentiated. e.g “r”“r”, , “rb”“rb”, , “w”“w”, , “wb”“wb” … …

In Linux, there is no text-mode. fopen() In Linux, there is no text-mode. fopen() a lw aysa lw ays open file in open file in binary-modebinary-mode . So, . So, “r”“r” and and “rb”“rb” are are the same. There is no error whether you pass the same. There is no error whether you pass “b”“b” or not or not

– fopen(“myFile.txt”, “r”);fopen(“myFile.txt”, “r”); /* prefer to use this *//* prefer to use this */– fopen(“myFile.txt”, “rb”);fopen(“myFile.txt”, “rb”); /* In Linux, both lines are same! *//* In Linux, both lines are same! */

Page 11: Common mistakes in C programming

Common MistakesCommon Mistakes4. strncpy() 4. strncpy() (1 of 2)(1 of 2)

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

strncpy() always tries to copy strncpy() always tries to copy nn character from character from srcsrc into into destdest. If . If (m<n)(m<n) chars are copied → chars are copied → (n-m)(n-m) number of zeros will be filled into number of zeros will be filled into destdest → → always copies always copies nn characters into characters into destdest

So, the following codes might So, the following codes might C R AS H !!!C R AS H !!!charchar str[ str[55];];strncpy(str, strncpy(str, “abc”“abc”, , 1010); ); /* Will copy “abc” and 7 zeros into str *//* Will copy “abc” and 7 zeros into str */

Page 12: Common mistakes in C programming

Common MistakesCommon Mistakes4. strncpy() 4. strncpy() (2 of 2)(2 of 2)

The following codes is redundantThe following codes is redundant– charchar str[ str[1010];];– memset (str, memset (str, 00, , 1010);); /* ← No need, strncpy() will do the thing *//* ← No need, strncpy() will do the thing */– strncpy(str, strncpy(str, “abc”“abc”, , 1010); ); /* Will copy “abc” and 7 zeros into str *//* Will copy “abc” and 7 zeros into str */

Page 13: Common mistakes in C programming

Common MistakesCommon Mistakes5. memset()5. memset()

OnlyOnly use memset() to initialize variables to use memset() to initialize variables to ZEROZERO

N E V E RN E V E R use memset() to initialize variables to use memset() to initialize variables to any values rather than zeroany values rather than zero

Since, memset() fills memory with units in Since, memset() fills memory with units in bytebyte

Page 14: Common mistakes in C programming

Common MistakesCommon Mistakes6. fgets()6. fgets()

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

fgets() only reads at most fgets() only reads at most (size -1)(size -1) chars from chars from streamstream into into ss and then adds and then adds '\0''\0' at the end of at the end of ss

It reads only (size -1) charactersIt reads only (size -1) characters

Page 15: Common mistakes in C programming

Common MistakesCommon Mistakes6. Non-null-terminated string6. Non-null-terminated string

When working with When working with non-null-terminatednon-null-terminated string, string, do not use do not use “%s”“%s”. Instead, use . Instead, use “%.*s”“%.*s”

– voidvoid display( display(charchar **msg)msg)– {{

• printf(printf(“The msg: %.256s”“The msg: %.256s”, msg);, msg);• printf(printf(“The msg: %.*s”“The msg: %.*s”, , 256256, msg);, msg); /* does the same thing *//* does the same thing */

– }}

Page 16: Common mistakes in C programming

Common MistakesCommon Mistakes7. #include guard7. #include guard (1 of 3) (1 of 3)

Problem:Problem:

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

mylib.h is opened mylib.h is opened tw ic etw ic e → compiler time → compiler time definitelydefinitely increases increases

main.c file1.h file2.h mylib.h#include “file1.h”#include “file2.h”

#include “mylib.h”

/* something belongs to file1 */

#include “mylib.h”

/* something belongs to file2 */

extern int i;

Page 17: Common mistakes in C programming

Common MistakesCommon Mistakes7. #include guard7. #include guard (2 of 3) (2 of 3)

S olution: S olution: #inc lude g uard#inc lude g uard

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

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

Most of compilers support “include guard optimisation feature”: the include 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 → guard is cached at the first call, later the file (mylib.h) will not be opened → compiler time is fastercompiler time is faster

main.c file1.h file2.h mylib.h#include “file1.h”#include “file2.h”

#include “mylib.h”

/* something belongs to file1 */

#include “mylib.h”

/* something belongs to file2 */

#ifndef MYLIB_H#define MYLIB_H

extern int i;

#endif

Page 18: Common mistakes in C programming

Common MistakesCommon Mistakes7. #include guard7. #include guard (3 of 3) (3 of 3)

S olution: S olution: #inc lude g uard (optim ized)#inc lude g uard (optim ized)

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

mylib.h is opened mylib.h is opened onc eonc e → faster → faster

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

Us eles sU s eles s if the compiler supports “include guard optimisation feature” if the compiler supports “include guard optimisation feature”

main.c file1.h file2.h mylib.h#include “file1.h”#include “file2.h”

#ifndef MYLIB_H#include “mylib.h”#endif

/* something belongs to file1 */

#ifndef MYLIB_H#include “mylib.h”#endif

/* something belongs to file2 */

#ifndef MYLIB_H#define MYLIB_H

extern int i;

#endif

Page 19: Common mistakes in C programming

Common MistakesCommon Mistakes8. Get ID of a thread8. Get ID of a thread

Get ID of a process is easy (Get ID of a process is easy (pid_tpid_t getpid(getpid(voidvoid););). How ). How about thread?about thread?

#include#include <sys/syscall.h> <sys/syscall.h>#define#define gettid() gettid() syscall(__NR_gettid)syscall(__NR_gettid)printf ( printf ( "Thread ID: %d\n""Thread ID: %d\n", gettid() );, gettid() );

Page 20: Common mistakes in C programming

Common MistakesCommon Mistakes9. Buffer Overflow, Stack Overwrite9. Buffer Overflow, Stack Overwrite

Don't write the following codes, it will overwrite Don't write the following codes, it will overwrite some important data in your application:some important data in your application:

intint GlobalBuffer[ GlobalBuffer[1010][][2020];];voidvoid InitializeBuffer() InitializeBuffer(){{ intint i, j; i, j; forfor (i = (i = 00; I < ; I < 100100; i++) ; i++) /* You go out off the boundary *//* You go out off the boundary */ forfor (j = (j = 00; j < ; j < 2121; j++) ; j++) /* Again, go out off the boundary *//* Again, go out off the boundary */ GlobalBuffer[i][j] = GlobalBuffer[i][j] = 00;;}}

You think you will never be silly like this? Yep, u'r rite, but sometimes you make mistake like this !You think you will never be silly like this? Yep, u'r rite, but sometimes you make mistake like this !

Page 21: Common mistakes in C programming

Thanks for watchingThanks for watchingIf you see it useful → clap your hands :-)If you see it useful → clap your hands :-)