Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

C++ VSC: Eric Lecolinet - Télécom Paristech WWW - Telecom-Paristech - FR/ Elc

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 22

C++ vs C

Eric Lecolinet - Tlcom ParisTech www.telecom-paristech.fr/~elc

structures
class nexiste pas en C struct existe
- mais ne contient que des variables dinstance

il ny a :
- pas de mthodes (ni de classe, ni dinstance) - pas de constructeur, ni de destructeur - pas de variables de classe - pas de niveaux daccs : tout est public - pas de rednition des oprateurs loprateur = (affectation) recopie les champs un par un il ne peut tre modi ni interdit (sauf astuces)

allocation/destruction
- pas doprateurs new et delete (ni de constructeur, ni de destructeur) - malloc( ), calloc( ), realloc( ) allouent de la mmoire - free( ) libre la mmoire - sizeof( ) : taille (en octets) - toujours initialiser les pointeurs : un pointeur indni doit valoir NULL
typedef struct { double x, y; } Point; void foo() { Point * p = (Point*) malloc(sizeof(Point)); ..... free(p); p = NULL; .....

notes sur typedef


typedef dnit un nouveau nom de type
struct POINT { double x, y; }; typedef struct POINT Point; void foo() { struct POINT pt1; // struct est ncessaire Point pt2 = {4.1, 7.3}; Point tab[10]; Point * p = (Point*) malloc(sizeof(Point)); ....... }

Forme contracte:
typedef struct POINT { double x, y; } Point;

ou encore :
typedef struct { double x, y; } Point;

encapsulation en C
C
typedef struct { char* name; long id; } User; User* createUser (const char* name, int id); void destroyUser (User*); void setUserName (User*, const char* name); void printUser (const User*); .... void foo() { User* u = createUser("Dupont"); setUserName(u, "Durand"); ..... destroyUser(u); u = NULL;

C++
class User { char* name; // en fait utiliser string long id; public: User (const char* name, int id); virtual ~User( ); virtual void setName(const char* name); virtual void print() const; .... }; void foo() { User* u = new User("Dupont"); u->setName("Durand"); .... delete u; u = NULL;

hritage et polymorphisme
pas dhritage
class Player : public User { .... virtual void print() const; .... };

pas de polymorphisme
User * user = new Player(...); user->print(); // liaison tardive si print() est virtual

certaines librairies (GTK...) les simulent


- en imbriquant les objets - via des pointeurs de fonctions

pseudo-hritage
typedef struct User { int a; void (*print) (const struct User*); } User; typedef struct Player { User base; int b; } Player; User* newUser() { User* p = (User*) malloc(sizeof(User)); p->a = 0; p->print = printUser; return p; } Player* newPlayer() { Player* p = (Player*) malloc(sizeof(Player)); p->base.a = 0; p->base.print = printPlayer; // cast ncessaire p->b = 0; return p; } int main() { Player* p = newPlayer(); p->base.a = 1; p->b = 2; print(p); } // NB: en fait il faudrait partager les pointeurs de fonctions // de tous les objets dune mme classe via une vtable

// subclass

void print(const User* u) { (u->print)(u); } void printUser(const User *u) { printf("printUser a=%d \n", u->a); } void printPlayer(const Player *u) { printf("printPlayer a=%d b=%d\n", u->base.a, u->b); }

passage des arguments


seulement par valeur
- les arguments sont recopis dans les paramtres - sauf pour les tableaux : cest leur adresse qui est recopie dans un pointeur
int ChercheVal(oat tab[ ], int taille, oat val) { int k; for (k = 0; k < taille; k++) { if (tab[k] == val) return k; } return -1; // tab est un pointeur de type oat *

void foo() { oat tab[ ] = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; int indice = ChercheVal(tab, 10, 4.); .... }

// tab est un tableau de type oat[]

passage des arguments


les pointeurs permettent de rcuprer une valeur
- via une indirection
void foo() {! int i;

oat x; } scanf("%d %f", &i, &x);

scanf() rcupre les adresses des variables i et j de foo() ce qui lui permet de modier leurs valeurs (= ce quelles contiennent en mmoire)

exemple
void swap1(int i, int j) { int aux; aux = i; i = j; j = aux; } int main() { int a = 5, b = 0; swap1(a, b); printf(" a = %d, b = %d \n", a, b); } void swap2(int* pi, int* pj) { int aux; aux = *pi; *pi = *pj; *pj = aux; } int main() { int a = 5, b = 0; swap2(&a, &b); printf(" a = %d, b = %d \n", a, b); }

exemple
void swap1(int i, int j) { int aux; aux = i; i = j; j = aux; } int main() { int a = 5, b = 0; swap1(a, b); printf(" a = %d, b = %d \n", a, b); } a et b inchangs NB: pareil en Java !!! void swap2(int* pi, int* pj) { int aux; aux = *pi; *pi = *pj; *pj = aux; } int main() { int a = 5, b = 0; swap2(&a, &b); printf(" a = %d, b = %d \n", a, b); } swap2 change les valeurs de a et b car pi et pj de swap2() pointent sur les variables a et b de foo()

pointeurs et tableaux
tab

int tab[10]; int* p = tab;

// quivaut : p = &tab[0] // valeur du ime lment du tableau // adresse du ime lment du tableau

tab[k] == *(tab + k) &tab[k] == tab + k De mme : p[k] == *(p + k) &p[k] == p + k

// valeur du ime lment partir de p // adresse du ime lment partir de p

pointeurs et tableaux
int tab[10]; int* p = tab;

Un pointeur est une variable et peut donc changer de valeur


p = tab; p++; CORRECT

Un tableau nest pas une variable !


tab = p; tab++; FAUX !!!

Dans les deux cas :


int val = tab[i]; int val = p[i]; CORRECT CORRECT

arithmtique des pointeurs


char tabc[10]; char* pc = tabc; int tabi[10]; int* pi = tabi;

! !

pc = pc + 5; pi = pi + 5;

// 5 caractres plus loin (= 5 octets) // 5 entiers plus loin (= 20 octets si int sur 4 octets)

Larithmtique sur les pointeurs na de sens que : lorsque quils sont typs et de mme type !

pointeurs non typs (void*)


void* = pointeur non typ
affectation entre pointeurs de types diffrents possible via des casts sport haut risque !
int i; char* pc; pc = (char*) &i; // un int occupe 4 octets sur telle machine // un char occupe 1 octet // pc pointe sur le 1er octet de i

attention
les tailles dpendent des machines (et des options de compilation) les int, oat... doivent commencer certaines adresses : (ex: tous les 4 octets pour certains processeur 32 bits)

tableaux bi-dimensionnels
oat mat[5][10] = {{1, 2, 3, 4}, {5, 6}, {7, 8, 9}}; oat truc[][10] = {{1, 2, 3, 4}, {5, 6}, {7, 8, 9}};

initialisation ligne par ligne nombre de lignes ventuellement implicite nombre de colonnes obligatoire et constant : mat[y][x] == *(mat + y*NB_COL + x)

tableaux de tableaux
autre manire de crer des tableaux bi-dimensionnels
char * jours[ ] = { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche", };

etc...

jours[ligne][colonne] renvoie le caractre correspondant les lignes ne sont pas ncessairement de mme taille

chanes de caractres
pas de classe string, il faut manipuler les char* une chane de caractres
est une suite de char termine par un 0 (la valeur entire nulle) peut tre dnie de 2 manires : par un tableau :! ! ! ! ! ! ! ! ! char s[ ] = "abcd"; char* p = "abcd"; par un pointeur pointant sur un littral : ! !

0 nal rajout automatiquement dans les 2 cas

chanes de caractres
pas de classe string, il faut manipuler les char* une chane de caractres
est une suite de char termine par un 0 (la valeur entire nulle) peut tre dnie de 2 manires : par un tableau :! ! ! ! ! ! ! ! ! char s[ ] = "abcd"; char* p = "abcd"; par un pointeur pointant sur un littral : ! !

0 nal rajout automatiquement dans les 2 cas

attention
sizeof(s) = 5 : nombre de caractres + 0 nal sizeof(p) = 4 : taille du pointeur (4 si processeur 32 bits) => seul le 0 nal permet de dterminer la taille dans ce cas

lire une chanes de caractres


char* s; scanf("%s", s); char s[20]; scanf("%s", s); char s[1000]; scanf("%s", s); // ACCEPTABLE la rigueur // DANGEREUX !!! // FAUX !!!

Pourquoi ?

lire une chanes de caractres


char* s; scanf("%s", s); char s[20]; scanf("%s", s); char s[1000]; scanf("%s", s); // ACCEPTABLE la rigueur // DANGEREUX !!! // FAUX !!!

Pourquoi ? parce que la mmoire nest pas alloue ou (possiblement) trop petite

lire une chanes de caractres


plus sr
char s[20]; scanf("%19s", s); // lit au plus 19 caractres (19=20-1 attention au 0 nal !)

plus gnral
char buffer[1000]; if ( fgets( buffer, sizeof(buffer), stdin ) != NULL ) ..... char* fgets(char* str, int num, FILE* stream)

lit la ligne entire depuis le chier, mais au plus num-1 caractres


(le reste sera lu par le prochain appel fgets())

retourne NULL en n de chier ou en cas derreur Note: scanf("%s", s) sarrte au premier espace rencontr, contrairement fgets()

attention la scurit !
#include <stdio.h> #include <stdbool.h> #include <string.h> #define CODE_SECRET "1234" int main(int argc, char**argv) { bool is_valid = false; char code[5]; printf("Enter password: "); scanf("%s", code); if (strcmp(code, CODE_SECRET) == 0) is_valid = true; if (is_valid) printf("Welcome dear customer ;-)\n"); else printf("Invalid password !!!\n"); return 0; }

Questions : Que fait ce programme ? Est-il sr ?

attention la scurit !
#include <stdio.h> #include <stdbool.h> #include <string.h> #define CODE_SECRET "1234" int main(int argc, char**argv) { bool is_valid = false; char code[5]; printf("Enter password: "); scanf("%s", code); if (strcmp(code, CODE_SECRET) == 0) is_valid = true; if (is_valid) printf("Welcome dear customer ;-)\n"); else printf("Invalid password !!!\n"); printf("Adresses: %p %p %p %p\n", code, &is_valid, &argc, argv); return 0; }

Avec LLVM sous MacOSX 10.7.1 :


Enter password: 111111 Welcome dear customer ;-) Adresses: 0x7fff5fbff98a 0x7fff5fbff98f 0x7fff5fbff998 0x7fff5fbff900

Dbordement des chanes : technique typique de piratage informatique

autres fonctions dentres/sorties


depuis/dans un chier
int fscanf(FILE* stream, const char* format, ...) int fprintf(FILE* stream, const char* format, ...) int fputs(const char* str, FILE* stream)

depuis/dans une chane de caratres


int sscanf(const char* from_str, const char* format, ...) int sprintf(char* to_str, const char* format, ...) // attention la taille de to_str !!!

donnes brutes (format binaire)


size_t fread(void* ptr, size_t size, size_t count, FILE* stream) size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream)

E/S console pseudo chiers: stdin, stdout, stderr

macros
#dene TAILLE 1024 #dene MESSAGE "hello word" #dene MIN(X,Y) ((X) < (Y) ? (X) : (Y)) char tableau[TAILLE]; int i = 1, j = 2; int k = MIN(i, j); // macro paramtre

// ne pas crire char tableau[1024]

substitution textuelle par le prprocesseur C avant la compilation les macros permettent (entre-autres) de paramtrer le programme

macros
attention aux effets de bords :
#dene MIN(X,Y) ((X) < (Y) ? (X) : (Y)) int i = 1; int j = 2; int res1 = MAX(i++,j++); int res2 = MAX(++i,++J); // res1 = ? // res2 = ?

prfrer les numrations ou variables const quand cest possible


const oat pi = 3.14;

// cette valeur ne peut pas changer

enum Jour {LUNDI=1, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE}; enum Jour j = JEUDI; if (j == SAMEDI) printf("en week end\n");

directives de compilation
#ifndef Person_h #define Person_h #include <string> #include <iostream> #if defined(RAW_POINTERS) # define PTR(TYPE) TYPE* #elif defined(INTRUSIVE_POINTERS) # include "intrusive_ptr.h" # define PTR(TYPE) intrusive_ptr<TYPE> #else # error "Undefined pointer mode" #endif #endif /* Person_h */

taille des types de base


les tailles dpendent de la plate-forme !
- contrairement Java o elles sont spcies par la norme

rgles
char : caractre (1 octet) int : entier (short <= int <= long) short : entier court (>= 16 bits) bool : boolen (depuis C99, inclure stdbool.h) long : entier long (>= 32 bits) oat : ottant simple prcision double : ottant double prcision long double : encore plus prcis

exemples
ex: Sun 32 bits avec gcc : short = 16 / int = long = 32 / oat = 32 / double = 64 bits voir headers standard limits.h et oat.h (dans: /usr/include sous Unix)

signe des types de base


les types sont signs par dfaut sauf char
- a dpend de la plate-forme ! - => source derreurs pour les programmes multi plate-formes unsigned char : de 0 255 signed char : de -128 127

transtypage implicite
conversion implicite vers le type plus grand
Attention : les conversions implicites peuvent tre trompeuses !
int i = 2, j = 4; char c = \n; oat x = 4., y1, y2, y3; i = c; c = i; x = i; i = x; y1 = i + x; y2 = i / x; y3 = i / j;

Que vaut y3 ?

transtypage implicite
conversion implicite vers le type plus grand
Attention : les conversions implicites peuvent tre trompeuses !
int i = 2, j = 4; char c = \n; oat x = 4., y1, y2, y3; i = c; c = i; /* entier tronqu */ x = i; i = x; /* rel tronqu */ y1 = i + x; y2 = i / x; y3 = i / j;

!!! Danger : y3 vaut 0 car i et j sont des int

transtypage explicite
conversion explicite au moyen dun cast
int i = 2, j = 4; oat y, z; y = i / j; z = (oat) i / j; // y = 0.0 // Z = 0.5

Attention : ce qui suit na aucun sens ! char * s = 1234; int i = (int) s; // compile mais compltement faux !

manipulation de bits
oprateurs
&! ET |! OU inclusif ^ ! OU exclusif <<! dcalage gauche >> ! dcalage droite ~ ! complment un int n = 0xff, m = 0; m = n & 0x10; m = n << 2; /* quivalent : m = n * 4 */

Attention: ne pas confondre & avec && (et logique) ni | avec | | (ou logique)

compilation spare
plusieurs chiers
- compils indpendamment
.... int main() { char* s = "cos(0.5)"; float res = calc(s); }

main.c
....

calc.c

plusieurs fonctions
- une fonction dnie un chier - est appele dans d'autres chiers

float calc(char* s) { float res; .... return res; } ....

....

Compilation

librairies main.o calc.o


binaires contenant cos() printf() etc.

Edition de liens

excutable

....

compilation spare
plusieurs chiers
- compils indpendamment
.... int main() { char* s = "cos(0.5)"; float res = calc(s); }

main.c
....

calc.c

plusieurs fonctions
- une fonction dnie un chier - est appele dans d'autres chiers

float calc(char* s) { float res; .... return res; } ....

....

Compilation

danger !
- C suppose que la fonction existe - pas de vrication de la signature dans ce cas !

librairies main.o calc.o


binaires contenant cos() printf() etc. ....

Edition de liens

excutable

compilation spare
trois cas possibles
fonction dnie : dans aucun chier ni librairie => erreur ldition de lien: Symbol not found dans un autre chier avec mme signature => excutable cr et qui fonctionne dans un autre chier avec une autre signature => excutable cr mais plante ou rsultats errons

headers partags
header
- contient dclarations des fonctions dnies dans les .c main.c - inclus la fois dans les chiers : - o les fonctions sont dnies - o les fonctions sont appeles cohrence vrie par transitivit main.o calc.o
#include <stdio.h> #include "calc.h" int main() { char* s = "cos(0.5)"; float res = calc(s); } ....

calc.h
float calc(char* s); ....

calc.c
#include <math.h> #include "calc.h" float calc(char* s) { float res; .... return res; } ....

librairies
binaires contenant cos() printf() etc.

excutable

....

headers partags
header
- contient dclarations des fonctions dnies dans les .c main.c - inclus la fois dans les chiers : - o les fonctions sont dnies - o les fonctions sont appeles aussi pour les fonctions des librairies
#include <stdio.h> #include <math.h>
#include <stdio.h> #include "calc.h" int main() { char* s = "cos(0.5)"; float res = calc(s); } ....

calc.h
float calc(char* s); ....

calc.c
#include <math.h> #include "calc.h" float calc(char* s) { float res; .... return res; } ....

// compile mais // rsultat incorrect sans // #include<math.h>

librairies main.o calc.o


binaires contenant cos() printf() etc. ....

int main() { double x = cos(0.5); printf("Rsultat: %f \n", x); }

excutable

headers partags
obligatoires en C++
- contrairement C : - la compilation choue si une fonction n'est pas dclare - lditeur de liens vrie la signature des fonctions

pas de headers en Java


- mais le compilateur vrie la cohrence des signatures

librairies statiques et dynamiques


librairies statiques
- code binaire insr dans lexcutable la compilation - extension .a (Unix)

librairies dynamiques
- code binaire charg dynamiquement lexcution - .dll (Windows), .so (Linux), dylib (Mac) - avantages : - programmes moins gros et plus rapides (moins de swap si DLL partage) - inconvnient : - ncessite la prsence de la DLL (cf. licences et versions) (cf. variable LD_LIBRARY_PATH (ou quivalent) sous Unix)

mlanger C et C++
meilleure solution
- tout compiler (y compris les .c) avec compilateur C++

si on mlange compil C et compil C++


- dition de liens avec compil C++ - main() doit tre dans un chier C++ - une fonction C doit tre dclare comme suit dans C++
extern "C" void foo(int i, char c, float x); ou extern "C" { void foo(int i, char c, float x); int } goo(char* s, char const* s2);

mlanger C et C++
dans un header C
- pouvant indiffremment tre inclus dans un .c ou un .ccp, crire :
#ifdef __cplusplus extern "C" { #endif void foo(int i, char c, float x); int goo(char* s, char const* s2);

#ifdef __cplusplus } #endif

Vous aimerez peut-être aussi