• No results found

COMMON PROGRAMMING PROBLEMS IN C

In document C Lecture 1 (Page 118-127)

* There are a number of common programming pitfalls in C that even trap experienced programmers:

1: Confusing "=" (assignment operator) with "==" (equality operator). For example:

if ( x = 1 ) {

}

-- is bogus, and so is: for ( x == 1; ...

2: Confusing precedence of operations in expressions. When in doubt, use parentheses to enforce precedence.

3: Confusing structure-member operators. If "struct_val" is a structure and "struct_ptr" is a pointer to a structure, then:

struct_val->myname -- is wrong and so is: struct_ptr.myname

4: Using incorrect formatting codes for "printf()" and "scanf()". Using a "%f" to print an "int", for example, can lead to bizarre output.

5: Remember that the actual base index of an array is 0, and the final index is 1 less than the declared size. For example:

int data[20]; ... for ( x = 1; x <= 20; ++x ) { printf( "%d\n", data[x] ); }

-- will give invalid results when "x" is 20. Since C does not do bounds checking, this one might be hard to catch.

6: Muddling syntax for multidimensional arrays. If: data[10][10]

-- is a two-dimensional array, then: data[2][7]

-- will select an element in that array. However: data[ 2, 7 ]

-- will give invalid results but not be flagged as an error by C.

7: Confusing strings and character constants. The following is a string: "Y"

-- as opposed to the character constant: 'Y'

This can cause troubles in comparisons of strings against character constants. 8: Forgetting that strings end in a null character ('\0'). This means that a string will always be one character bigger than the text it stores. It can also cause trouble if a string is being created on a character-by-character basis, and the program doesn't tack the null character onto the end of it.

9: Failing to allocate enough memory for a string -- or, if pointers are declared, to allocate any memory for it at all.

10: Declaring a string with a fixed size and then assigning it to a string literal: char a[256] = "This doesn't work!";

11: Failing to check return values from library functions. Most library functions return an error code; while it may not be desireable to check every invocation of "printf()", be careful not to ignore error codes in critical operations.

Of course, forgetting to store the value returned by a function when that's the only way to get the value out of it is a bonehead move, but people do things like that every now and then.

12: Having duplicate library-function names. The compiler will not always catch such bugs.

13: Forgetting to specify header files for library functions.

14: Specifying variables as parameters to functions when pointers are supposed to be specified, and the reverse. If the function returns a value through a

parameter, that means it must be specified as a pointer: myfunc( &myvar );

The following will not do the job: myfunc( myvar );

Remember that a function may require a pointer as a parameter even if it doesn't return a value, though as a rule this is not a good programming practice.

15: Getting mixed up when using nested "if" and "else" statements. The best way to avoid problems with this is to always use brackets. Avoiding complicated "if" constructs is also a good idea; use "switch" if there's any choice in the matter. Using "switch" is also useful even for simple "if" statements, since it makes it easier to expand the construct if that is necessary.

16: Forgetting semicolons -- though the compiler usually catches this -- or adding one where it isn't supposed to be -- which it usually doesn't. For example:

for( x = 1; x < 10; ++x ); {

printf( "%d\n", x ) }

-- never prints anything.

17: Forgetting "break" statements in "switch" constructs. As commented earlier, doing so will simply cause execution to flow from one clause of the "switch" to the next.

18: Careless mixing and misuse of signed and unsigned values, or of different data types. This can lead to some insanely subtle bugs. One particular problem to watch out for is declaring single character variables as "unsigned char". Many I/O functions will expect values of "unsigned int" and fail to properly flag EOF. It is recommended to cast function arguments to the proper type even if it appears that type conversion will take care of it on its own.

19: Confusion of variable names. It is recommended that such identifiers be unique in the first 6 characters to ensure portability of code.

20: In general, excessively tricky and clever code. Programs are nasty beasts and even if it works, it will have to be modified and even ported to different languages. Maintain a clean structure and do the simple straightforward thing, unless it imposes an unacceptable penalty.

STDIO.H

stdio.h, which stands for "standard input/output header", is the header in the C standard library that contains macro definitions, constants, and declarations of functions and types used for various standard input and output operations. Functions declared in stdio.h are extremely popular, since as a part of the C standard library, they are guaranteed to work on any platform that supports C Functions declared in stdio.h can generally be divided into two categories: the functions for file manipulation and the functions for input-output manipulation.

Name Notes

File manipulation functions

fclose closes a file associated with the FILE * value passed to it

fopen, freopen opens a file for certain types of reading or writing

remove removes a file (deletes it)

rename renames a file

rewind acts as if fseek(stream, 0L, SEEK_SET) was called for the stream

passed, and then its error indicator cleared

Input-output manipulation functions

clearerr clears end-of-file and error indicators for a given stream

feof checks whether an stream end-of-file indicator has been set for a given

ferror checks whether an error indicator has been set for a given stream

fflush forces any pending buffered output to be written to the file

associated with a given stream

fgetpos stores the file position indicator of the stream associated by its

first argument (a FILE *) to its second argument (a fpos_t *)

fgetc returns one character from a file

fgets gets a string from the file (ending at newline or end-of-file)

fputc writes one character to a file

fputs writes a string to a file

ftell returns a file-position indicator which can then be passed to fseek

fseek seeks through a file

fread reads data from a file

fwrite writes data to a file

getc

reads and returns a character from a given stream and advances the file position indicator; it is allowed to be a macro with the same effects as fgetc, except that it may evaluate the stream more than once

getchar has the same effects as getc(stdin)

gets reads characters from stdin until a newline is encountered and stores them in its only argument

printf, vprintf used to print to the standard output stream

fprintf, vfprintf used to print to a file

sprintf, snprintf, vsprintf, vsnprintf

used to print to a char array (C string)

perror writes an error message to stderr

putc writes and returns a character to a stream and advances the file position indicator for it; equivalent to fputc, except that a macro version may evaluate the stream more than once

putchar,

fputchar has the same effects as putc(stdout)

fscanf, vfscanf used to input from a file

sscanf,

vsscanf used to input from a char array (e.g., a C string)

setbuf, setvbuf sets the buffering mode for a given stream

tmpnam creates a temporary filename

ungetc pushes a character back onto a stream

puts outputs a character string to stdout

conio.h

conio.h is a C header file used in old MS-DOS compilers to create text user interfaces. It is not described in The C Programming Language book , and it is not part of the C standard library, ISO C nor is it required by POSIX.

Member functions

int kbhit(void) Determines if a keyboard key was pressed.

int getch(void) Reads a character directly from the console without buffer, and without echo.

int getche(void) Reads a character directly from the console without buffer, but with echo.

char *cgets(char *buffer) Reads a string directly from the console.

int cscanf(char *format,

arg0,... argn) Reads formated values directly from the console.

int putch(int c) Writes a character directly to the console.

int cputs(const char *string) Writes a string directly to the console.

int cprintf(const char *format, arg0,... argn)

Formats values and writes them directly to the console.

string.h

string.h is the header in the C standard library for the C programming language which contains macro definitions, constants, and declarations of functions and types used not only for string handling but also various memory handling functions; the name is thus something of a misnomer.

Functions declared in string.h are extremely popular, since as a part of the C standard library, they are guaranteed to work on any platform which supports C. However, some security issues exist with these functions, such as buffer

overflows, leading programmers to prefer safer, possibly less portable variants.

Functions

Name Notes

void *memcpy(void *dest, const void *src, size_t n);

copies n bytes between two memory areas, which must not overlap

void *memmove(void *dest, const void *src, size_t n);

copies n bytes between two memory areas; unlike with memcpy the areas may overlap

void *memchr(const void *s, char c, size_t n);

returns a pointer to the first occurrence of c in the first n bytes of s, or NULL if not found

int memcmp(const void *s1, const void *s2, size_t n);

compares the first n characters of two memory areas

void *memset(void *, int,

size_t); overwrites a memory area with a byte pattern

char *strcat(char *dest, const

char *src); appends the string src to dest

char *strncat(char *, const char *, size_t);

appends at most n characters of the string src to dest

char *strchr(const char *, int); locates a character in a string, searching from the beginning

char *strrchr(const char *, int); locates a character in a string, searching from the end

int strcmp(const char *, const

char *); compares two strings lexicographically

int strncmp(const char *, const char *, size_t);

compares up to the first n bytes of two strings lexicographically

int strcoll(const char *, const char *);

compares two strings using the current locale's

collating order

char *strcpy(char *toHere,

char *strncpy(char *toHere, const char *fromHere, size_t);

copies up to n bytes of a string from one location to another

char *strerror(int); returns the string representation of an error number e.g. errno (not thread-safe)

size_t strlen(const char *); finds the length of a C string

size_t strspn(const char *s, const char *accept);

determines the length of the maximal initial substring of s consisting entirely of characters in accept

size_t strcspn(const char *s, const char *reject);

determines the length of the maximal initial

substring of s consisting entirely of characters not in reject

char *strpbrk(const char *s, const char *accept);

finds the first occurrence of any character in accept in s

char *strstr(const char *haystack, const char *needle);

finds the first occurrence of the string "needle" in the longer string "haystack".

char *strtok(char *, const char *);

parses a string into a sequence of tokens; non- thread safe in the spec, non-reentrant

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

transforms src into a collating form, such that the numerical sort order of the transformed string is equivalent to the collating order of src.

In document C Lecture 1 (Page 118-127)

Related documents