Upload
larion
View
5.772
Download
3
Embed Size (px)
DESCRIPTION
Describe some basic mistakes when programming with C
Citation preview
Article: Common Article: Common Mistakes In C Mistakes In C ProgrammingProgrammingBy Khanh Ngo-DuyBy Khanh [email protected]@elarion.com
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
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
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;
};
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;
};
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
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 ...
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 */
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)
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! */
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 */
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 */
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
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
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 */
– }}
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;
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
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
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() );
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 !
Thanks for watchingThanks for watchingIf you see it useful → clap your hands :-)If you see it useful → clap your hands :-)