Ceci est à l’origine un post sur mon blog, mais je me suis dit que j’allais vous partager quelques codes C amusants.
Le C est un langage que je n’ai pas pratiqué depuis bien longtemps. Il faut dire, hormis la programmation système et quelques niches, on ne le voit pas trop ailleurs. En tout cas au travail, je n’ai jamais eu à en faire. Et d’un point de vue personnel, je suis plus sur d’autres langages comme Python ou Haskell. Mais il n’empêche que j’ai stocké, au gré de mes pérégrinations, quelques liens intéressants sur ce qu’on peut faire d’amusant en poussant ce langage hors des sentiers battus.
- Une classe string
- Une implémentation de vector
- Gestion des exceptions
- Implémenter la libC
- Les mots-clefs du C
- Un ECS en C
Une classe string
Toute personne ayant fait du C le dira (sauf @Taurre mais il est de mauvaise foi ), la gestion des chaînes de caractères est très… sommaire, par rapport à d’autres langages. Allouer des tableaux de char soi-même, c’est sympa 5 min, mais bon, si l’on avait une vraie encapsulation, comme les classes string
disponibles dans quasiment tous les langages venus après C, ça serait mieux. Ça tombe bien, @Maëlan l’a fait, à base de listes chaînées pour stocker des sous-chaînes et faciliter les opérations de concaténation, etc.
Le code a été posté de base sur le Site du Zéro, mais par soucis de postérité, on va le remettre ici.
/**
*** String.h
***
*** module: String − header file
*** function: provides the `String` object type (abstract data type) and
*** functions for using it; the `String` object type is designed to
*** manage dynamic and/or large characters strings.
*** This header is the interface file of the module, it provides
*** the incomplete type declaration of `String` (because `String`
*** is an ADT) and the prototype of the usable functions.
*** author: Maëlan (aka Maëlan44)
*** (see http://www.siteduzero.com/membres-294-232877.html )
*** website: http://www.siteduzero.com/forum-83-683766-p1-une-implementation-de-string-en-c.html
***
*** last modified: 09/07/2011, 15:15
***
**/
#ifndef INCLUDED_STRING_2011_08_25_17_30_MM
#define INCLUDED_STRING_2011_08_25_17_30_MM
#include <stddef.h> // for size_t
#include <stdio.h> // for FILE
/*** TYPES ***/
/* the String object, to manage large and/or dynamic strings;
it takes the form of a linked list of datas including an allocated block
and informations concerning this block and the linked list */
typedef struct String String;
/*** FUNCTIONS ***/
/* Creates a String object filled with the content of the string passed; if NULL
is passed, lets it null (ie. empty and with no capacity) */
String* String_new(const char*);
/* Creates an empty String object with at least the capacity specified */
String* String_newEmpty(size_t);
/* Creates a new String object that is a copy of the passed one */
String* String_copy(const String*);
/* Deletes completely the String object */
void String_del(String*);
/* Returns the size of the String */
/*extern inline*/ size_t String_size(const String*);
/* idem */
#define String_length String_size
/* Returns the capacity of the String */
/*extern inline*/ size_t String_capacity(const String*);
/* boolean: Is the String empty (ie. the size is zero)? */
/*extern inline*/ int String_isEmpty(const String*);
/* boolean: Is the String null (ie. the size and capacity are zero)? */
/*extern inline*/ int String_isNull(const String*);
/* Empties the String, setting its size to zero without altering its capacity */
void String_empty(String*);
/* Empties the String, setting its size and capacity to zero */
void String_clear(String*);
/* Returns the character of index specified in the String; returns '\\0' if the
index is out of range */
char String_char(/*const*/ String*, size_t);
/* Sets the character of index specified in the String to the value specified;
if it is '\\0', then this index will become the end of the String; if the
index is out of range, the linked String is not modified */
void String_setChar(String*, size_t, char);
/* compare the two Strings the same way as strcmp does */
int String_cmp(const String*, const String*);
/* compare the String object with the ”regular” characters string, the same way
as strcmp does */
int String_cmpCStr(const String*, const char*);
/* returns the index of the 1st occurrence of the character in the String, or a
negative number if the the character is no found */
ptrdiff_t String_searchChar(const String*, char c);
/* returns the index of the last occurrence of the character in the String, or a
negative number if the the character is no found */
ptrdiff_t String_searchLastChar(const String*, char c);
/* Concatenates a copy of the 2nd String object to the first; returns the final
String, or NULL if there was an error */
String* String_concat(String*, const String*);
/* Write the content of the String on the stream */
void String_fput(const String*, FILE*);
/* idem, the stream is `stdout` */
/*extern inline*/ void String_put(const String*);
/* Returns a buffer containing the content of the String in the form of a plain
characters string; this buffer shall not be modified by the user, and it may
be modified by the String functions to stay up-to-date or be freed if it is
no longer the case */
const char* String_cStr(String*);
/* Returns the buffer containing the "cstr" of the String if it exists and is
up-to-date, NULL otherwise */
/*extern inline*/ const char* String_getCStr(const String*);
#endif
/**
*** String.c
***
*** module: String − source file
*** function: provides the `String` object type (abstract data type) and
*** functions for using it; the `String` object type is designed to
*** manage dynamic and/or large characters strings.
*** This file is the main source file of the module, it contains
*** the full declaration of the type `String`, as well as others
*** types and additionnal function declarations (static), both used
*** internally by the implementation. Of course it contains (in
*** fact, includes) also the definitions of all the functions.
*** author: Maëlan (aka Maëlan44)
*** (see http://www.siteduzero.com/membres-294-232877.html )
*** website: http://www.siteduzero.com/forum-83-683766-p1-une-implementation-de-string-en-c.html
***
*** last modified: 09/06/2011, 18:40
***
**/
#include "String.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "error.h"
/*** CONSTANTS ***/
/* see below */
#define STRING_LOG2BLKSZ 8
/* maximal lenght of the buffer of a String block */
#define STRING_TOTALMAXBLKSZ (1<<STRING_LOG2BLKSZ)
/* maximal lenght of the buffer of a String block, without the terminating '\\0' */
#define STRING_MAXBLKSZ (STRING_TOTALMAXBLKSZ-1)
/* lenght of the static buffer of a String block; it is designed for a struct of
type `StringBlock` to fit in 32 bytes */
//#define STRING_TOTALBUFSZ ( (32-sizeof(*char)-2*STRING_LOG2BLKSZ/8) \\
/ sizeof(char) )
#define STRING_TOTALBUFSZ 19
/* lenght of the static buffer of a String block, without the terminating '\\0' */
#define STRING_BUFSZ (STRING_TOTALBUFSZ-1)
/*** TYPES ***/
/* NOTE:
* Here, "block capacity" will mean the allocated length for the block minus 1,
* the latter being reserved for the terminating '\\0'. The "String capacity" or
* "total capacity" will mean the sum of the capacities of all the blocks
* composing the String.
* Similarly, "block size" will mean the used length without the '\\0'. The
* "String size" of "total size" is the sum of the sizes of all the blocks.
*/
/* a String block, link of a linked list;
contains a buffer and informations for using it */
typedef struct StringBlk {
/* static buffer: optimisation for short strings or string blocks
[OPT:buf] */
char buf[STRING_TOTALBUFSZ];
/* buffer; shall point to the member `buf` if it is used, else to the
allocated memory
/!\\ Shall never be NULL! */
char* p;
/* block size (shall be lesser than or equal to the member `capac`) */
unsigned int size :STRING_LOG2BLKSZ,
/* block capacity (shall be at least STRING_BUFSZ, since it is the size
of the static buffer; can not be greater than STRING_MAXBLKSZ) */
capac :STRING_LOG2BLKSZ;
/* block following this one in the linked list */
struct StringBlk* next;
} StringBlk;
/* the String object itself;
it takes the form of a linked list of blocks, each including a buffer and
informations concerning it */
struct String {
/* total length */
size_t size,
/* total capacity */
capac;
/* first block of the string */
StringBlk *first,
/* last block: optimisation for operations on the end of the string,
such as concatenating or adding a character [OPT:last] */
*last;
/* optimisation: a cursor pointing to the last character read [OPT:cur] */
struct { size_t pos; /* index of the 1st char. of the block pointed */
StringBlk* blk;
} cur;
/* buffer containing the full character string obtained via
`String_cStr` (and returned as constant); shall be NULL if this
function had not be called yet, or if the content is outdated (in
this case, it shall be freed if needed).
For reasons of optimisation, this buffer can be that of the first
block of the String, in which case it shall not be freed.
If the buffer exists, it can be used to perform optimisations on
operations involving reading content [OPT:cstr]. */
char* cStr;
};
/*** AUXILIARY FUNCTIONS ***/
/* returns the power of 2 greater than or equal to `n` */
static unsigned int pow2sup(unsigned int n) {
if(!n) return 0;
unsigned int r= 1;
while(n>r) r<<=1;
return r;
/** // solution found here < http://forum.hardware.fr/hfr/Programmation/C-2/quizz-calculer-superieure-sujet_52944_1.htm#t757526 >
if(!n || !(n&(n-1)) return n;
__asm__ (
"bsr eax, n"
"mov n, eax"
);
return 1<<n;
**/
}
/*** MAIN FUNCTIONS ***/
/** functions for object `StringBlock` **/
/* Creates a new String block filled with the content of the string passed, or
at most its STRING_MAXBLKSZ first characters; the second argument must be the
size of the string passed */
static StringBlk* StringBlk_new(const char*, size_t);
/* Creates a new empty String block with at least the specified capacity */
static StringBlk* StringBlk_newEmpty(unsigned int);
/* Creates a copy of the String block (ie. a new String block filled with the
content of the one passed) */
static inline StringBlk* StringBlk_copy(const StringBlk*);
/* Deletes the String block and all the following String blocks of the linked
list to which it belongs */
static void StringBlk_del(StringBlk*);
/* boolean: Does the String block uses its static buffer? */
static inline int StringBlk_useStaticBuf(const StringBlk* this);
#include "String.c.1"
/** functions for object `String` **/
/* boolean: Has the String an existing "cstr"? */
static inline int String_hasCStr(const String*);
/* boolean: Has the String an existing "cstr", the buffer of which is also that
of the 1st block? */
static inline int String_hasSharedCStr(const String*);
/* boolean: Has the String an existing "cstr", the buffer of which is allocated
separately? */
static inline int String_hasSeparateCStr(const String*);
/* Delete the "cstr" of the String */
static inline void String_delCStr(String*);
#include "String.c.2"
/**
*** String.c.1
***
*** module: String − source file (part 1)
*** function: provides the `String` object type (abstract data type) and
*** functions for using it; the `String` object type is designed to
*** manage dynamic and/or large characters strings.
*** This file contains the definitions of functions working on the
*** `StringBlock` object type.
*** author: Maëlan (aka Maëlan44)
*** (see http://www.siteduzero.com/membres-294-232877.html )
*** website: http://www.siteduzero.com/forum-83-683766-p1-une-implementation-de-string-en-c.html
***
*** last modified: 09/07/2011, 20:45
***
**/
static StringBlk* StringBlk_newEmpty(unsigned int capac) {
StringBlk* r = malloc(sizeof(StringBlk));
if(r==NULL) {
errorMessage("malloc", "when allocating a String block");
return NULL;
}
if(capac<=STRING_BUFSZ) {
r->p = r->buf;
r->capac = STRING_BUFSZ;
} else {
r->capac = (capac>STRING_MAXBLKSZ)? STRING_MAXBLKSZ : capac;
//r->capac = log2sup(r->blk.capac) - 1;
r->p = malloc((r->capac+1)*sizeof(char));
if(r->p == NULL) {
errorMessage("malloc", "when allocating a dynamic"
" buffer for a String block");
free(r);
return NULL;
}
}
*r->p = '\\0';
r->size = 0;
//r->next = NULL;
return r;
}
static StringBlk* StringBlk_new(const char* s, size_t size) {
StringBlk* r;
//if(s==NULL) size = 0;
r = StringBlk_newEmpty(size);
if(r==NULL) return NULL;
r->size = (size > r->capac)? r->capac : size;
/*if(s!=NULL) */strncpy(r->p, s, r->size);
r->p[r->size] = '\\0';
return r;
}
static inline StringBlk* StringBlk_copy(const StringBlk* this)
{ //if(this==NULL) return NULL;
return StringBlk_new(this->p, this->size); }
static void StringBlk_del(StringBlk* this) {
StringBlk* blk;
while(this!=NULL) {
blk = this;
this = this->next;
if(!StringBlk_useStaticBuf(blk))
free(blk->p);
free(blk);
}
}
static inline int StringBlk_useStaticBuf(const StringBlk* this)
{ return this->p == this->buf; }
/**
*** String.c.2
***
*** module: String − source file (part 2)
*** function: provides the `String` object type (abstract data type) and
*** functions for using it; the `String` object type is designed to
*** manage dynamic and/or large characters strings.
*** This file contains the definitions of functions working on the
*** `String` object type.
*** author: Maëlan (aka Maëlan44)
*** (see http://www.siteduzero.com/membres-294-232877.html )
*** website: http://www.siteduzero.com/forum-83-683766-p1-une-implementation-de-string-en-c.html
***
*** last modified: 09/07/2011, 18:50
***
**/
/** CREATION, DELETION
** newEmpty, new, copy, del
**/
String* String_newEmpty(size_t capac) {
String* r;
StringBlk** p;
r = malloc(sizeof(String));
if(r==NULL) {
errorMessage("malloc", "when allocating a String");
return NULL;
}
r->capac = 0;
p = &r->first;
do { /* at least one block (so STRING_BUFSZ) is created, even if the
capacity required is 0 */
*p = StringBlk_newEmpty(capac-r->capac);
if(*p==NULL) {
errorMessage("StringBlk_newEmpty", "when creating a"
" String block");
String_del(r);
return NULL;
}
r->capac += (*p)->capac;
if(capac > r->capac) p = &(*p)->next;
} while(capac > r->capac);
r->last = *p;
(*p)->next = NULL;
r->size = 0;
r->cur.pos = 0;
r->cur.blk = r->first;
r->cStr = r->first->p;
return r;
}
String* String_new(const char* s) {
String* r;
r = malloc(sizeof(String));
if(r==NULL) {
errorMessage("malloc", "when allocating a String");
return NULL;
}
r->size = 0;
r->capac = 0;
if(s==NULL) {
r->first = NULL;
r->last = NULL;
} else {
size_t size = strlen(s);
StringBlk** p = &r->first;
do {
*p = StringBlk_new(s+r->size, size-r->size);
if(*p==NULL) {
errorMessage("StringBlk_new", "when creating a"
" String block");
String_del(r);
return NULL;
}
r->capac += (*p)->capac;
r->size += (*p)->size;
if(size != r->size) p = &(*p)->next;
} while(size != r->size);
r->last = *p;
(*p)->next = NULL;
}
r->cur.pos = 0;
r->cur.blk = r->first;
r->cStr = NULL;
return r;
}
String* String_copy(const String* this) {
if(this==NULL) return NULL;
if(String_hasCStr(this))
return String_new(this->cStr);
String* r;
r = malloc(sizeof(String));
if(r==NULL) {
errorMessage("malloc", "when allocating a String");
return NULL;
}
StringBlk *blk = this->first,
**p = &r->first;
r->size = 0;
r->capac = 0;
if(blk==NULL) r->first = NULL;
else while(blk!=NULL) {
*p = StringBlk_copy(blk);
if(*p==NULL) {
errorMessage("StringBlk_copy", "when creating a copy of"
" a String block");
String_del(r);
return NULL;
}
r->capac += (*p)->capac;
r->size += (*p)->size;
blk = blk->next;
if(blk!=NULL) p = &(*p)->next;
}
r->last = *p;
(*p)->next = NULL;
r->cur.pos = 0;
r->cur.blk = r->first;
r->cStr = NULL;
return r;
}
void String_del(String* this) {
if(this==NULL) return;
if(String_hasSeparateCStr(this))
free(this->cStr);
if(!String_isNull(this))
StringBlk_del(this->first);
free(this);
}
/** SIZE, CAPACITY
** size, capacity, isEmpty, isNull, empty, clear
**/
inline size_t String_size(const String* this)
{ return (this!=NULL)? this->size : 0; }
inline size_t String_capacity(const String* this)
{ return (this!=NULL)? this->capac : 0; }
inline int String_isEmpty(const String* this)
{ return (this!=NULL)? !this->size : 1; }
inline int String_isNull(const String* this)
{ return (this!=NULL)? !this->capac : 1; }
void String_empty(String* this) {
if(this==NULL) return;
StringBlk* p = this->first;
while(p!=NULL) {
*p->p = '\\0';
p->size = 0;
p = p->next;
}
this->size = 0;
}
void String_clear(String* this) {
if(this==NULL) return;
if(String_hasCStr(this)) {
if(String_hasSeparateCStr(this))
free(this->cStr);
this->cStr = NULL;
}
if(!String_isNull(this)) {
StringBlk_del(this->first);
this->first = NULL;
this->last = NULL;
}
this->capac = 0;
this->size = 0;
this->cur.pos = 0;
this->cur.blk = NULL;
}
/** CSTR
** getCStr, delCStr, hasCStr, hasSharedCStr, hasSeparateCStr
**/
inline const char* String_getCStr(const String* this)
{ return this->cStr; }
void String_delCStr(String* this) {
free(this->cStr);
this->cStr = NULL;
}
static inline int String_hasCStr(const String* this)
{ return this->cStr != NULL; }
static inline int String_hasSharedCStr(const String* this)
{ return this->capac && this->cStr == this->first->p; }
static inline int String_hasSeparateCStr(const String* this)
{ return this->cStr != NULL && this->cStr != this->first->p; }
/** BASIC READ / WRITE
** char, setChar
**/
char String_char(/*const*/ String* this, size_t i) {
if(this==NULL || i>=this->size) return '\\0';
if(String_hasCStr(this)) return this->cStr[i];
if(i < this->cur.pos) {
this->cur.pos = 0;
this->cur.blk = this->first;
}
while(i >= this->cur.pos + this->cur.blk->size) {
this->cur.pos += this->cur.blk->size;
this->cur.blk = this->cur.blk->next;
}
return this->cur.blk->p[i - this->cur.pos];
}
void String_setChar(String* this, size_t i, char c) {
if(this==NULL || i>=this->size) return;
if(i < this->cur.pos) {
this->cur.pos = 0;
this->cur.blk = this->first;
}
while(i >= this->cur.pos + this->cur.blk->size) {
this->cur.pos += this->cur.blk->size;
this->cur.blk = this->cur.blk->next;
}
this->cur.blk->p[i-this->cur.pos] = c;
if(c=='\\0') {
this->size = i;
this->cur.blk->size = i - this->cur.pos;
this->last = this->cur.blk;
if(this->last->next!=NULL) {
StringBlk_del(this->last->next);
this->last->next = NULL;
}
}
if(String_hasSeparateCStr(this)) {
this->cStr[i] = c;
if(c=='\\0') {
// cf here: < http://www.siteduzero.com/forum-83-704272-p1-causes-d-echec-de-realloc.html >
char* tmp = realloc(this->cStr, (i+1)*sizeof(char));
if(tmp==NULL)
String_delCStr(this);
else
this->cStr = tmp;
}
}
}
/** UTILITIES
** cmp, cmpCStr, searchChar, searchLastChar
**/
int String_cmp(const String* s1, const String* s2) {
if(String_isEmpty(s1)) return !String_isEmpty(s2);
if(String_isEmpty(s2)) return -1;
if(String_hasCStr(s1) && String_hasCStr(s2))
return strcmp(s1->cStr, s2->cStr);
StringBlk *b1 = s1->first,
*b2 = s2->first;
size_t i1 = 0,
i2 = 0;
while(b1->p[i1] == b2->p[i2]) {
if(b1->p[i1]=='\\0') return 0;
++i1; ++i2;
while(i1 >= b1->size) { /* while and not if, since there can
be empty blocks */
i1 = 0;
b1 = b1->next;
if(b1==NULL) return s1->size < s2->size;
}
while(i2 >= b2->size) {
i2 = 0;
b2 = b2->next;
if(b2==NULL) return -(s1->size > s2->size);
}
}
return (b1->p[i1] > b2->p[i2])? -1 : 1;
}
int String_cmpCStr(const String* this, const char* s) {
if(String_isEmpty(this)) return s!=NULL && *s!='\\0';
if(s==NULL || *s=='\\0') return -1;
if(String_hasCStr(this))
return strcmp(this->cStr, s);
StringBlk *blk = this->first;
size_t i = 0,
j = 0;
while(blk->p[i] == s[j]) {
if(s[j]=='\\0') return 0;
++i; ++j;
while(i >= blk->size) { /* while and not if, since there can
be empty blocks */
i = 0;
blk = blk->next;
if(blk==NULL) return s[j]!='\\0';
}
}
return (blk->p[i] > s[j])? -1 : 1;
}
ptrdiff_t String_searchChar(const String* this, char c) {
if(String_isEmpty(this)) return (c=='\\0')? 0 : -1;
if(c=='\\0') return this->size;
if(String_hasCStr(this)) return strchr(this->cStr, c) - this->cStr;
StringBlk* blk = this->first;
size_t pos = 0;
char* r;
while(blk!=NULL) {
r = strchr(blk->p, c);
if(r!=NULL) return r - blk->p + pos;
pos += blk->size;
blk = blk->next;
}
return -1;
}
ptrdiff_t String_searchLastChar(const String* this, char c) {
if(String_isEmpty(this)) return (c=='\\0')? 0 : -1;
if(c=='\\0') return this->size;
if(String_hasCStr(this)) return strrchr(this->cStr, c) - this->cStr;
StringBlk* blk = this->first;
size_t pos = 0;
char* r;
ptrdiff_t found = -1;
while(blk!=NULL) {
r = strrchr(blk->p, c);
if(r!=NULL) return found = r - blk->p + pos;
pos += blk->size;
blk = blk->next;
}
return found;
}
/** INSERTION
** concat
**/
String* String_concat(String* this, const String* s2) {
if(this==NULL) return NULL;
if(s2==NULL) return this;
String* cpy = String_copy(s2);
if(cpy==NULL) {
errorMessage("String_copy", "when copying a String to add it to"
" the end of another one");
return NULL;
}
if(String_isNull(this)) this->first = cpy->first;
else this->last->next = cpy->first;
this->last = cpy->last;
this->capac += cpy->capac;
this->size += cpy->size;
/* /!\\ UPDATE CSTR DATAS */
free(cpy);
return this;
}
/** IN / OUT
** fput, put
**/
void String_fput(const String* this, FILE* stream) {
if(this==NULL || stream==NULL) return;
StringBlk* p = this->first;
while(p!=NULL) {
fputs(p->p, stream);
p = p->next;
}
}
inline void String_put(const String* this)
{ String_fput(this, stdout); }
Une implémentation de vector
De même que pour string, on ne dispose de base en C d'aucune abstraction pour un conteneur dynamique comme on en a en C++, Python, etc. Et bon, faire 40 malloc / free par fonction, c’est sympa 5 min. Mais c’était sans compter sur les expériences que Qnope a fait subir au préprocesseur (disponible ici) et dont nous remettrons le code ici dans un soucis de disponibilité. Notons au passage que le préprocesseur est souvent utilisé pour implémenter une pseudo-généricité en C.
Commençons par la gestion des erreurs.
#ifndef ERROR_H_INCLUDED
#define ERROR_H_INCLUDED
typedef enum{
/** Succès **/
QGM_SUCCESS, /** Rien à signalé **/
/** Mauvaise allocations **/
QGM_BADALLOC, /** Allocation de mémoire échouée **/
/** Fuites de mémoires **/
QGM_MEMORYLEAKSALLOC, /** Fuites de mémoires (Allocation) **/
/** L'élément n'existe pas **/
QGM_NOELEMENTALLOC, /** Pointeur à détruire n'existe pas **/
QGM_NOELEMENTSTACK, /** Aucun élément à dépiler **/
QGM_NOELEMENTQUEUE, /** Aucun élement à défiler **/
QGM_NOELEMENTVECTOR, /** Aucun élément à supprimer **/
}QGM_Error_t;
/** Variable recevant l'erreur **/
extern QGM_Error_t QGM_Error;
/** Synopsis : Indique si il y a ou non une erreur
Retour : 1 : erreur. 0 : Pas d'erreur **/
int QGM_IsError(void);
/** Synopsis : Permet de récupèrer l'erreur
Retour : Chaîne de caractère contenant l'erreur **/
char const *QGM_GetError(void);
#endif
#include <stdio.h>
#include "error.h"
/** Variable recevant l'erreur **/
QGM_Error_t QGM_Error = QGM_SUCCESS;
/** Tableau des erreurs en fonction de QGM_Error **/
char const *QGM_String_Error[] = {
/** Succès **/
"",
/** Mauvaise allocations **/
"Not space\n",
/** Fuites de mémoires **/
"Memory Leaks Are Detected (Allocation)\n",
/** L'élément n'existe pas **/
"This pointer can't be free (Allocation)\n",
"Nothing element to pop (Stack)\n",
"Nothing element to pop (Queue)\n",
"Nothing element to delete (Vector)\n",[[secret]]
};
int QGM_IsError(void){
return (QGM_Error == QGM_SUCCESS) ? 0 : 1;
}
/** Fonction permettant de récupérer l'erreur **/
char const *QGM_GetError(void)
{
char const *t = QGM_String_Error[QGM_Error];
QGM_Error = QGM_SUCCESS;
return t;
}
Maintenant, le code qui nous intéresse vraiment (attention, détournement de préprocesseur en approche).
#ifndef DATA_H_INCLUDED
#define DATA_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
/*****************************************************************************/
/********************************** HEADER ***********************************/
/*****************************************************************************/
/********************************** VECTOR ***********************************/
/*****************************************************************************/
#define QGM_Vector_Header(TYPE)\
typedef struct{\
TYPE *data;\
size_t size;\
unsigned nElem;\
}QGM_Vector_##TYPE;\
\
QGM_Vector_##TYPE *QGM_Vector_##TYPE##_Init(void);\
int QGM_Vector_##TYPE##_Resize(QGM_Vector_##TYPE*);\
int QGM_Vector_##TYPE##_PushFront(QGM_Vector_##TYPE*, TYPE);\
int QGM_Vector_##TYPE##_PushBack(QGM_Vector_##TYPE*, TYPE);\
int QGM_Vector_##TYPE##_Insert(QGM_Vector_##TYPE*, TYPE, unsigned);\
TYPE QGM_Vector_##TYPE##_PopFront(QGM_Vector_##TYPE*);\
TYPE QGM_Vector_##TYPE##_PopBack(QGM_Vector_##TYPE*);\
TYPE QGM_Vector_##TYPE##_Erase(QGM_Vector_##TYPE*, unsigned);\
size_t QGM_Vector_##TYPE##_Size(QGM_Vector_##TYPE*);\
void QGM_Vector_##TYPE##_Quit(QGM_Vector_##TYPE*);
/*****************************************************************************/
/********************************** STACK ************************************/
/*****************************************************************************/
#define QGM_Stack_Header(TYPE)\
typedef struct{\
TYPE *data;\
size_t size;\
unsigned nElem;\
}QGM_Stack_##TYPE;\
\
QGM_Stack_##TYPE *QGM_Stack_##TYPE##_Init(void);\
int QGM_Stack_##TYPE##_Resize(QGM_Stack_##TYPE*);\
int QGM_Stack_##TYPE##_Push(QGM_Stack_##TYPE*, TYPE);\
TYPE QGM_Stack_##TYPE##_Pop(QGM_Stack_##TYPE*);\
size_t QGM_Stack_##TYPE##_Size(QGM_Stack_##TYPE*);\
void QGM_Stack_##TYPE##_Quit(QGM_Stack_##TYPE*);
/*****************************************************************************/
/********************************** Queue ************************************/
/*****************************************************************************/
#define QGM_Queue_Header(TYPE)\
typedef struct{\
TYPE *data;\
size_t size;\
unsigned nElem;\
unsigned pop;\
unsigned push;\
}QGM_Queue_##TYPE;\
\
QGM_Queue_##TYPE *QGM_Queue_##TYPE##_Init(void);\
int QGM_Queue_##TYPE##_Resize(QGM_Queue_##TYPE*);\
void QGM_Queue_##TYPE##_Adjust(QGM_Queue_##TYPE*);\
int QGM_Queue_##TYPE##_Push(QGM_Queue_##TYPE*, TYPE);\
TYPE QGM_Queue_##TYPE##_Pop(QGM_Queue_##TYPE*);\
size_t QGM_Queue_##TYPE##_Size(QGM_Queue_##TYPE*);\
void QGM_Queue_##TYPE##_Quit(QGM_Queue_##TYPE*);
/*****************************************************************************/
/********************************* FONCTIONS *********************************/
/*****************************************************************************/
/********************************** VECTOR ***********************************/
/*****************************************************************************/
#define QGM_Vector_Functions(TYPE)\
QGM_Vector_##TYPE *QGM_Vector_##TYPE##_Init(void){\
QGM_Vector_##TYPE *self;\
if((self = (malloc)(sizeof *self)) == NULL)\
goto _error;\
if((self->data = (malloc)(sizeof *self->data)) == NULL)\
goto _error;\
self->nElem = 0;\
self->size = 1;\
return self;\
_error:\
free(self);\
QGM_Error = QGM_BADALLOC;\
return NULL;\
}\
\
int QGM_Vector_##TYPE##_Resize(QGM_Vector_##TYPE *self){\
TYPE *ptr = (realloc)(self->data,\
self->size * 3 * sizeof *ptr);\
if(ptr == NULL)\
goto _error;\
self->size *= 3;\
self->data = ptr;\
return 1;\
_error :\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
int QGM_Vector_##TYPE##_PushFront(QGM_Vector_##TYPE *self, TYPE data){\
if(self->nElem >= self->size)\
if(QGM_Vector_##TYPE##_Resize(self) == 0)\
goto _error;\
memmove(self->data + 1,\
self->data,\
self->nElem++ * sizeof *self->data);\
*self->data = data;\
return 1;\
_error :\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
int QGM_Vector_##TYPE##_PushBack(QGM_Vector_##TYPE *self, TYPE data){\
if(self->nElem >= self->size)\
if(QGM_Vector_##TYPE##_Resize(self) == 0)\
goto _error;\
self->data[self->nElem++] = data;\
return 1;\
_error :\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
int QGM_Vector_##TYPE##_Insert\
(QGM_Vector_##TYPE *self, TYPE data, unsigned place){\
if(self->nElem >= self->size)\
if(QGM_Vector_##TYPE##_Resize(self) == 0)\
goto _error;\
if(place >= self->nElem)\
place = self->nElem;\
memmove(self->data + place + 1,\
self->data + place,\
(self->nElem++ - place) * sizeof *self->data);\
self->data[place] = data;\
return 1;\
_error:\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
TYPE QGM_Vector_##TYPE##_PopFront(QGM_Vector_##TYPE *self){\
TYPE ifError;\
TYPE data;\
if(self->nElem == 0)\
goto _error;\
data = *self->data;\
memmove(self->data,\
self->data + 1,\
--self->nElem * sizeof *self->data);\
return data;\
_error:\
QGM_Error = QGM_NOELEMENTVECTOR;\
memset(&ifError, 255, sizeof ifError);\
return ifError;\
}\
\
TYPE QGM_Vector_##TYPE##_PopBack(QGM_Vector_##TYPE *self){\
TYPE ifError;\
if(self->nElem == 0)\
goto _error;\
return self->data[--self->nElem];\
_error:\
QGM_Error = QGM_NOELEMENTVECTOR;\
memset(&ifError, 255, sizeof ifError);\
return ifError;\
}\
\
TYPE QGM_Vector_##TYPE##_Erase(QGM_Vector_##TYPE *self, unsigned place){\
TYPE ifError;\
TYPE data;\
if(self->nElem == 0)\
goto _error;\
if(place >= self->nElem)\
place = self->nElem;\
data = self->data[place];\
memmove(self->data + place,\
self->data + place + 1,\
(--self->nElem - place) * sizeof *self->data);\
return data;\
_error:\
QGM_Error = QGM_NOELEMENTVECTOR;\
memset(&ifError, 255, sizeof ifError);\
return ifError;\
}\
\
size_t QGM_Vector_##TYPE##_Size(QGM_Vector_##TYPE *self){\
return self->nElem;\
}\
\
void QGM_Vector_##TYPE##_Quit(QGM_Vector_##TYPE *self){\
free(self->data);\
free(self);\
}
/*****************************************************************************/
/********************************** STACK ************************************/
/*****************************************************************************/
#define QGM_Stack_Functions(TYPE)\
QGM_Stack_##TYPE *QGM_Stack_##TYPE##_Init(void){\
QGM_Stack_##TYPE *self;\
if((self = (malloc)(sizeof *self)) == NULL)\
goto _error;\
if((self->data = (malloc)(sizeof *self->data)) == NULL)\
goto _error;\
self->nElem = 0;\
self->size = 1;\
return self;\
_error:\
free(self);\
QGM_Error = QGM_BADALLOC;\
return NULL;\
}\
\
int QGM_Stack_##TYPE##_Resize(QGM_Stack_##TYPE *self){\
TYPE *ptr = (realloc)(self->data,\
self->size * 3 * sizeof *ptr);\
if(ptr == NULL)\
goto _error;\
self->size *= 3;\
self->data = ptr;\
return 1;\
_error :\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
int QGM_Stack_##TYPE##_Push(QGM_Stack_##TYPE *self, TYPE data){\
if(self->nElem >= self->size)\
if(QGM_Stack_##TYPE##_Resize(self) == 0)\
goto _error;\
self->data[self->nElem++] = data;\
return1;\
_error :\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
TYPE QGM_Stack_##TYPE##_Pop(QGM_Stack_##TYPE *self){\
TYPE ifError;\
if(self->nElem == 0)\
goto _error;\
return self->data[--self->nElem];\
_error:\
QGM_Error = QGM_NOELEMENTSTACK;\
memset(&ifError, 255, sizeof ifError);\
return ifError;\
}\
\
size_t QGM_Stack_##TYPE##_Size(QGM_Stack_##TYPE *self){\
return self->nElem;\
}\
\
void QGM_Stack_##TYPE##_Quit(QGM_Stack_##TYPE *self){\
free(self->data);\
free(self);\
}
/*****************************************************************************/
/********************************** Queue ************************************/
/*****************************************************************************/
#define QGM_Queue_Functions(TYPE)\
QGM_Queue_##TYPE *QGM_Queue_##TYPE##_Init(void){\
QGM_Queue_##TYPE *self;\
if((self = (malloc)(sizeof *self)) == NULL)\
goto _error;\
if((self->data = (malloc)(sizeof *self)) == NULL)\
goto _error;\
self->nElem = 0;\
self->size = 1;\
self->push = self->pop = 0;\
return self;\
_error:\
free(self);\
QGM_Error = QGM_BADALLOC;\
return NULL;\
}\
\
int QGM_Queue_##TYPE##_Resize(QGM_Queue_##TYPE *self){\
TYPE *ptr = (realloc)(self->data,\
self->size * 3 * sizeof *ptr);\
if(ptr == NULL)\
goto _error;\
self->size *= 3;\
self->data = ptr;\
return 1;\
_error:\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
void QGM_Queue_##TYPE##_Adjust(QGM_Queue_##TYPE *self){\
memmove(self->data,\
self->data + self->pop,\
(self->push - self->pop) * sizeof *self->data);\
self->push -= self->pop;\
self->pop = 0;\
}\
\
int QGM_Queue_##TYPE##_Push(QGM_Queue_##TYPE *self, TYPE data){\
if(self->nElem >= self->size)\
if(QGM_Queue_##TYPE##_Resize(self) == 0)\
goto _error;\
if(self->push == self->size)\
QGM_Queue_##TYPE##_Adjust(self);\
self->data[self->push++] = data;\
++self->nElem;\
return 1;\
_error:\
QGM_Error = QGM_BADALLOC;\
return 0;\
}\
\
TYPE QGM_Queue_##TYPE##_Pop(QGM_Queue_##TYPE *self){\
TYPE ifError;\
TYPE data;\
if(self->nElem == 0)\
goto _error;\
data = self->data[self->pop++];\
if(--self->nElem == 0)\
self->push = self->pop = 0;\
return data;\
_error:\
QGM_Error = QGM_NOELEMENTQUEUE;\
memset(&ifError, 255, sizeof ifError);\
return ifError;\
}\
\
size_t QGM_Queue_##TYPE##_Size(QGM_Queue_##TYPE *self){\
return self->nElem;\
}\
\
void QGM_Queue_##TYPE##_Quit(QGM_Queue_##TYPE *self){\
free(self->data);\
free(self);\
}
#endif
Qnope nous fournit plusieurs exemples pour tester son code. D’abord un vector.
#include "QGM/alloc.h"
#include "QGM/error.h"
#include "QGM/data.h"
/** Active le vector int**/
QGM_Vector_Header(int);
QGM_Vector_Functions(int);
/** Active le vector double **/
QGM_Vector_Header(double);
QGM_Vector_Functions(double);
int main(void){
QGM_Vector_int *vectorInt;
QGM_Vector_double *vectorDouble;
int i;
if((vectorDouble = QGM_Vector_double_Init()) == NULL)
return 1;
if((vectorInt = QGM_Vector_int_Init()) == NULL){
QGM_Vector_double_Quit(vectorDouble);
return 1;
}
for(i = 0; i < 20; ++i)
if(QGM_Vector_double_PushFront(vectorDouble, (double)i / 10.0) == 0)
goto end;
for(i = 0; i < 20; ++i)
if(QGM_Vector_int_PushFront(vectorInt, i) == 0)
goto end;
/** Affichage du vector double sans rien supprimer **/
printf("Vector Double\n");
for(i = 0; i < 20; ++i)
printf("%.1f ", vectorDouble->data[i]);
printf("Il y a %u elements dans le vectorDouble\n",
QGM_Vector_double_Size(vectorDouble));
/** Affichage du vector int en supprimant toutes les données **/
printf("\nVector int\n");
for(i = 0; i < 20; ++i)
printf("%d ", QGM_Vector_int_PopBack(vectorInt));
printf("\nIl y a %u element dans le vectorInt\n",
QGM_Vector_int_Size(vectorInt));
end:
QGM_Vector_int_Quit(vectorInt);
QGM_Vector_double_Quit(vectorDouble);
return 0;
}
Puis une pile.
#include "QGM/alloc.h"
#include "QGM/error.h"
#include "QGM/data.h"
/** Active la pile double **/
QGM_Stack_Header(double);
QGM_Stack_Functions(double);
int main(void){
QGM_Stack_double *stack = QGM_Stack_double_Init();
int i;
for(i = 0; i < 20; ++i)
if(QGM_Stack_double_Push(stack, (double)i / 10.0) == 0)
goto end;
for(i = 0; i < 21; ++i)
printf("%.1f ", QGM_Stack_double_Pop(stack));
printf("\n%s", QGM_GetError());
end:
QGM_Stack_double_Quit(stack);
return 0;
}
Et enfin une file.
#include "QGM/alloc.h"
#include "QGM/error.h"
#include "QGM/data.h"
/** Active la file long **/
QGM_Queue_Header(long);
QGM_Queue_Functions(long);
int main(void){
QGM_Queue_long *queue = QGM_Queue_long_Init();
int i;
for(i = 0; i < 20; ++i)
if(QGM_Queue_long_Push(queue, i) == 0)
goto end;
for(i = 0; i < 21; ++i)
printf("%d ", QGM_Queue_long_Pop(queue));
printf("\n%s", QGM_GetError());
end:
QGM_Queue_long_Quit(queue);
return 0;
}
Gestion des exceptions
Je vois déjà ceux qui sautent de leur siège en hurlant à l’hérésie, qu’en C il n’y a pas d’exception. Oui, mais c’était sans compter sur une bande de fans de C99 et de longjumps. Et aussi de détournement de macros. Le résultat n’est évidemment pas aussi souple qu’un langage implémentant naturellement les exceptions tel C++, mais c’est quand même impressionnant et c’est ici que ça se passe.
@Taurre
#ifndef EXCEPTION_H
#define EXCEPTION_H
#include <setjmp.h>
#define TRY for (es->next = &(struct exception) { .next = es->next } \
, es->num = 1; es->num && !setjmp(es->next->buf) \
; es->next = es->next->next, es->num = 0)
#define CATCH(NUM) for (; es->num == (NUM); es->num = 0)
#define FINALLY for (; es->num; es->num = 0)
#define THROW(NUM) for (jmp_buf *buf = &es->next->buf \
; (es->next = es->next->next, es->num = (NUM)); longjmp(*buf, es->num))
extern struct exception {
jmp_buf buf;
int num;
struct exception *next;
} *es;
#endif /* EXCEPTION_H */
#include <stddef.h>
#include "exception.h"
struct exception *es = &(struct exception) { .next = NULL };
#include <stdio.h>
#include <stdlib.h>
#include "exception.h"
enum {
TEST1 = 1, TEST2 = 2, TEST3 = 3
};
static void
test3(void)
{
THROW(TEST3);
}
static void
test2(void)
{
THROW(TEST2);
}
static void
test1(void)
{
TRY {
test2();
} CATCH (TEST2) {
puts("TEST2");
THROW(TEST1);
}
}
int
main(void)
{
TRY {
test1();
} CATCH (TEST1) {
puts("TEST1");
}
TRY {
;
}
TRY {
test3();
} FINALLY {
puts("Exception!");
}
return EXIT_SUCCESS;
}
@Maëlan
Avec une pile statique
#define CATCH_STACKSZ 16
/* pile globale : st est la pile, p est un pointeur sur l’élément actuel */
extern struct {
struct {
int code;
jmp_buf env;
} *p, st[CATCH_STACKSZ];
} catch;
/* catch.p est initialisé au 1er élément de catch.st - 1 (indique qu’on
n’est dans aucun bloc try). */
#define CATCH_ERROR(msg) \
fputs( \
"error: file `" #__FILE__ "`, line " #__LINE__ ", function `" \
#__func__ "`: " msg ".\n" , \
stderr)
/* version non sécurisée */
#define _try \
if( ++catch.p , !( catch.p->code = setjmp(catch.p->env) ) )
/* entrée dans un bloc try → on empile */
/* version sécurisée (attention les yeux) */
#define try \
if(catch.p == catch.st + CATCH_STACKSZ - 1) /* On est déjà au sommet de la pile. */ \
CATCH_ERROR("too much nested `try` blocks (a maximum of "
#CATCH_STACKSZ " is supported), skipping this one"); \
else _try
/* version non sécurisée */
#define _catch(ID) \
for(ID = (catch.p--)->code ; (catch.p+1)->code; (catch.p+1)->code = 0)
/* sortie d’un bloc try → on dépile */
/* version sécurisée */
#define catch(ID) \
if(catch.p == catch.st + CATCH_STACKSZ - 1) /* On est déjà au sommet de la pile. */ \
CATCH_ERROR("skipping corresponding `catch` block"); \
else _catch(ID)
/* version non sécurisée */
#define _throw(CODE) \
longjmp(catch.p->env, (CODE))
/* version sécurisée */
#define throw(CODE) \
if(catch.p == catch.st - 1) { /* On n’est dans aucun bloc try. */ \
CATCH_ERROR("throw not in a try block"); \
exit(CODE); /* Pourquoi pas ? */ \
} else _throw(CODE);
Avec une pile automatique
#ifndef INCLUDED_TRYCATCH_H
#define INCLUDED_TRYCATCH_H
/* Ce header nécessite C99. */
#if !defined (__STDC_VERSION__) || __STDC_VERSION__ < 199901L
#error : USE OF “TRYCATCH.H” REQUIRES C99.
#endif
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
/* type : pile d’environnements `jmp_buf` (utilisée dans la macro `try`) */
struct try {
struct try *prev;
jmp_buf env;
};
/* type : informations de gestion de la pile de jmp_buf et des exceptions */
struct catch {
struct try *envs; /* haut de la pile (NULL si pas dans un bloc `try`) */
int code; /* code numérique de l’exception (0 si sans objet) */
};
/* globale de gestion du système d’exceptions */
extern struct catch catch;
/* gestion des erreurs (message d’erreur détaillé) */
#define Throw(msg) \
fprintf(stderr, "error [file `%s`, line %u, function `%s`]: %s.\n", \
__FILE__, __LINE__, __func__, (msg))
/** TRY **/
/* Crée un élément de la pile de classe automatique (non nommé et local au bloc
`try`), le rajoute au sommet de la pile globale, puis appelle `setjmp` pour
définir le point de retour de `throw`.
Dépile à la fin. On doit dépiler dans le `for` car l’élément de pile lui est
local. Dans tous les cas, la boucle `for` ne doit pas se répéter.
Deux cas de figure :
− sortie normale du bloc `try` : cf la 3è partie du `for` pour dépiler, et la
1ère condition pour ne pas recommencer le bloc (utilise le comportement en
cours-circuit de &&) ;
− sortie avec un `throw` : cf le ternaire de la 2ème condition, pour dépiler
et arrêter le `for`. */
#define try \
for( catch.code = 1, catch.envs = & (struct try) { .prev = catch.envs } \
; catch.code && ( setjmp(catch.envs->env) \
? (catch.envs = catch.envs->prev, 0) \
: 1 ) \
; catch.code = 0, catch.envs = catch.envs->prev \
)
/** CATCH (+ catchSwitch) **/
/* Exécute le contenu du bloc s’il y a une exception (code non nul), en stockant
son code dans la variable de type `int` passée (qui peut être une déclaration,
auquel cas cette variable sera locale au bloc `catch`).
Un bloc `catch` n’aura d’effet que s’il est le premier après un bloc `try`
(remet le code d’exception à 0). */
#define catch(VAR) \
for(VAR = catch.code ; catch.code ; catch.code = 0)
/* Raccourci pour effectuer un `switch` sur le code de l’exception, sans nommer
une variable pour le stocker (s’utilise comme un `switch` normal). */
#define catchSwitch() \
for(; catch.code ; catch.code = 0) \
switch(catch.code)
/** THROW (+ rethrow) **/
/* « Lance » le code d’exception passé, qui doit être non nul. L’exécution du
programme revient grâce à `longjmp` au dernier bloc `try` englobant. Si aucun
bloc `try` n’englobe `throw`, quitte le programme avec le code lancé. */
#define throw(CODE) \
do { \
catch.code = (CODE); \
if(!catch.code) { \
Throw("throwing a null exception code"); \
exit(EXIT_FAILURE); \
} \
else if(!catch.envs) { \
Throw("`throw` outside of a `try` block"); \
exit(catch.code); \
} \
else \
longjmp(catch.envs->env, catch.code); \
} while(0)
/* « Relance » l’exception qui a été capturée (possible seulement dans un bloc
`catch`). */
#define rethrow() \
throw(catch.code)
#endif
#include "trycatch.h"
struct catch catch = {NULL, 0};
#include "trycatch.h"
#include <stdio.h>
typedef enum {EXC_Z, EXC_NOTRY, EXC_42, EXC_DIVBYZ, EXC_ORDER} Exception;
int division(int a, int b) {
if(!a)
throw(EXC_Z); /* doit provoquer une erreur (code nul) */
else if(a==-1)
throw(EXC_NOTRY); /* doit provoquer une erreur (throw sans try) */
else if(b==42)
throw(EXC_42); /* exception relancée avec rethrow() */
else if(!b)
throw(EXC_DIVBYZ); /* exception gérée dans le catchSwitch() */
else if(a<b)
throw(EXC_ORDER); /* idem */
return a / b;
}
int main(void) {
int a, b;
while(1) {
fputs("a, b? ", stdout);
scanf("%i%i", &a, &b);
try {
try {
printf("%i / %i = %i\n", a, b, division(a,b));
}
catchSwitch() {
case EXC_DIVBYZ:
puts("exc: /0"); break;
case EXC_ORDER:
puts("exc: a<b"); break;
default: /* EXC_42 ou EXC_NOTRY */
rethrow(); break;
}
}
catch(Exception e) {
if(e == EXC_42)
puts("exc: 42");
else /* EXC_NOTRY */
rethrow(); /* erreur : pas dans un bloc `try` */
}
}
}
Implémenter la libC
Le langage C a une bibliothèque standard assez légère et peu fournie. Il arrive qu’on propose d’en recoder des fonctions en guise d’exercices, la plus connue étant sans doute printf
et sa famille. Mais un membre de l’ex-SdZ s’est dit que ce serait plus marrant de TOUT recoder. Le projet n’a jamais abouti, mais il y a quelques jolis codes qui traînent toujours là-bas. On trouve du printf
, de la gestion d’erreurs, des manipulations de chaînes de caractères, du tri, bref, plein de trucs cools.
@informaticienzero
Les horribles codes ci-dessous sont par votre humble serviteur.
#ifndef MY_STRING_H
#define MY_STRING_H
char * my_strcpy(char * copie, const char * origine);
char * my_strncpy(char * dst, const char * src, int n);
size_t my_strlen(const char * str);
const char * my_strchr(const char * str, int car);
char * my_strcat(char * dest, const char * orig);
int my_strcmp(const char * str1, const char *str2);
int my_strncmp(const char * str1, const char *str2, int n);
char * my_strstr (const char * str, const char * unstr);
char * my_strpbrk(const char * str, const char * c);
#endif
#include <stddef.h>
#include "my_string.h"
char * my_strcpy(char * dst, const char * src)
{
size_t i;
for (i = 0; (dst[i] = src[i]); i++);
return dst;
}
char * my_strncpy(char * dst, const char * src, int n)
{
size_t i;
for (i = 0; (dst[i] = src[i]) && i < n; i++);
dst[i] = '\0';
return dst;
}
size_t my_strlen(const char * str)
{
size_t lg = 0;
while(str[lg])
lg++;
return lg;
}
const char * my_strchr(const char * str, int car)
{
while(*str)
{
if (*str == car || *str == '\0')
return str;
else
str++;
}
return NULL;
}
char * my_strcat(char * dest, const char * orig)
{
my_strcpy(dest + my_strlen(dest), orig);
return dest;
}
char * my_strncat(char * dest, const char * orig, int n)
{
my_strncpy(dest + my_strlen(dest), orig, n);
return dest;
}
int my_strcmp(const char * str1, const char *str2)
{
while(*str1 == *str2)
{
if (*str1 == 0)
return 0;
str1++;
str2++;
}
return *str1 < *str2 ? -1 : 1;
}
int my_strncmp(const char * str1, const char *str2, int n)
{
while(n--)
{
if (*str1 == 0 || *str1 != *str2)
return *str1 < *str2 ? -1 : 1;
str1++;
str2++;
}
return 0;
}
char * my_strstr (const char * str, const char * unstr)
{
while(*str)
{
if (my_strncmp(str, unstr, my_strlen(unstr)) == 0)
return (char*)str;
str++;
}
return NULL;
}
char * my_strpbrk(const char * str, const char * c)
{
size_t i, len = my_strlen(c);
while(*str)
{
for (i = 0; i < len; i++)
{
if(c[i] == *str)
return (char*)str;
}
str++;
}
return NULL;
}
Par @Taurre
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
long
strtol(char const * restrict s, char * restrict * p, int base)
{
static long conv[] = {
['0'] = 0,
['1'] = 1,
['2'] = 2,
['3'] = 3,
['4'] = 4,
['5'] = 5,
['6'] = 6,
['7'] = 7,
['8'] = 8,
['9'] = 9,
['a'] = 10,
['b'] = 11,
['c'] = 12,
['d'] = 13,
['e'] = 14,
['f'] = 15,
['A'] = 10,
['B'] = 11,
['C'] = 12,
['D'] = 13,
['E'] = 14,
['F'] = 15,
};
long n = 0;
int neg = (*s == '-') ? (++s, 1) : 0;
if (base > 16)
goto err;
for (; *s != '\0'; ++s) {
if (!isxdigit(*s))
goto err;
if (LONG_MAX / base < n) {
n = (neg) ? LONG_MIN : LONG_MAX;
errno = ERANGE;
goto err;
} else
n *= base;
if (LONG_MAX - conv[*s] < n) {
n = (neg) ? LONG_MIN : LONG_MAX;
errno = ERANGE;
goto err;
} else
n += conv[*s];
}
goto ret;
err:
if (p != NULL)
*p = s;
ret:
return (neg) ? -n : n;
}
Par Holt
Et maintenant, une petite implémentation de printf
.
#include "printf.h"
#include <stdarg.h>
#include <stdio.h> /* Only for putchar */
#include <wchar.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#if __STDC_VERSION__ >= 199901L
#include <stdint.h>
#define VP_C99__
typedef intmax_t vp_intmax_t ;
typedef uintmax_t vp_uintmax_t ;
#else
typedef long int vp_intmax_t ;
typedef unsigned long int vp_uintmax_t ;
#endif
enum flag_t {
FLAG_LJUST = 1 << 0,
FLAG_PSIGN = 1 << 1,
FLAG_SPACE = 1 << 2,
FLAG_SHARP = 1 << 3,
FLAG_ZEROS = 1 << 4
};
#define HAS_LEFT_JUSTIFIED__(c) ((c) & FLAG_LJUST)
#define HAS_ZERO_PADDED__(c) ((c) & FLAG_ZEROS)
#define HAS_PRINT_SIGN__(c) ((c) & FLAG_PSIGN)
#define HAS_BLANK_IF_POS__(c) ((c) & FLAG_SPACE)
#define HAS_SHARP_FLAG__(c) ((c) & FLAG_SHARP)
/* Check if the specified character is a "precision" character. i.e, it gives information on paramater size (long, short, long long). */
static int is_spe_ (char c) {
return c == 'l'
|| c == 'h' ;
}
/* Check if the specified character is a "format" character. i.e, it gives information about the type of parameter and the conversion to do. */
static int is_format_ (char c) {
return c == 's'
|| c == 'i'
|| c == 'x'
|| c == 'u'
|| c == 'd'
|| c == 'c'
|| c == 'u'
|| c == 'x'
|| c == 'X'
|| c == 'o'
|| c == 'p'
|| c == 'f'
|| c == 'e'
|| c == 'E'
|| c == 'g'
|| c == 'G'
|| c == 'n';
}
static enum flag_t get_flags_ (const char **format) {
enum flag_t flags = 0 ;
for (; ; (*format)++) {
switch (**format) {
case '-': flags |= FLAG_LJUST ; break ;
case '0': flags |= FLAG_ZEROS ; break ;
case '+': flags |= FLAG_PSIGN ; break ;
case ' ': flags |= FLAG_SPACE ; break ;
case '#': flags |= FLAG_SHARP ; break ;
default:
goto end ;
}
}
end:
return flags ;
}
/* Get information from format, and store it into the specified arguments :
flags is set to a combination of allowed FLAG (see above), if there is flag in format
min_width is set to the minimum width the representation may be, if there is one specified
max_width is set to the maximum width the representation may be, if there is one specified
args is used in case of the minimum width must be taken from a parameter
All parameters, except format and args, may be NULL. In this cas, they are not set. */
static void get_format_parts_ (const char *format,
enum flag_t *flags,
int *min_width,
int *max_width,
va_list *args) {
int tmp ;
char *aux ;
enum flag_t f ;
/* Set flags value, if it's not NULL. */
f = get_flags_ (&format) ;
if (flags != NULL) *flags = f;
/* Set min_width value. If format contains '*', min_width is taken from arg_list, otherwize it's taken from format. */
tmp = -1 ;
if (*format != '*') {
tmp = strtol(format, &aux, 10) ;
if (aux == format) {
tmp = -1 ;
}
format = aux ;
}
else {
tmp = va_arg(*args, unsigned int) ;
}
if (min_width != NULL) *min_width = tmp ;
/* Set max_width, if it's 0, just set it to UINT_MAX. */
tmp = -1 ;
if (*format == '.') {
++format ;
if (*format != '*') {
tmp = strtol(format, &aux, 10) ;
}
else {
tmp = va_arg(*args, unsigned int) ;
}
}
if (max_width != NULL) *max_width = tmp ;
}
/* Get an vp_intmax_t from va_list after converting it according to spe.
spe must be h (short), l (long int), L (vp_intmax_t) or any other character (int)
args must not be NULL */
static vp_intmax_t get_lli_ (char spe, va_list *args) {
switch (spe) {
case 'h':
return (vp_intmax_t)va_arg(*args, int) ;
break ;
case 'l':
return (vp_intmax_t)va_arg(*args, long int) ;
break ;
#ifdef VP_C99__
case 'L':
return (vp_intmax_t)va_arg(*args, vp_intmax_t) ;
break ;
#endif
default:
return (vp_intmax_t) va_arg(*args, int) ;
}
}
/* Get an vp_uintmax_t from va_list after converting it according to spe.
spe must be h (unsigned short), l (unsigned long int), L (vp_uintmax_t) or any other character (unsigned int)
args must not be NULL */
static vp_uintmax_t get_ulli_ (char spe, va_list *args) {
switch (spe) {
case 'h':
return (vp_uintmax_t)va_arg(*args, unsigned int) ;
break ;
case 'l':
return (vp_uintmax_t)va_arg(*args, unsigned long int) ;
break ;
#ifdef VP_C99__
case 'L':
return (vp_uintmax_t)va_arg(*args, vp_uintmax_t) ;
break ;
#endif
default:
return (vp_uintmax_t) va_arg(*args, unsigned int) ;
}
}
/* Return the number of character needed to print var in base base. */
static int ullinchar_ (vp_uintmax_t var, int base) {
int count = 0 ;
if (var == 0) {
return 1 ;
}
for (; var != 0; var /= base) {
count ++ ;
}
return count ;
}
/* Convert the vp_uintmax_t val into a string, stored in the array pointed by buff. The conversion is made
according to base and upper. If nchar is not NULL, it contains the number of character used to represend val.
The char array is NOT nul terminated. */
static int ulli2str_ (vp_uintmax_t val,
char *buff,
int base,
int upper,
int *nchar)
{
static const char *hexxdigits = "0123456789abcdefghijklmnopqrstuvwxyz" ;
int i ;
int ntchar ;
base = (base == 0) ? 10 : base ;
ntchar = ullinchar_ (val, base) ;
for (i=ntchar-1; i >= 0; i--, val /= base) {
buff[i] = hexxdigits[val%base] ;
if (upper) {
buff[i] = toupper(buff[i]) ;
}
}
if (nchar != NULL) {
*nchar = ntchar ;
}
return ntchar ;
}
/* Print everything needed before a number according to specification :
neg if the number is negative
padd the minimum_width
sign if the sign must be printed, even with positive value
blank if a blank character must be printed before positive value
left_justified
nchar the number of char used to represent the number */
static int vp_before_number_ (int neg,
int padd,
int sign,
int blank,
int zero_padded,
int left_justified,
int nchar)
{
int count = 0 ;
char padchar = (zero_padded) ? '0' : ' ' ;
if (left_justified || zero_padded) {
if (neg) {
putchar('-') ;
++ count ;
}
else if (sign) {
putchar('+') ;
++ count ;
}
else if (blank && !zero_padded) {
putchar(' ') ;
++ count ;
}
}
if (!left_justified) {
count += ((sign || blank) && !zero_padded) ? 1 : 0 ;
for (; count + nchar < padd; ++count) {
putchar(padchar) ;
}
if (sign && !zero_padded) {
putchar('+') ;
}
else if (blank && !zero_padded) {
putchar(' ') ;
}
}
return count ;
}
static int vp_after_number_ (int padd,
int nchar_printed,
int left_justified) {
int count = 0 ;
if (left_justified) {
for (; count + nchar_printed < padd; ++count) {
putchar(' ') ;
}
}
return count ;
}
#define BUFF_INT_REPR_SIZE__ 200
/* Print a formated string corresponding to val according to the specified parameters :
padd : the representation of val will be at least of padd character
sign : if not 0, a '+' character will be put before positive value
blank : if not 0 and sign not 1, a blank character will be put before positive value
zero_padded : if not 0, '0' character will be used instead of blank character to do padding
left_justified
base : the base conversion to for the conversion
upper : if specified and base >= 10, the letters will be output upper instead of lower */
static int vp_format_ulli_ (vp_uintmax_t val,
int padd,
int sign,
int blank,
int zero_padded,
int left_justified,
int base,
int upper)
{
char buff[BUFF_INT_REPR_SIZE__] ;
int ncharprint, count = 0;
int i ;
ulli2str_ (val, buff, base, upper, &ncharprint) ;
count += vp_before_number_ (0, padd, sign, blank, zero_padded, left_justified, ncharprint) ;
for (i=0; i<ncharprint; ++i, ++count) {
putchar(buff[i]) ;
}
count += vp_after_number_ (padd, count, left_justified) ;
return count ;
}
/* Print a formated string corresponding to val according to the specified parameters (base conversion is 10) :
padd : the representation of val will be at least of padd character
sign : if not 0, a '+' character will be put before positive value
blank : if not 0 and sign not 1, a blank character will be put before positive value
zero_padded : if not 0, '0' character will be used instead of blank character to do padding
left_justified */
static int vp_format_lli_ (vp_intmax_t val,
int padd,
int sign,
int blank,
int zero_padded,
int left_justified)
{
int count = 0;
vp_uintmax_t abs = (val < 0) ? -val : val ;
if (val < 0 && (left_justified || zero_padded)) {
putchar('-') ;
count ++ ;
padd -- ;
sign = 0 ;
blank = 0 ;
}
count += vp_format_ulli_ (abs, padd, sign, blank, zero_padded, left_justified, 10, 0) ;
return count ;
}
/* Print a formated string according to the format contains in 'format', the value is taken from args accord to spe specification as a signed integer. */
static int vp_li_ (const char *format, va_list *args, char spe) {
vp_intmax_t val ;
int padd ;
enum flag_t flags ;
get_format_parts_ (format, &flags, &padd, NULL, args) ;
padd = (padd < 0) ? 0 : padd ;
val = get_lli_ (spe, args) ;
return vp_format_lli_ (val, padd, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags), HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags)) ;
}
/* Print a formated string according to the format contains in 'format', the value is taken from args accord to spe specification as un unsigned integer. */
static int vp_ulli_ (const char *format, va_list *args, char spe) {
vp_uintmax_t val ;
int padd ;
enum flag_t flags ;
get_format_parts_ (format, &flags, &padd, NULL, args) ;
padd = (padd < 0) ? 0 : padd ;
val = get_ulli_ (spe, args) ;
return vp_format_ulli_ (val, padd, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags), HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags), 10, 0) ;
}
/* Print a formated string according to the format contains in 'format', the value is taken from args accord to spe specification as un unsigned integer.
Upper paramater just specified if letter must be set upper (1) or lower (0). */
static int vp_ullx_ (const char *format, va_list *args, char spe, char upper) {
vp_uintmax_t val ;
enum flag_t flags ;
int padd;
get_format_parts_ (format, &flags, &padd, NULL, args) ;
padd = (padd < 0) ? 0 : padd ;
val = get_ulli_ (spe, args) ;
if (HAS_SHARP_FLAG__ (flags) && val != 0) {
padd -= 2 ;
putchar('0') ;
putchar((upper) ? 'X' : 'x') ;
}
return vp_format_ulli_ (val, padd, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags), HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags), 16, upper) ;
}
#define CHAR_FOR_PTR__ 8
/* Print a formated string according to the format contains in 'format', the value is taken from args accord to spe specification and converted as un unsigned int.
Only '-' flags is allowed in format, and the only value used from format is min_width (see above). The conversion is done with no zero padded, with at least a
minimum width of CHAR_FOR_PTR, and in uppercase letter. */
static int vp_ptr_ (const char *format, va_list *args, char spe) {
vp_uintmax_t val = (unsigned int)va_arg(*args, void*) ;
int padd ;
enum flag_t flags ;
int count = 0 ;
get_format_parts_ (format, &flags, &padd, NULL, args) ;
padd = (padd < 0) ? 0 : padd ;
if (HAS_LEFT_JUSTIFIED__(flags)) {
count = vp_format_ulli_ (val, CHAR_FOR_PTR__, 0, 0, 1, 0, 16, 1) ;
for (; count < padd; ++count) {
putchar(' ') ;
}
}
else {
count = CHAR_FOR_PTR__ ;
for (; count < padd; ++count) {
putchar(' ') ;
}
vp_format_ulli_ (val, CHAR_FOR_PTR__, 0, 0, 1, 0, 16, 1) ;
}
return count ;
}
/* Print a formated string according to the format contains in 'format', the value is taken from args accord to spe specification as un unsigned integer. */
static int vp_ullo_ (const char *format, va_list *args, char spe) {
vp_uintmax_t val ;
enum flag_t flags;
int padd;
get_format_parts_ (format, &flags, &padd, NULL, args) ;
padd = (padd < 0) ? 0 : padd ;
if (HAS_SHARP_FLAG__ (flags)) {
putchar('0') ;
padd -- ;
}
val = get_ulli_ (spe, args) ;
return vp_format_ulli_ (val, padd, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags), HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags), 8, 0);
}
/* Print the wide string pointed by str according to specified parameters :
max_width specify the maximum number of character from str that must be printed, this value must be lower than str length
padd specify the minimum amount of character that must be printed, if it is more than str length or max_width, blank character are added
left_justified specify if string must be left justified */
static int vp_format_wstr_ (const wchar_t *str,
int max_width,
int padd,
int left_justified) {
int count = 0 ;
if (left_justified) {
for (; count < max_width && *str != '\0'; ++count, ++str) {
putwchar(*str) ;
}
}
else {
count = wcslen(str) ;
count = (count > max_width) ? max_width : count ;
}
for (; count < padd ; ++count) {
putchar(' ') ;
}
if (!left_justified) {
for (; max_width > 0 && *str != '\0'; ++str, --max_width) {
putwchar(*str) ;
}
}
return count ;
}
/* Print the string pointed by str according to specified parameters :
max_width specify the maximum number of character from str that must be printed, this value must be lower than str length
padd specify the minimum amount of character that must be printed, if it is more than str length or max_width, blank character are added
left_justified specify if string must be left justified */
static int vp_format_str_ (const char *str,
int max_width,
int padd,
int left_justified) {
int count = 0 ;
if (left_justified) {
for (; count < max_width && *str != '\0'; ++count, ++str) {
putchar(*str) ;
}
}
else {
count = strlen(str) ;
count = (count > max_width) ? max_width : count ;
}
for (; count < padd ; ++count) {
putchar(' ') ;
}
if (!left_justified) {
for (; max_width > 0 && *str != '\0'; ++str, --max_width) {
putchar(*str) ;
}
}
return count ;
}
/* Print the string (or wide string, specified by spe) contains in args, according to format. Only '-' flags is allowed. */
static int vp_str_ (const char *format, va_list *args, char spe) {
char *str ;
wchar_t *wstr ;
int max_width, min_width ;
enum flag_t flags ;
get_format_parts_ (format, &flags, &min_width, &max_width, args) ;
min_width = (min_width < 0) ? 0 : min_width ;
max_width = (max_width < 0) ? UINT_MAX : max_width ;
if (spe == 'l') {
wstr = va_arg(*args, wchar_t*) ;
return vp_format_wstr_ (wstr, max_width, min_width, HAS_LEFT_JUSTIFIED__(flags)) ;
}
else {
str = va_arg(*args, char *) ;
return vp_format_str_ (str, max_width, min_width, HAS_LEFT_JUSTIFIED__(flags)) ;
}
return 0 ;
}
/* Simple output for string with no format ! */
static int vp_simple_str_ (const char *str) {
int count = 0 ;
for (; *str != '\0'; ++count, ++str) {
putchar(*str) ;
}
return count ;
}
/* Print the char (or whar_t, specified by spe) contains in args. */
static int vp_char_ (const char *format, va_list *args, char spe) {
if (spe == 'l') {
putwchar(va_arg(*args, int)) ;
return sizeof(wchar_t) ;
}
else {
putchar(va_arg(*args, int)) ;
return 1 ;
}
}
/* Get the number of character needed to represent the integer part of a double. */
static int dbl_intp_nchar_ (double val) {
int count = 0 ;
if (val < 1) {
return 1 ;
}
for (val = floor(val); val > DBL_MIN; val = floor(val/10)) {
count ++ ;
}
return count;
}
#ifndef VP_C99__
double round (double x) {
double r = floor(x) + 0.5;
return (r < x) ? ceil(x) : r ;
}
#endif
/* Convert the double val into a string, stored in the array pointed by buff. val must be positive The conversion is made
according to base and upper. If nchar is not NULL, it contains the number of character used to represend val.
The char array is NOT nul terminated. */
static int dbl2strf_ (double val,
char *buff,
int always_point,
int precision,
int *nchar)
{
static const char *digits = "0123456789" ;
int i, count = 0 ;
int nchar_intp = dbl_intp_nchar_ (val) ;
int nchar_total ;
for (i=0; i<precision; ++i) {
val *= 10.0 ;
}
val = round(val) ;
/* nchar = number of char for integer part (from dbl_intp_nchar_) + one for point + precision. */
nchar_total = nchar_intp + precision + ((always_point || precision > 0) ? 1 : 0);
for (i = nchar_total - 1; i > nchar_intp; --i, val = floor(val/10), ++count) {
if (i > DBL_DIG) {
buff[i] = '0' ;
}
else {
buff[i] = digits[(int)fmod(val, 10)] ;
}
}
if (always_point || precision > 0) {
buff[i--] = '.' ;
++ count ;
}
for (; i >= 0; --i, val = floor(val/10), ++count) {
if (i > DBL_DIG) {
buff[i] = '0' ;
}
else {
buff[i] = digits[(int)fmod(val, 10)] ;
}
}
if (nchar != NULL) {
*nchar = count ;
}
return count ;
}
#define BUFF_DOUBLE_REPR_SIZE__ BUFF_INT_REPR_SIZE__
static int vp_double_f_ (double val,
char flags,
int min_width,
int precision) {
char buff[BUFF_DOUBLE_REPR_SIZE__] ;
int neg=0, nbchar_print, i ;
int count = 0 ;
if (val < 0) {
val = -val ;
neg = 1 ;
}
nbchar_print = dbl2strf_ (val, buff, HAS_SHARP_FLAG__ (flags), precision, NULL) ;
count += vp_before_number_ (neg, min_width, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags),
HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags), nbchar_print) ;
for (i=0; i<nbchar_print; ++i, ++count) {
putchar(buff[i]) ;
}
count += vp_after_number_ (min_width, count, HAS_LEFT_JUSTIFIED__ (flags)) ;
return count ;
}
#define DBL_MIN_EXP_DIG__ 3
/* Transforme a double representation as f format (2005465.0) in an exponent representation WITHOUT exponent (2.005465),
the exponent value is stored in exp args and the number of char necessary is returned.
val is the value represented in buff
nbchar is the number of character used to represent val in buff
precision is the precision required (i.e the number of character after point)
always_point must be true if a point should always be printed (event for 2.e+001 for example)
start_buff will point to the beginning of the buffer, it is possible that this function modified it */
static int dbl2stre_ (double val,
char *buff,
int always_point,
int precision,
int *nchar,
int *exp) {
*exp = 0 ;
for (; val > 10.0; val /= 10.0, ++(*exp)) { }
for (; val < 1.0; val *= 10.0, --(*exp)) { }
return dbl2strf_ (val, buff, always_point, precision, nchar) ;
}
#define DBL_PRECI_MAX__ DBL_DIG
static int vp_double_e_ (double val,
enum flag_t flags,
int min_width,
int precision,
int upper) {
char buff[BUFF_DOUBLE_REPR_SIZE__] ;
int neg=0, nbchar, count ;
int i ;
int exp ;
if (val < 0) {
val = -val ;
neg = 1 ;
}
nbchar = dbl2stre_ (val, buff, HAS_SHARP_FLAG__ (flags), precision, NULL, &exp) ;
/* Put the e and sign : */
buff[nbchar++] = (upper) ? 'E' : 'e' ;
/* Nom, we print ! */
count = 0 ;
count += vp_before_number_ (neg, min_width, HAS_PRINT_SIGN__ (flags), HAS_BLANK_IF_POS__ (flags),
HAS_ZERO_PADDED__ (flags), HAS_LEFT_JUSTIFIED__ (flags), nbchar + DBL_MIN_EXP_DIG__ + 1) ;
for (i=0; i<nbchar; ++i, ++count) {
putchar(buff[i]) ;
}
/* Juste use the unsigned long long function to put the exponent. */
count += vp_format_lli_ (exp, DBL_MIN_EXP_DIG__ + 1, 1, 0, 1, 0) ;
count += vp_after_number_ (min_width, count, HAS_LEFT_JUSTIFIED__ (flags)) ;
return count ;
}
#define DBL_MIN_REP_G__ 1e-3
#define DBL_MAX_REP_G__ 1e3
static int vp_double_g_ (double val,
enum flag_t flags,
int min_width,
int precision,
int upper) {
if (val < DBL_MAX_REP_G__ && val > DBL_MIN_REP_G__) {
if (precision > 0) {
if (DBL_EPSILON > abs(val - ceil(val))) {
precision = 0 ;
}
}
return vp_double_f_ (val, flags, min_width, precision) ;
}
else {
return vp_double_e_ (val, flags, min_width, precision, upper) ;
}
}
#ifndef VP_C99__
static int isnan(double x) {
return x != x ;
}
static int isfinite(double x) {
return !((x == x) && (x != 0) && (x + 1 == x));
}
#endif
#define NAN_STR__ "NaN"
#define INF_STR__ "Inf"
static int vp_double_nan_or_inf_ (const char *str,
enum flag_t flags,
int min_width) {
int count ;
count = vp_before_number_ (0, min_width, 0, 0, 0, HAS_LEFT_JUSTIFIED__(flags), strlen(str)) ;
count += vp_simple_str_ (str) ;
count += vp_after_number_ (min_width, count, HAS_LEFT_JUSTIFIED__ (flags)) ;
return count ;
}
#define DEFAULT_PRECISION__ 6
static int vp_double_ (const char *format, va_list *args, char type) {
double val ;
int min_width, precision ;
int count ;
enum flag_t flags ;
get_format_parts_ (format, &flags, &min_width, &precision, args) ;
min_width = (min_width < 0) ? 0 : min_width ;
precision = (precision < 0) ? DEFAULT_PRECISION__ : precision ;
val = va_arg(*args, double) ;
if (isnan(val)) {
return vp_double_nan_or_inf_ (NAN_STR__, flags, min_width) ;
}
else if (!isfinite(val)) {
return vp_double_nan_or_inf_ (INF_STR__, flags, min_width) ;
}
switch (type) {
case 'e': count = vp_double_e_ (val, flags, min_width, precision, (toupper(type) == type)) ; break ;
case 'g': count = vp_double_g_ (val, flags, min_width, precision, (toupper(type) == type)) ; break ;
case 'f':
default : count = vp_double_f_ (val, flags, min_width, precision) ;
}
return count ;
}
void vp_store_count_ (const char *format,
va_list *args,
int count) {
int *n = va_arg (*args, int*) ;
*n = count ;
}
/* Get the type and specification of the format :
type is set to a format type (d, f, x, X, etc... )
spe is set to a specification (l, h, L for ll) if one is found, otherwize it is set to '\0'
The function returns a pointer to the type character (i.e containing format type information) from the format parameter */
static const char *get_type_and_spe_ (const char *format, char *type, char *spe) {
*spe = '\0' ;
for (; *format != '\0' && !is_format_(*format); ++format) {
if (is_spe_(*format)) {
*spe = *format ;
format ++ ;
#ifdef VP_C99__
if (*spe == 'l' && *format == 'l') {
*spe = 'L' ;
format ++ ;
}
#endif
break ;
}
}
*type = *format ;
return format ;
}
static int vprintf_ (const char *format, va_list args) {
int count = 0 ;
char f, s ;
const char *tmp ;
for (; *format != '\0'; format++) {
switch (*format) {
case '%':
++ format ;
tmp = get_type_and_spe_ (format, &f, &s) ;
switch (f) {
case 'd':
case 'i': count += vp_li_ (format, &args, s) ; break ;
case 's': count += vp_str_ (format, &args, s) ; break ;
case 'c': count += vp_char_ (format, &args, s) ; break ;
case 'u': count += vp_ulli_ (format, &args, s) ; break ;
case 'x': count += vp_ullx_ (format, &args, s, 0) ; break ;
case 'X': count += vp_ullx_ (format, &args, s, 1) ; break ;
case 'p': count += vp_ptr_ (format, &args, s) ; break ;
case 'o': count += vp_ullo_ (format, &args, s) ; break ;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G': count += vp_double_ (format, &args, f) ; break ;
case 'n': vp_store_count_ (format, &args, count) ; break ;
case '%':
default: putchar(*format) ; count ++ ;
}
format = (*tmp) ? tmp : format ;
break ;
default:
putchar(*format) ;
++ count ;
}
}
return count ;
}
int my_printf (const char *format, ...) {
int r ;
va_list args ;
va_start(args, format) ;
r = vprintf_ (format, args) ;
va_end(args) ;
return r ;
}
Les mots-clefs du C
Un projet que j’avais initié, du temps où le SdZ était un site que j’aimais fréquenter. Le but ? Donner une explication simple et illustrée des différents mot-clefs qui existent en C. La plupart y sont présents, dont une explication sur asm que j’avais pris beaucoup de temps à rédiger et qui me parait pas claire du tout avec quelques années de recul (à moins que ce soit parce que c’est l’assembleur qui est un langage peu clair).
Lire ce topic, c’est aussi voir, pour ceux qui n’ont pas connu, à quoi ressemblait le zCode et se dire que le Markdown, c’est plus simple.
Voici une liste des mots-clefs sur lesquels on avait écrit à l’époque.
- asm
- if / else
- int
- do-while / const
- break / continue / goto / case / default / switch / signed / unsigned
- short / int / long
- sizeof / register
Un ECS en C
Un dernier mais non des moindres, une implémentation du principe d'Entities, Components, Systems en C, alors que la plupart des articles sur le sujet que j’ai vu le font en C++.
Voilà, fin de cette série de liens sur ce qu’on peut s’amuser à faire en C. Bon personnellement, je ne le ferai pas parce que je suis habitué au confort apporté par des langages comme C++, Python ou C#. Mais ça prouve que le C est vraiment un langage à tout faire qu’on peut tordre dans tous les sens pour faire des trucs insensés.