the ANSI standard was completed in 1989 and ratified as ANSI X3.159-1989 "Programming Language C". this version of the language is often referred to as "ANSI C". later on sometimes the label "C89" is used to distinguish it from C90 but using the same labeling method. the same standard as C89 was ratified by the International Organization for Standardization as ISO/IEC 9899:1990, with only formatting changes, which is sometimes referred to as "C90". therefore, the terms "C89" and "C90" refer to essentially the same language
if you are concerned about strict adherence to the ISO standard, you should use the -ansi
option when you compile your programs with the gcc
. and if you want be precious then use -Wall
option
you are encouraged to conform to the standards, as doing so helps portability between compilers
start with /*
and finish with */
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
an identifier is a sequence of nondigit characters (including the underscore _ and the lower-case and upper-case letters) and digits. the first character shall be a nondigit character
const int *ptr_to_constant ; int *const constant_ptr ; typedef int *int_ptr ; const int_ptr const_ptr ;
there are four kinds of scopes: file, block, function and function prototype
a label name is the only kind of identifier that has function scope. it can be used in a
goto
statement anywhere in the function in which it appears, and is declared implicitly by its syntactic
appearance followed by a : and a statement
int main (int argc , char** argv , char** envp) { int Iv ; long int Lv ; signed int Pv ; unsigned int Kv ; short int Sv ; _Bool B1var = 0 ; _Bool B2var = 1 ; float Fv ; double Dv ; long double Dd ; return sizeof (Fv) ; }
приведение типов:
int atoi (char* s)
long atol (char* s)
float atof (char* s)
int main (int argc, char** argv, char** envp) { int x = 11 ; { int x = 12 ; /* not visible outside this block */ } return x ; /* return 11 */ }
goto label_identifier ; continue ; break ; return expression ;
if (condition) { ... } else { ... }
#include <stdio.h> #include <stdlib.h> int main (int argc, char** argv, char** envp) { if (argc != 2) { puts ("usage: prg arg") ; exit (1) ; } ; int x = atoi (argv[1]) ; switch (x) { case 1 : puts ("1") ; break ; case 2 : puts ("2") ; break ; default: puts ("3") ; break ; } ; return 0 ; }
for ( expression-1 ; expression-2 ; expression-3 ) { statement(s) }
while (cond_1) { /*...*/ while (cond_2) { /*...*/ if (cond_3) goto end_loop1; /*...*/ } /*...*/ end_loop1: ; }
do { /* ... */ while (cond)
use macro #define
for that
#include <stdio.h> #define MAX_VALUE 100 /* global but not variable ! */ int main (int argc, char** argv, char** envp) { printf ("%i\n", MAX_VALUE) ; return 0 ; }
static
variable in function #include <stdio.h> void myfAunc (void) ; int main (int argc, char** argv, char** envp) { myfAunc () ; myfAunc () ; myfAunc () ; return 0 ; } void myfAunc () { static count = 0 ; printf ("%i\n", count++) ; }
and you can do it with ADT also. for example:
#include <stdio.h> #include <stdlib.h> typedef struct { int num ; double factor ; } XSTRUCT ; XSTRUCT myfun (int) ; float main (int argc, char** argv, char** envp) { XSTRUCT r ; r = myfun (2) ; printf ("%2i * e = %7.4f\n", r.num , r.factor) ; r = myfun (3) ; printf ("%2i * e = %7.4f\n", r.num , r.factor) ; r = myfun (4) ; printf ("%2i * e = %7.4f\n", r.num , r.factor) ; return 0 ; } XSTRUCT myfun (int x) { static XSTRUCT mystate ; mystate.num = mystate.num + x ; mystate.factor = mystate.num * 2.71828 ; return mystate ; }
interface - file ifcace.h :
#define MAX 100 #define VAL 12.8 enum { RED = 1 , GREEN = 2 , YELLOW = 7 } ; int myfunc1 (int) ; double myfunc2 (double) ;
implementation - file module1.c :
int myfunc1 (int x) { return x + 2 ; }
implementation - file module2.c :
double myfunc2 (double x) { return x * 2 ; }
main program - file main.c :
#include <stdio.h> #include "iface.h" int main (int argc, char** argv, char** envp) { double x = myfunc2 (VAL + GREEN) ; printf ("%7.2f\n", (x + (double) myfunc1 (MAX)) ) ; return 0 ; }
and now in shell: gcc -Wall module1.c module2.c main.c
C89 has no "pass-by-reference" functionality - all params are passed by value and
&
just means "addressof" and is a way to formulate a pointer from a variable. however, consider this:
void func (int* x) { *x = 4; } void callfunc () { int x = 7 ; func (&x) ; }
you're passing the value of the address of the local variable 'x' to 'func' as a pointer argument. a pointer is a memory address containing another memory address. underneath a reference to object is typically the machine address of the object - attach gdb to a process and you'll be able to see this
what happens if you assign to a reference?
you change the state of the referent ('referent' is the object to which the reference refers)
what happens if you return a reference?
the function call can appear on the left hand side of an assignment operator
#include <stdlib.h> int one (void) { return 1 ; } ; int two (void) { return 2 ; } ; int main (int argc, char** argv, char** env) { int (*z) () ; if (atoi (argv[1]) > 10) { z = &one ; } else { z = &two ; } ; return z() ; }
int myfun (int) ; int twice (int fp(int), int x) { return fp (x) ; } int main (int arc, char** argv, char** env) { return twice (&myfun, 6) ; } int myfun (int x) { return x * 2 ; }
one very simple way: enum
type. for exampe :
enum { RED = 1 , GREEN = 2 , YELLOW = 7 } ;
enum identifier { enumerator-list }
and two more complicated ways : stuct and union. for example :
#include <stdio.h> #include <stdlib.h> /* product type */ typedef struct { int num ; double factor ; char* name ; int rate[10] ; } XSTRUCT ; /* sum type */ typedef union { int num ; char sign ; } YUNION ; int main (int argc, char* argv, char* envp) { YUNION z ; XSTRUCT k ; scanf ("%i", &(z.num)) ; printf ("%i\n", z.num) ; return 0 ; }
ADT can be nested! for example:
#include "stdio.h" typedef union { char* descript ; int identifyer ; } Sum ; typedef struct { char* name ; float salary ; } Product ; typedef union { Sum data1 ; Product data2 ; } Complex1 ; typedef struct { Product set1 ; Sum set2 ; } Complex2 ; int main (int arc, char** argv, char** env) { Sum y = { .descript = "Bob" } ; Complex1 x = { .data1 = y , .data2 = { .salary = 3.2 , .name = "Alice" } } ; printf ("salary %12.3f\n", x.data2.salary) ;; Sum a = { .identifyer = 14 } ; Complex2 b = { .set1 = { .salary = 4.7 , .name = "Bob" } , .set2 = a } ; printf ("name %12s\n", b.set1.name) ; return 0 ; }
to assigning a value to an array with in a structure:
typedef struct _temp_t { int16_t _values[4]; } TEMP_T; void func (void) { TEMP_T *temps; int x = 5; temps._values[0] = x; }getting an error :
... error: request for member '_values' in something not a structure or union TEMP_T *temps;
drop the * from that line. then it will be a true TEMP_T object instead of a pointer. alternatively, malloc
some memory to temps and then use:
temps->_values[0] = x;
int myfun (int) ; typedef struct { int age ; int (*funPtr) (int) ; } F ; int main (int arc, char** argv, char** env) { F a ; a.age = 32 ; a.funPtr = &myfun ; return a.funPtr (a.age) ; } int myfun (int x) { return x * 2 ; }
a notorious source of program bugs is trying to put more characters in a string than fit in its allocated size. when writing code that extends strings or moves characters into a pre-allocated array, you should be very careful to keep track of the length of the text and make explicit checks for overflowing the array. many of the library functions do not do this for you! remember also that you need to allocate an extra byte to hold the null character that marks the end of the string
it's fairly common for programs to have a need to do some simple kinds of lexical analysis and parsing, such as splitting a command string up into tokens. you can do this with the strtok function, declared in the header file string.h
char * strtok (char *restrict newstring, const char *restrict delimiters)
a string can be split into tokens by making a series of calls to the function strtok. the string to be split up is passed as the 'newstring' argument on the first call only. the strtok function uses this to set up some internal state information. subsequent calls to get additional tokens from the same string are indicated by passing a null pointer as the newstring argument. calling strtok with another non-null newstring argument reinitializes the state information
the 'delimiters' argument is a string that specifies a set of delimiters that may surround the token being extracted. all the initial characters that are members of this set are discarded. the first character that is not a member of this set of delimiters marks the beginning of the next token. the end of the token is found by looking for the next character that is a member of the delimiter set. this character in the original string newstring is overwritten by a null character, and the pointer to the beginning of the token in newstring is returned. on the next call to strtok, the searching begins at the next character beyond the one that marked the end of the previous token. note that the set of delimiters do not have to be the same on every call in a series of calls to strtok
if the end of the string newstring is reached, or if the remainder of string consists only of delimiter characters, strtok returns a null pointer
arguments and returned values are double
trigonometry | sin(x), cos(x), tan(x) |
inverse trigonometry | asin(x), acos(x), atan(x) arctan(y/x) atan2(y,x) |
hyperbolic trigonometry | sinh(x), cosh(x), tanh(x) |
exp , logs | exp(x), log(x), log10(x) |
remainders | modf(x,*ip), fmod(x,y) |
powers | pow(x,y), sqrt(x) |
rounding | ceil(x), floor(x), fabs(x) |
you should compile the source with
-lm
option
there are two library functions provided by C defined under <stdlib.h> header file to facilitate dynamic memory allocation:
// ptr = (cast-type*) malloc (byte-size)
int* ptr = malloc (100 * sizeof(int)) ;
free (ptr)
malloc()
and
free()
don't work the same way on every OS.
malloc()
is system/compiler dependent.
malloc()
gets memory from the kernel when it needs it, and typically in units of pages. memory can only be removed from your program in units of pages, and even that is unlikely to be observed
since you may only want 4 bytes, and the OS needs to work in pages (often 4k),
malloc()
takes a page, and puts it's own information in there so it can keep track of what you have allocated and freed from that page. when you call
free()
, it takes your pointer, backs up to where it's data is, and operates on that
/* read from file (first arg) */ /* clang -std=c89 file_write.c */ #include <stdio.h> int main (int argc, char **argv, char** envp) { char input_str[256] ; FILE *fd = fopen (argv[1], "r") ; fscanf (fd, "%255[^\n]", &input_str) ; fclose (fd) ; printf ("%s\n", input_str) ; return(0); }
/* write to file (first arg) with word (secont arg) */ /* clang -std=c89 file_write.c */ #include <stdio.h> int main (int argc, char **argv, char** envp) { FILE *fd = fopen ("something.txt", "w") ; fprintf (fd, "hello %s", "Hello & goodby") ; fclose (fd) ; return(0) ; }
пример - поиск в игрушечной базе данных
главную функцию программы поместим в файл main.c
// file main.c extern void init (void) ; extern char* countryName (int) ; int main (void) { init () ; for ( ; ;) { int uid ; printf ("userID : ") ; scanf ("%i", &uid) ; printf ("%s \n", countryName (uid)) ; } ; return 0 ; }
заголовочный файл базы:
// file simpleDB.h #include <stdio.h> #include <string.h> struct user { char* name ; int uid ; } ; struct phone { char* number ; char* name ; } ; struct code { char* code ; char* country ; } ; struct user users [5] ; struct phone phones[4] ; struct code codes [2] ; char mcod [4] ; char* nullUser = "user not found" ; char* nullPhone = "phone not found" ; char* nullCode = "code not found" ; char* nullCountry = "country not found" ;
код базы :
// file simpleDB.c #include "simpleDB.h" void init (void) { struct user a, b, c, d, s ; struct code e, f ; struct phone g, h, i, j ; a.name = "Neo" ; a.uid = 1 ; users[0] = a ; b.name = "Cipher" ; b.uid = 2 ; users[1] = b ; c.name = "Morpheus" ; c.uid = 3 ; users[2] = c ; d.name = "Trinity" ; d.uid = 4 ; users[3] = d ; s.name = "Switch" ; s.uid = 5 ; users[4] = s ; g.number = "123444" ; g.name = "Neo" ; phones[0] = g ; h.number = "456555" ; h.name = "Cipher" ; phones[1] = h ; i.number = "567888" ; i.name = "Morpheus" ; phones[2] = i ; j.number = "" ; j.name = "Trinity" ; phones[3] = j ; e.country = "USA" ; e.code = "123" ; codes[0] = e ; f.country = "RUS" ; f.code = "456" ; codes[1] = f ; printf ("db initiated\n") ; } char* findUser (int d) { int i = 0 ; for (i ; i < 5 ; i++) if (users[i].uid == d) return users[i].name ; return nullUser ; } ; char* getPhone (char* u) { int i = 0 ; for (i ; i < 4 ; i++) if (phones[i].name == u) return phones[i].number ; return nullPhone ; } ; char* getCode (char* p) { // ---- dirty hack: *mcod = '\0' ; strncpy(mcod, p, 3) ; // ---- canonical: // memcpy(mcod, p, 3) ; // mcod[3] = '\0' ; if (strlen (mcod) == 3) return mcod ; else return nullCode ; } ; char* findByCode (char* mycod) { int i = 0 ; for (i ; i < 2 ; i++) if (!strcmp (mycod, codes[i].code)) return codes[i].country ; return nullCode ; }
будем кодить функцию char* countryName (int), помещая сей продукт в файл work.c
// file work.c #include <setjmp.h> extern char* nullUser ; extern char* nullPhone ; extern char* nullCode ; extern char* nullCountry ; char* countryName (int d) { char *ans ; jmp_buf b ; int k = setjmp (b) ; if (k == 0) { ans = findUser (d) ; if (!strcmp (ans, nullUser)) longjmp (b, 1) ; ans = getPhone (ans) ; if (!strcmp (ans, nullPhone)) longjmp (b, 2) ; ans = getCode (ans) ; if (!strcmp (ans, nullCode)) longjmp (b, 3) ; ans = findByCode (ans) ; if (!strcmp (ans, nullCountry)) longjmp (b, 4) ; return ans ; } return k == 1 ? nullUser : k == 2 ? nullPhone : k == 3 ? nullCode : k == 4 ? nullCountry : "unknown error" ; }
#include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { printf ("%s\n", getenv ("QUERY_STRING")); return 0; }
POST data is appended to the request header, after a double newline. in a CGI-BIN environment, you read it from STDIN:
#include <stdio.h> #include <stdlib.h> int main (int argc, char* argv[]) { const char * __restrict__ len_ = getenv ("CONTENT_LENGTH") ; int len = strtol (len_, NULL, 10) ; char* postdata ; postdata = malloc (len + 1) ; fgets (postdata, len + 1, stdin) ; printf ("your data is : %s\n", postdata) ; free (postdata) ; return 0 ; }
be warned that the server is not required to send you an EOF character (or some termination indicator) at the end of the POST data. never read more than CONTENT_LENGTH bytes