(→Saída do programa) |
|||
(54 intermediate revisions by the same user not shown) | |||
Line 6: | Line 6: | ||
== Animal simples == | == Animal simples == | ||
− | A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “animal”. | + | A tarefa é a modelação e implementação em C (ficheiros '''.h''' e '''.c''') de uma estrutura de dados que represente uma versão simples do conceito “animal”. |
− | Um animal tem como características o nome (_name), a idade (_age) e o peso (_weight). | + | Um animal tem como características o nome ('''_name'''), a idade ('''_age''') e o peso ('''_weight'''). |
− | Devem ser ainda implementadas as funções newAnimal e destroyAnimal. A função newAnimal reserva memória suficiente para representar um animal e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assume-se que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyAnimal liberta os recursos associados ao animal. | + | Devem ser ainda implementadas as funções '''newAnimal''' e '''destroyAnimal'''. A função newAnimal reserva memória suficiente para representar um animal e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assume-se que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyAnimal liberta os recursos associados ao animal. |
Outras funções a implementar: | Outras funções a implementar: | ||
− | * função de comparação – equalsAnimal –, por forma a considerar que dois animais são iguais se as suas características forem iguais | + | * função de comparação – '''equalsAnimal''' –, por forma a considerar que dois animais são iguais se as suas características forem iguais |
− | * funções de acesso às variáveis de um animal: getAnimalName, getAnimalAge, getAnimalWeight (dado um animal, retornam um dos seus campos) | + | * funções de acesso às variáveis de um animal: '''getAnimalName''', '''getAnimalAge''', '''getAnimalWeight''' (dado um animal, retornam um dos seus campos) |
− | * função printAnimal que, quando aplicada a um animal, apresenta os seus dados ( | + | * função '''printAnimal''' que, quando aplicada a um animal, apresenta os seus dados (usa-se '''printf''' para apresentar cada campo do animal) |
− | * programa – main – que ilustra a utilização das funções anteriores | + | * programa – '''main''' – que ilustra a utilização das funções anteriores |
− | <c> | + | === Interface do conceito "Animal" === |
− | // | + | |
− | </c> | + | {{CollapsedCode|Ficheiro '''Animal.h'''| |
+ | <source lang="c" line> | ||
+ | #ifndef __ANIMAL_H__ | ||
+ | #define __ANIMAL_H__ | ||
+ | |||
+ | // this typedef is used to hide the concept's implementation details | ||
+ | typedef struct animal *Animal; | ||
+ | |||
+ | Animal newAnimal(const char *name, int age, double weight); | ||
+ | void destroyAnimal(Animal animal); | ||
+ | |||
+ | int equalsAnimal(Animal animal1, Animal animal2); | ||
+ | const char *getAnimalName(Animal animal); | ||
+ | int getAnimalAge(Animal animal); | ||
+ | double getAnimalWeight(Animal animal); | ||
+ | void printAnimal(Animal animal); | ||
+ | |||
+ | #endif | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Implementação do conceito "Animal" === | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''Animal.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <string.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include "Animal.h" | ||
+ | |||
+ | struct animal { | ||
+ | char _name[16]; | ||
+ | int _age; | ||
+ | double _weight; | ||
+ | }; | ||
+ | |||
+ | Animal newAnimal(const char *name, int age, double weight) { | ||
+ | Animal animal = (Animal)malloc(sizeof(struct animal)); | ||
+ | if (animal != NULL) { | ||
+ | strcpy(animal->_name, name); | ||
+ | animal->_age = age; | ||
+ | animal->_weight = weight; | ||
+ | } | ||
+ | return animal; | ||
+ | } | ||
+ | |||
+ | void destroyAnimal(Animal animal) { | ||
+ | if (animal) | ||
+ | free(animal); | ||
+ | } | ||
+ | |||
+ | const char *getAnimalName (Animal animal) { return animal->_name; } | ||
+ | int getAnimalAge (Animal animal) { return animal->_age; } | ||
+ | double getAnimalWeight(Animal animal) { return animal->_weight; } | ||
+ | |||
+ | /* note that we require animal1 and animal2 to be valid animals: any of | ||
+ | them being NULL pointers will result in a false comparison. */ | ||
+ | int equalsAnimal(Animal animal1, Animal animal2) { | ||
+ | if (animal1 == NULL || animal2 == NULL) return 0; | ||
+ | return !strcmp(getAnimalName(animal1), getAnimalName(animal2)) && | ||
+ | getAnimalAge(animal1) == getAnimalAge(animal2) && | ||
+ | getAnimalWeight(animal1) == getAnimalWeight(animal2); | ||
+ | } | ||
+ | |||
+ | void printAnimal(Animal animal) { | ||
+ | printf("== Animal ==\n"); | ||
+ | printf("Name: %s\n", getAnimalName(animal)); | ||
+ | printf("Age: %d\n", getAnimalAge(animal)); | ||
+ | printf("Weight: %g\n", getAnimalWeight(animal)); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Programa exemplo === | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''main.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <stdio.h> | ||
+ | #include "Animal.h" | ||
+ | |||
+ | int main() { | ||
+ | Animal a1 = newAnimal("Tareco", 12, 3.4); // could be a cat... | ||
+ | Animal a2 = newAnimal("Piloto", 1, 12.3); // is it a dog?? | ||
+ | |||
+ | printAnimal(a1); | ||
+ | printAnimal(a2); | ||
+ | |||
+ | printf("a1==a1? %s\n", equalsAnimal(a1, a1) ? "yes" : "no"); | ||
+ | printf("a1==a2? %s\n", equalsAnimal(a1, a2) ? "yes" : "no"); | ||
+ | |||
+ | destroyAnimal(a1); | ||
+ | destroyAnimal(a2); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Como compilar e executar? === | ||
+ | |||
+ | O programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável. | ||
+ | |||
+ | gcc -c Animal.c -o Animal.o | ||
+ | gcc -c main.c -o main.o | ||
+ | |||
+ | A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando. | ||
+ | |||
+ | gcc -o main main.o Animal.o | ||
+ | |||
+ | Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima). | ||
+ | |||
+ | A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."): | ||
+ | |||
+ | ./main | ||
== Gato simples == | == Gato simples == | ||
− | A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “gato”. | + | A tarefa é a modelação e implementação em C (ficheiros '''.h''' e '''.c''') de uma estrutura de dados que represente uma versão simples do conceito “gato”. |
− | Um gato tem como características o nome (_name), a idade (_age), o peso (_weight), o volume do ronronar (_purrLevel) e o grau de suavidade do pêlo (_fluffiness). | + | Um gato tem como características o nome ('''_name'''), a idade ('''_age'''), o peso ('''_weight'''), o volume do ronronar ('''_purrLevel''') e o grau de suavidade do pêlo ('''_fluffiness'''). |
− | Devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato. | + | Devem ser ainda implementadas as funções '''newCat''' e '''destroyCat'''. A função '''newCat''' reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo '''_name''' tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função '''destroyCat''' liberta os recursos associados ao gato. |
Outras funções a implementar: | Outras funções a implementar: | ||
− | * função de comparação – equalsCat –, por forma a considerar que dois gatos são iguais se as suas características forem iguais | + | * função de comparação – '''equalsCat''' –, por forma a considerar que dois gatos são iguais se as suas características forem iguais |
− | * funções de acesso às variáveis de um gato: getCatName, getCatAge, getCatWeight, getCatPurrLevel, getCatFluffiness (dado um gato, retornam um dos seus campos) | + | * funções de acesso às variáveis de um gato: '''getCatName''', '''getCatAge''', '''getCatWeight''', '''getCatPurrLevel''', '''getCatFluffiness''' (dado um gato, retornam um dos seus campos) |
− | * função printCat que, quando aplicada a um gato, apresenta os seus dados ( | + | * função '''printCat''' que, quando aplicada a um gato, apresenta os seus dados (usa-se '''printf''' para apresentar cada campo do gato) |
− | * programa – main – que ilustra a utilização das funções anteriores | + | * programa – '''main''' – que ilustra a utilização das funções anteriores |
+ | |||
+ | === Interface do conceito "Cat" === | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''Cat.h'''| | ||
+ | <source lang="c" line> | ||
+ | #ifndef __CAT_H__ | ||
+ | #define __CAT_H__ | ||
+ | |||
+ | typedef struct cat *Cat; | ||
+ | |||
+ | Cat newCat(const char *name, int age, double weight, int purrLevel, | ||
+ | double fluffiness); | ||
+ | void destroyCat(Cat cat); | ||
+ | |||
+ | int equalsCat(Cat cat1, Cat cat2); | ||
+ | const char *getCatName(Cat cat); | ||
+ | int getCatAge(Cat cat); | ||
+ | double getCatWeight(Cat cat); | ||
+ | int getCatPurrLevel(Cat cat); | ||
+ | double getCatFluffiness(Cat cat); | ||
+ | void printCat(Cat cat); | ||
+ | |||
+ | #endif | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Implementation do conceito "Cat" === | ||
+ | |||
+ | Notar a repetição de muitas definições (relativamente a Animal). | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''Cat.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <string.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include "Cat.h" | ||
+ | |||
+ | struct cat { | ||
+ | char _name[16]; | ||
+ | int _age; | ||
+ | double _weight; | ||
+ | int _purrLevel; | ||
+ | double _fluffiness; | ||
+ | }; | ||
+ | |||
+ | Cat newCat(const char *name, int age, double weight, int purrLevel, | ||
+ | double fluffiness) { | ||
+ | Cat cat = (Cat)malloc(sizeof(struct cat)); | ||
+ | if (cat != NULL) { | ||
+ | strcpy(cat->_name, name); | ||
+ | cat->_age = age; | ||
+ | cat->_weight = weight; | ||
+ | cat->_purrLevel = purrLevel; | ||
+ | cat->_fluffiness = fluffiness; | ||
+ | } | ||
+ | return cat; | ||
+ | } | ||
+ | |||
+ | void destroyCat(Cat cat) { | ||
+ | if (cat) | ||
+ | free(cat); | ||
+ | } | ||
+ | |||
+ | const char *getCatName (Cat cat) { return cat->_name; } | ||
+ | int getCatAge (Cat cat) { return cat->_age; } | ||
+ | double getCatWeight (Cat cat) { return cat->_weight; } | ||
+ | int getCatPurrLevel (Cat cat) { return cat->_purrLevel; } | ||
+ | double getCatFluffiness(Cat cat) { return cat->_fluffiness; } | ||
+ | |||
+ | /* note that we require cat1 and cat2 to be valid cats: any of | ||
+ | them being NULL pointers will result in a false comparison. */ | ||
+ | int equalsCat(Cat cat1, Cat cat2) { | ||
+ | if (cat1 == NULL || cat2 == NULL) return 0; | ||
+ | return !strcmp(getCatName(cat1), getCatName(cat2)) && | ||
+ | getCatAge(cat1) == getCatAge(cat2) && | ||
+ | getCatWeight(cat1) == getCatWeight(cat2) && | ||
+ | getCatPurrLevel(cat1) == getCatPurrLevel(cat2) && | ||
+ | getCatFluffiness(cat1) == getCatFluffiness(cat2); | ||
+ | } | ||
+ | |||
+ | void printCat(Cat cat) { | ||
+ | printf("== Cat ==\n"); | ||
+ | printf("Name: %s\n", getCatName(cat)); | ||
+ | printf("Age: %d\n", getCatAge(cat)); | ||
+ | printf("Weight: %g\n", getCatWeight(cat)); | ||
+ | printf("Purr level: %d\n", getCatPurrLevel(cat)); | ||
+ | printf("Fluffiness: %g\n", getCatFluffiness(cat)); | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Programa exemplo === | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''main.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <stdio.h> | ||
+ | #include "Cat.h" | ||
+ | |||
+ | int main() { | ||
+ | Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1); | ||
+ | Cat c2 = newCat("Pantufa", 1, 12.3, 2, 2.7); | ||
+ | |||
+ | printCat(c1); | ||
+ | printCat(c2); | ||
+ | |||
+ | printf("c1==c1? %s\n", equalsCat(c1, c1) ? "yes" : "no"); | ||
+ | printf("c1==c2? %s\n", equalsCat(c1, c2) ? "yes" : "no"); | ||
+ | |||
+ | destroyCat(c1); | ||
+ | destroyCat(c2); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Como compilar e executar? === | ||
+ | |||
+ | Tal como primeiro exemplo, o programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável. Neste caso, o módulo Animal.o não é utilizado e não aparece nos comandos: | ||
+ | |||
+ | gcc -c Cat.c -o Cat.o | ||
+ | gcc -c main.c -o main.o | ||
+ | |||
+ | A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando. | ||
+ | |||
+ | gcc -o main main.o Cat.o | ||
+ | |||
+ | Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima). | ||
+ | |||
+ | A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."): | ||
+ | |||
+ | ./main | ||
− | < | + | === Saída do programa === |
− | + | ||
− | </ | + | <source lang="text"> |
+ | == Cat == | ||
+ | Name: Tareco | ||
+ | Age: 12 | ||
+ | Weight: 3.4 | ||
+ | Purr level: 3 | ||
+ | Fluffiness: 3.1 | ||
+ | == Cat == | ||
+ | Name: Pantufa | ||
+ | Age: 1 | ||
+ | Weight: 12.3 | ||
+ | Purr level: 2 | ||
+ | Fluffiness: 2.7 | ||
+ | c1==c1? yes | ||
+ | c1==c2? no | ||
+ | </source> | ||
== Gato menos simples == | == Gato menos simples == | ||
Line 44: | Line 304: | ||
Como se pode observar acima, existe alguma repetição na definição de animal e de gato. Como o conceito de gato contém alguma informação do conceito de animal, seria conveniente a sua reutilização. | Como se pode observar acima, existe alguma repetição na definição de animal e de gato. Como o conceito de gato contém alguma informação do conceito de animal, seria conveniente a sua reutilização. | ||
− | A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão do conceito “gato”, mas considerando-o como uma animal caracterizado pelo volume do ronronar (_purrLevel) e pelo grau de suavidade do pêlo (_fluffiness). | + | A tarefa é a modelação e implementação em C (ficheiros '''.h''' e '''.c''') de uma estrutura de dados que represente uma versão do conceito “gato”, mas considerando-o como uma animal caracterizado pelo volume do ronronar ('''_purrLevel''') e pelo grau de suavidade do pêlo ('''_fluffiness'''). |
− | Tal como anteriormente, devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato. | + | Tal como anteriormente, devem ser ainda implementadas as funções '''newCat''' e '''destroyCat'''. A função '''newCat''' reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo '''_name''' tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função '''destroyCat''' liberta os recursos associados ao gato. |
Note-se que o facto de o gato ser um animal não altera a necessidade de implementação das funções associadas ao gato. | Note-se que o facto de o gato ser um animal não altera a necessidade de implementação das funções associadas ao gato. | ||
Assim, as funções a implementar são: | Assim, as funções a implementar são: | ||
− | * função de comparação – equalsCat –, por forma a considerar que dois gatos são iguais se as suas características forem iguais | + | * função de comparação – '''equalsCat''' –, por forma a considerar que dois gatos são iguais se as suas características forem iguais |
− | * funções de acesso às variáveis internas de um gato: getCatName, getCatAge, getCatWeight, getCatPurrLevel, getCatFluffiness (dado um gato, retornam um dos seus campos). Note-se que algumas destas características são fornecidas pelo conceito “animal” | + | * funções de acesso às variáveis internas de um gato: '''getCatName''', '''getCatAge''', '''getCatWeight''', '''getCatPurrLevel''', '''getCatFluffiness''' (dado um gato, retornam um dos seus campos). Note-se que algumas destas características são fornecidas pelo conceito “animal” |
− | * função printCat que, quando aplicada a um gato, apresenta os seus dados ( | + | * função '''printCat''' que, quando aplicada a um gato, apresenta os seus dados (usa-se '''printf''' para apresentar cada campo específico do gato) |
− | * programa – main – que ilustra a utilização das funções anteriores | + | * programa – '''main''' – que ilustra a utilização das funções anteriores |
+ | |||
+ | === Interface do conceito "Cat" === | ||
+ | |||
+ | Notar que a interface é necessariamente igual à do exemplo anterior, pois é característica do conceito "gato". | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''Cat.h'''| | ||
+ | <source lang="c" line> | ||
+ | #ifndef __CAT_H__ | ||
+ | #define __CAT_H__ | ||
+ | |||
+ | typedef struct cat *Cat; | ||
+ | |||
+ | Cat newCat(const char *name, int age, double weight, int purrLevel, | ||
+ | double fluffiness); | ||
+ | void destroyCat(Cat cat); | ||
+ | |||
+ | int equalsCat(Cat cat1, Cat cat2); | ||
+ | const char *getCatName(Cat cat); | ||
+ | int getCatAge(Cat cat); | ||
+ | double getCatWeight(Cat cat); | ||
+ | int getCatPurrLevel(Cat cat); | ||
+ | double getCatFluffiness(Cat cat); | ||
+ | void printCat(Cat cat); | ||
+ | |||
+ | #endif | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Implementação do conceito "Cat" === | ||
+ | |||
+ | Notar as partes onde é utilizado o conceito "animal". | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''Cat.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <string.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <stdio.h> | ||
+ | #include "Animal.h" | ||
+ | #include "Cat.h" | ||
+ | |||
+ | // note that, since a cat is an animal, part of the cat will | ||
+ | // be implemented as an animal. | ||
+ | struct cat { | ||
+ | Animal _animal; | ||
+ | int _purrLevel; | ||
+ | double _fluffiness; | ||
+ | }; | ||
+ | |||
+ | Cat newCat(const char *name, int age, double weight, int purrLevel, | ||
+ | double fluffiness) { | ||
+ | Cat cat = (Cat)malloc(sizeof(struct cat)); | ||
+ | if (cat != NULL) { | ||
+ | // use previously defined animal allocator | ||
+ | cat->_animal = newAnimal(name, age, weight); | ||
+ | if (cat->_animal == NULL) { | ||
+ | free(cat); | ||
+ | cat = NULL; | ||
+ | } | ||
+ | else { | ||
+ | cat->_purrLevel = purrLevel; | ||
+ | cat->_fluffiness = fluffiness; | ||
+ | } | ||
+ | } | ||
+ | return cat; | ||
+ | } | ||
+ | |||
+ | // destroying the cat has to undo its construction | ||
+ | void destroyCat(Cat cat) { | ||
+ | if (cat) { | ||
+ | // first, release the animal part | ||
+ | destroyAnimal(cat->_animal); | ||
+ | // then, release the rest of the cat | ||
+ | free(cat); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // these functions are implemented based on the animal versions | ||
+ | const char *getCatName (Cat cat) { return getAnimalName (cat->_animal); } | ||
+ | int getCatAge (Cat cat) { return getAnimalAge (cat->_animal); } | ||
+ | double getCatWeight (Cat cat) { return getAnimalWeight(cat->_animal); } | ||
+ | |||
+ | // these are cat-specific functions | ||
+ | int getCatPurrLevel (Cat cat) { return cat->_purrLevel; } | ||
+ | double getCatFluffiness(Cat cat) { return cat->_fluffiness; } | ||
+ | |||
+ | /* note that we require cat1 and cat2 to be valid cats: any of | ||
+ | them being NULL pointers will result in a false comparison. */ | ||
+ | int equalsCat(Cat cat1, Cat cat2) { | ||
+ | if (cat1 == NULL || cat2 == NULL) return 0; | ||
+ | return equalsAnimal(cat1->_animal, cat2->_animal) && | ||
+ | getCatPurrLevel(cat1) == getCatPurrLevel(cat2) && | ||
+ | getCatFluffiness(cat1) == getCatFluffiness(cat2); | ||
+ | } | ||
+ | |||
+ | // note that the output here is slightly different, because it uses the | ||
+ | // default animal implementation. | ||
+ | // we could have used the animal interface to obtain individual fields. | ||
+ | void printCat(Cat cat) { | ||
+ | printf("== Cat ==\n"); | ||
+ | printAnimal(cat->_animal); | ||
+ | printf("Purr level: %d\n", getCatPurrLevel(cat)); | ||
+ | printf("Fluffiness: %g\n", getCatFluffiness(cat)); | ||
+ | } | ||
+ | |||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Programa exemplo === | ||
+ | |||
+ | Notar que este programa é idêntico ao do caso anterior, uma vez que depende exclusivamente da interface do conceito "gato" (que não mudou) e não da sua implementação. | ||
+ | |||
+ | {{CollapsedCode|Ficheiro '''main.c'''| | ||
+ | <source lang="c" line> | ||
+ | #include <stdio.h> | ||
+ | #include "Cat.h" | ||
+ | |||
+ | int main() { | ||
+ | Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1); | ||
+ | Cat c2 = newCat("Piloto", 1, 12.3, 2, 2.7); | ||
+ | |||
+ | printCat(c1); | ||
+ | printCat(c2); | ||
+ | |||
+ | printf("c1==c1? %s\n", equalsCat(c1, c1) ? "yes" : "no"); | ||
+ | printf("c1==c2? %s\n", equalsCat(c1, c2) ? "yes" : "no"); | ||
+ | |||
+ | destroyCat(c1); | ||
+ | destroyCat(c2); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </source> | ||
+ | }} | ||
+ | |||
+ | === Como compilar e executar? === | ||
+ | |||
+ | Neste caso, o conceito de gato depende do conceito de animal inicialmente definido. Deste modo, o programa é constituído por três módulos a compilar separadamente (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável. | ||
+ | |||
+ | gcc -c Animal.c -o Animal.o | ||
+ | gcc -c Cat.c -o Cat.o | ||
+ | gcc -c main.c -o main.o | ||
+ | |||
+ | A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando. | ||
+ | |||
+ | gcc -o main main.o Cat.o Animal.o | ||
+ | |||
+ | Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima). | ||
+ | |||
+ | A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."): | ||
+ | |||
+ | ./main | ||
+ | |||
+ | === Saída do programa === | ||
− | < | + | Notar que a apresentação do conceito "gato" expõe a sua implementação como parte "animal". |
− | + | ||
− | </ | + | <source lang="text"> |
+ | == Cat == | ||
+ | == Animal == | ||
+ | Name: Tareco | ||
+ | Age: 12 | ||
+ | Weight: 3.4 | ||
+ | Purr level: 3 | ||
+ | Fluffiness: 3.1 | ||
+ | == Cat == | ||
+ | == Animal == | ||
+ | Name: Piloto | ||
+ | Age: 1 | ||
+ | Weight: 12.3 | ||
+ | Purr level: 2 | ||
+ | Fluffiness: 2.7 | ||
+ | c1==c1? yes | ||
+ | c1==c2? no | ||
+ | </source> | ||
== Discussão == | == Discussão == | ||
− | + | [[category:Ensino]] | |
− | + | [[category:PO]] | |
− | [[category: | + | [[category:PO Exemplos]] |
− | [[category: | + | [[category:C++]] |
Os exercícios seguintes ilustram a definição de tipos de dados abstractos e de suas instâncias. Estas instâncias são semelhantes aos objectos suportados por linguagens como o C++ ou o Java, mas, como estão implementados em C, algumas das operações têm de ser definidas explicitamente pelo programador.
Os exemplos tocam também no aspecto da reutilização de código: em linguagens OO, é algo que surge "naturalmente". Nestes exemplos, é necessário mais trabalho. Os exemplos são implementados em C++ no final (apenas para uma comparação mais directa com C: deixa-se como exercício a implementação em Java ou em outras linguagens de programação).
A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “animal”.
Um animal tem como características o nome (_name), a idade (_age) e o peso (_weight).
Devem ser ainda implementadas as funções newAnimal e destroyAnimal. A função newAnimal reserva memória suficiente para representar um animal e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assume-se que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyAnimal liberta os recursos associados ao animal.
Outras funções a implementar:
Ficheiro Animal.h |
---|
1 #ifndef __ANIMAL_H__
2 #define __ANIMAL_H__
3
4 // this typedef is used to hide the concept's implementation details
5 typedef struct animal *Animal;
6
7 Animal newAnimal(const char *name, int age, double weight);
8 void destroyAnimal(Animal animal);
9
10 int equalsAnimal(Animal animal1, Animal animal2);
11 const char *getAnimalName(Animal animal);
12 int getAnimalAge(Animal animal);
13 double getAnimalWeight(Animal animal);
14 void printAnimal(Animal animal);
15
16 #endif
|
Ficheiro Animal.c |
---|
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include "Animal.h"
5
6 struct animal {
7 char _name[16];
8 int _age;
9 double _weight;
10 };
11
12 Animal newAnimal(const char *name, int age, double weight) {
13 Animal animal = (Animal)malloc(sizeof(struct animal));
14 if (animal != NULL) {
15 strcpy(animal->_name, name);
16 animal->_age = age;
17 animal->_weight = weight;
18 }
19 return animal;
20 }
21
22 void destroyAnimal(Animal animal) {
23 if (animal)
24 free(animal);
25 }
26
27 const char *getAnimalName (Animal animal) { return animal->_name; }
28 int getAnimalAge (Animal animal) { return animal->_age; }
29 double getAnimalWeight(Animal animal) { return animal->_weight; }
30
31 /* note that we require animal1 and animal2 to be valid animals: any of
32 them being NULL pointers will result in a false comparison. */
33 int equalsAnimal(Animal animal1, Animal animal2) {
34 if (animal1 == NULL || animal2 == NULL) return 0;
35 return !strcmp(getAnimalName(animal1), getAnimalName(animal2)) &&
36 getAnimalAge(animal1) == getAnimalAge(animal2) &&
37 getAnimalWeight(animal1) == getAnimalWeight(animal2);
38 }
39
40 void printAnimal(Animal animal) {
41 printf("== Animal ==\n");
42 printf("Name: %s\n", getAnimalName(animal));
43 printf("Age: %d\n", getAnimalAge(animal));
44 printf("Weight: %g\n", getAnimalWeight(animal));
45 }
|
Ficheiro main.c |
---|
1 #include <stdio.h>
2 #include "Animal.h"
3
4 int main() {
5 Animal a1 = newAnimal("Tareco", 12, 3.4); // could be a cat...
6 Animal a2 = newAnimal("Piloto", 1, 12.3); // is it a dog??
7
8 printAnimal(a1);
9 printAnimal(a2);
10
11 printf("a1==a1? %s\n", equalsAnimal(a1, a1) ? "yes" : "no");
12 printf("a1==a2? %s\n", equalsAnimal(a1, a2) ? "yes" : "no");
13
14 destroyAnimal(a1);
15 destroyAnimal(a2);
16
17 return 0;
18 }
|
O programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável.
gcc -c Animal.c -o Animal.o gcc -c main.c -o main.o
A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.
gcc -o main main.o Animal.o
Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).
A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):
./main
A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “gato”.
Um gato tem como características o nome (_name), a idade (_age), o peso (_weight), o volume do ronronar (_purrLevel) e o grau de suavidade do pêlo (_fluffiness).
Devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato.
Outras funções a implementar:
Ficheiro Cat.h |
---|
1 #ifndef __CAT_H__
2 #define __CAT_H__
3
4 typedef struct cat *Cat;
5
6 Cat newCat(const char *name, int age, double weight, int purrLevel,
7 double fluffiness);
8 void destroyCat(Cat cat);
9
10 int equalsCat(Cat cat1, Cat cat2);
11 const char *getCatName(Cat cat);
12 int getCatAge(Cat cat);
13 double getCatWeight(Cat cat);
14 int getCatPurrLevel(Cat cat);
15 double getCatFluffiness(Cat cat);
16 void printCat(Cat cat);
17
18 #endif
|
Notar a repetição de muitas definições (relativamente a Animal).
Ficheiro Cat.c |
---|
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include "Cat.h"
5
6 struct cat {
7 char _name[16];
8 int _age;
9 double _weight;
10 int _purrLevel;
11 double _fluffiness;
12 };
13
14 Cat newCat(const char *name, int age, double weight, int purrLevel,
15 double fluffiness) {
16 Cat cat = (Cat)malloc(sizeof(struct cat));
17 if (cat != NULL) {
18 strcpy(cat->_name, name);
19 cat->_age = age;
20 cat->_weight = weight;
21 cat->_purrLevel = purrLevel;
22 cat->_fluffiness = fluffiness;
23 }
24 return cat;
25 }
26
27 void destroyCat(Cat cat) {
28 if (cat)
29 free(cat);
30 }
31
32 const char *getCatName (Cat cat) { return cat->_name; }
33 int getCatAge (Cat cat) { return cat->_age; }
34 double getCatWeight (Cat cat) { return cat->_weight; }
35 int getCatPurrLevel (Cat cat) { return cat->_purrLevel; }
36 double getCatFluffiness(Cat cat) { return cat->_fluffiness; }
37
38 /* note that we require cat1 and cat2 to be valid cats: any of
39 them being NULL pointers will result in a false comparison. */
40 int equalsCat(Cat cat1, Cat cat2) {
41 if (cat1 == NULL || cat2 == NULL) return 0;
42 return !strcmp(getCatName(cat1), getCatName(cat2)) &&
43 getCatAge(cat1) == getCatAge(cat2) &&
44 getCatWeight(cat1) == getCatWeight(cat2) &&
45 getCatPurrLevel(cat1) == getCatPurrLevel(cat2) &&
46 getCatFluffiness(cat1) == getCatFluffiness(cat2);
47 }
48
49 void printCat(Cat cat) {
50 printf("== Cat ==\n");
51 printf("Name: %s\n", getCatName(cat));
52 printf("Age: %d\n", getCatAge(cat));
53 printf("Weight: %g\n", getCatWeight(cat));
54 printf("Purr level: %d\n", getCatPurrLevel(cat));
55 printf("Fluffiness: %g\n", getCatFluffiness(cat));
56 }
|
Ficheiro main.c |
---|
1 #include <stdio.h>
2 #include "Cat.h"
3
4 int main() {
5 Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1);
6 Cat c2 = newCat("Pantufa", 1, 12.3, 2, 2.7);
7
8 printCat(c1);
9 printCat(c2);
10
11 printf("c1==c1? %s\n", equalsCat(c1, c1) ? "yes" : "no");
12 printf("c1==c2? %s\n", equalsCat(c1, c2) ? "yes" : "no");
13
14 destroyCat(c1);
15 destroyCat(c2);
16
17 return 0;
18 }
|
Tal como primeiro exemplo, o programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável. Neste caso, o módulo Animal.o não é utilizado e não aparece nos comandos:
gcc -c Cat.c -o Cat.o gcc -c main.c -o main.o
A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.
gcc -o main main.o Cat.o
Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).
A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):
./main
== Cat ==
Name: Tareco
Age: 12
Weight: 3.4
Purr level: 3
Fluffiness: 3.1
== Cat ==
Name: Pantufa
Age: 1
Weight: 12.3
Purr level: 2
Fluffiness: 2.7
c1==c1? yes
c1==c2? no
Como se pode observar acima, existe alguma repetição na definição de animal e de gato. Como o conceito de gato contém alguma informação do conceito de animal, seria conveniente a sua reutilização.
A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão do conceito “gato”, mas considerando-o como uma animal caracterizado pelo volume do ronronar (_purrLevel) e pelo grau de suavidade do pêlo (_fluffiness).
Tal como anteriormente, devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato.
Note-se que o facto de o gato ser um animal não altera a necessidade de implementação das funções associadas ao gato.
Assim, as funções a implementar são:
Notar que a interface é necessariamente igual à do exemplo anterior, pois é característica do conceito "gato".
Ficheiro Cat.h |
---|
1 #ifndef __CAT_H__
2 #define __CAT_H__
3
4 typedef struct cat *Cat;
5
6 Cat newCat(const char *name, int age, double weight, int purrLevel,
7 double fluffiness);
8 void destroyCat(Cat cat);
9
10 int equalsCat(Cat cat1, Cat cat2);
11 const char *getCatName(Cat cat);
12 int getCatAge(Cat cat);
13 double getCatWeight(Cat cat);
14 int getCatPurrLevel(Cat cat);
15 double getCatFluffiness(Cat cat);
16 void printCat(Cat cat);
17
18 #endif
|
Notar as partes onde é utilizado o conceito "animal".
Ficheiro Cat.c |
---|
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include "Animal.h"
5 #include "Cat.h"
6
7 // note that, since a cat is an animal, part of the cat will
8 // be implemented as an animal.
9 struct cat {
10 Animal _animal;
11 int _purrLevel;
12 double _fluffiness;
13 };
14
15 Cat newCat(const char *name, int age, double weight, int purrLevel,
16 double fluffiness) {
17 Cat cat = (Cat)malloc(sizeof(struct cat));
18 if (cat != NULL) {
19 // use previously defined animal allocator
20 cat->_animal = newAnimal(name, age, weight);
21 if (cat->_animal == NULL) {
22 free(cat);
23 cat = NULL;
24 }
25 else {
26 cat->_purrLevel = purrLevel;
27 cat->_fluffiness = fluffiness;
28 }
29 }
30 return cat;
31 }
32
33 // destroying the cat has to undo its construction
34 void destroyCat(Cat cat) {
35 if (cat) {
36 // first, release the animal part
37 destroyAnimal(cat->_animal);
38 // then, release the rest of the cat
39 free(cat);
40 }
41 }
42
43 // these functions are implemented based on the animal versions
44 const char *getCatName (Cat cat) { return getAnimalName (cat->_animal); }
45 int getCatAge (Cat cat) { return getAnimalAge (cat->_animal); }
46 double getCatWeight (Cat cat) { return getAnimalWeight(cat->_animal); }
47
48 // these are cat-specific functions
49 int getCatPurrLevel (Cat cat) { return cat->_purrLevel; }
50 double getCatFluffiness(Cat cat) { return cat->_fluffiness; }
51
52 /* note that we require cat1 and cat2 to be valid cats: any of
53 them being NULL pointers will result in a false comparison. */
54 int equalsCat(Cat cat1, Cat cat2) {
55 if (cat1 == NULL || cat2 == NULL) return 0;
56 return equalsAnimal(cat1->_animal, cat2->_animal) &&
57 getCatPurrLevel(cat1) == getCatPurrLevel(cat2) &&
58 getCatFluffiness(cat1) == getCatFluffiness(cat2);
59 }
60
61 // note that the output here is slightly different, because it uses the
62 // default animal implementation.
63 // we could have used the animal interface to obtain individual fields.
64 void printCat(Cat cat) {
65 printf("== Cat ==\n");
66 printAnimal(cat->_animal);
67 printf("Purr level: %d\n", getCatPurrLevel(cat));
68 printf("Fluffiness: %g\n", getCatFluffiness(cat));
69 }
|
Notar que este programa é idêntico ao do caso anterior, uma vez que depende exclusivamente da interface do conceito "gato" (que não mudou) e não da sua implementação.
Ficheiro main.c |
---|
1 #include <stdio.h>
2 #include "Cat.h"
3
4 int main() {
5 Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1);
6 Cat c2 = newCat("Piloto", 1, 12.3, 2, 2.7);
7
8 printCat(c1);
9 printCat(c2);
10
11 printf("c1==c1? %s\n", equalsCat(c1, c1) ? "yes" : "no");
12 printf("c1==c2? %s\n", equalsCat(c1, c2) ? "yes" : "no");
13
14 destroyCat(c1);
15 destroyCat(c2);
16
17 return 0;
18 }
|
Neste caso, o conceito de gato depende do conceito de animal inicialmente definido. Deste modo, o programa é constituído por três módulos a compilar separadamente (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável.
gcc -c Animal.c -o Animal.o gcc -c Cat.c -o Cat.o gcc -c main.c -o main.o
A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.
gcc -o main main.o Cat.o Animal.o
Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).
A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):
./main
Notar que a apresentação do conceito "gato" expõe a sua implementação como parte "animal".
== Cat ==
== Animal ==
Name: Tareco
Age: 12
Weight: 3.4
Purr level: 3
Fluffiness: 3.1
== Cat ==
== Animal ==
Name: Piloto
Age: 1
Weight: 12.3
Purr level: 2
Fluffiness: 2.7
c1==c1? yes
c1==c2? no