Baixe o app para aproveitar ainda mais
Prévia do material em texto
FUNÇÕES COM STRINGS E REGISTROS Estrutura de Dados I Strings As strings são vetores de caracteres com uma propriedade especial: O último caractere de toda string é o caractere nulo (escrito \0, ele é o caractere de código ASCII 0) Ambos exemplos são vetores de caracteres mas apenas o segundo é uma string char dog[5] = {'b','e','a','u','x'}; // não é string char cat[5] = {'f','a','t','s','\0'}; // string Strings A inicialização de uma string pode ser simplificada usando uma constante string Constantes string entre aspas duplas sempre incluem o \0 implicitamente char bird[10] = "Gaivota"; // caractere \0 está implícito char fish[] = "Sardinha"; // deixe o compilador contar B o z o \0 \0 \0 \0 0 1 2 3 4 5 6 7 char chefe[8] = "Bozo"; Caracteres '\0' são adicionados automaticamente Strings #include <iostream> #include <cstring> using namespace std; int main() { const int Tam = 15; char nome[Tam]; // vetor vazio char comp[Tam] = "C++omputador"; // vetor inicializado cout << "Olá! Eu sou o " << comp << "!"; cout << "Qual é seu nome?\n"; cin >> nome; cout << "Bem, " << nome << ", seu nome tem "; cout << strlen(nome) << " letras\ne está armazenado "; cout << "em um vetor de " << sizeof(nome) << " bytes.\n"; cout << "Sua inicial é " << nome[0] << ".\n"; comp[3] = '\0'; // caractere nulo cout << "Meu apelido é " << comp << endl; system("pause"); return 0; } Strings A saída do programa: A atribuição do caractere \0 para a quarta posição do vetor, encurtou a string, pelo menos para a função cout Olá! Eu sou o C++omputador! Qual é seu nome? Taylor Bem, Taylor, seu nome tem 6 letras e está armazenado em um vetor de 15 bytes. Sua inicial é T. Meu apelido é C++ Funções e Strings Uma string é um vetor de caracteres terminado pelo caractere nulo Muito do que foi aprendido sobre funções com vetores aplica-se ao uso de funções com strings Passar uma string como argumento significa passar o endereço do início da string Pode usar const para proteger um argumento string de uma modificação f u n c o e s \0 0 1 2 3 4 5 6 7 Funções e Strings Entretanto existem algumas diferenças no tratamento de strings Suponha que você deseja passar uma string como argumento de uma função, existem 3 possibilidades para representar a string: Um vetor de char Uma constante string entre aspas (string literal) Um ponteiro para char apontando para uma string Todas as opções são na verdade do tipo ponteiro para char (char *) Funções e Strings O protótipo de uma função que recebe uma string deve usar char * como tipo do parâmetro char fantasma[15] = "galopante"; char * str = "galatico"; int n1 = strlen(fantasma); // fantasma é &fantasma[0] int n2 = strlen(str); // ponteiro para char int n3 = strlen("gandulas"); // endereço da string int strlen(char *); // protótipo da função strlen Funções e Strings // Funções com parâmetros tipo string #include <iostream> using namespace std; int charEmString(const char * str, char ch); int main() { char espanto[15] = "uau!"; // string em vetor char * admira = "ulalalala!"; // admira aponta para string int nu = charEmString(espanto, 'u'); int na = charEmString(admira, 'a'); cout << nu << " caracteres u em " << espanto << "\n"; cout << na << " caracteres a em " << admira << "\n"; system("pause"); return 0; } Saída do programa: Funções e Strings int charEmString(const char * str, char ch) { int cont = 0; while (*str) // para quando str é '\0' { if (*str == ch) cont++; str++; // move ponteiro para o próximo char } return cont; } 2 caracteres u em uau! 4 caracteres a em ulalalala! Retorno de Strings Funções não retornam strings, elas podem retornar o endereço de strings Uma função nunca deve retornar o endereço de uma constante string ou de uma variável string definida dentro da própria função A memória para as variável locais é liberada ao final da execução da função char * inverteString(const char * str); Retorno de Strings // invstr.cpp -- método errado de retornar uma string #include <iostream> using namespace std; char * inverteString(const char * str); int main() { char nome[40]; cout << "Digite seu nome: "; cin >> nome; cout << "Seu nome invertido: "; cout << inverteString(nome) << endl; system("pause"); return 0; } Saída do programa: Retorno de Strings char * inverteString(const char * str) { char invertida[80]; invertida[strlen(str)] = '\0'; for (int i = 0, j = strlen(str)-1; i < strlen(str); i++, j--) invertida[j] = str[i]; return invertida; } Digite seu nome: taylor Seu nome invertido: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╚█╓\☺╠╠╠╠╠╠╠╠╠╠╠ Retorno de Strings Existem duas formas de retornar corretamente uma string de uma função: Retornando o endereço de uma string alocada dinamicamente com new Passando um parâmetro string adicional para ser modificado pela função char invertida[80]; // alocação estática char * invertida = new char[80]; // alocação dinâmica void inverteString(const char * str, char * invertida) Retorno de Strings Retornando o endereço de uma string alocada dinamicamente com new #include <iostream> using namespace std; char * inverteString(const char * str); int main() { char nome[40]; cout << "Digite seu nome: "; cin >> nome; char * pstr = inverteString(nome); cout << "Seu nome invertido: "; cout << pstr << endl; delete [] pstr; system("pause"); return 0; } Saída do programa: Retorno de Strings char * inverteString(const char * str) { char * invertida = new char[80]; invertida[strlen(str)] = '\0'; for (int i = 0, j = strlen(str)-1; i < strlen(str); i++, j--) invertida[j] = str[i]; return invertida; } Digite seu nome: taylor Seu nome invertido: rolyat Retorno de Strings Passando um parâmetro string adicional para ser modificado pela função #include <iostream> using namespace std; void inverteString(const char * str, char * inv); int main() { char nome[40], invertida[40]; cout << "Digite seu nome: "; cin >> nome; inverteString(nome, invertida); cout << "Seu nome invertido: "; cout << invertida << endl; system("pause"); return 0; } Saída do programa: Retorno de Strings void inverteString(const char * str, char * inv) { inv[strlen(str)] = '\0'; for (int i = 0, j = strlen(str)-1; i < strlen(str); i++, j--) inv[j] = str[i]; } Digite seu nome: taylor Seu nome invertido: rolyat Registros Suponha que você quer guardar informações sobre um jogador de futebol: Nome, salário, altura, peso, média de gols por partida, etc. É preciso de alguma forma guardar todas estas informações agrupadas Vetores não armazenam itens de tipos diferentes A solução: usar um registro Registros Um registro é um novo tipo de dado definido pelo programador: Para solucionar o problema anterior pode-se criar um tipo jogador, onde um jogador possui: Nome, altura, peso, salário, média de gols por partida e um time O registro agrupa diversas informações, de tipos possivelmente diferentes, sob um único identificador, que é um novo tipo de dado Registros Declaração de um registro: struct jogador { char nome[40]; float salario; int altura; }; Palavra chave struct Nome do registro Membros do Registro Finaliza a instrução de declaração Registros #include <iostream> using namespace std; struct jogador { char nome[40]; float salario; int altura; }; int main() { jogador a = {"Bebeto", 200000, 182}; jogador b = {"Romário",300000, 178}; cout << "Contratações para o próximo ano: " << a.nome << " e " << b.nome << "!\n"; cout << "Preço da aquisição: R$" << a.salario + b.salario << "!\n"; system("pause"); return 0; } Registros Saída do programa: Cada membro é tratado como uma variável do tipo definido no registro Contratações para o próximo ano: Bebeto e Romário! Preço da aquisição: R$500000! jogador bebeto; // declaração de variável bebeto // tipo jogador bebeto.nome // tipo string bebeto.salario // tipo float bebeto.altura // tipo int bebeto.nome[0] // tipo caractere Funções e Registros Quando se trata de funções, os registros se comportam como os tipos básicos da linguagem C++ O nome do registro é o nome de um novo tipo de dado que agrupa um conjunto de dados de tipos diferentes sob uma entidade única struct tempo { int horas; int mins; }; Funções e Registros Os registros são passados por valor e as funções trabalham com uma cópia dos dados Passar um registro por valor só faz sentido quando ele é relativamente pequeno tempo soma(tempo t1, tempo t2); ... soma(a,b); struct tempo { int horas; int mins; }; Funções e Registros // Usando registros com funções #include <iostream> using namespace std; struct tempo { int horas; int mins; }; const int MinsPorHora = 60; tempo somaTempo(tempo t1, tempo t2); void mostraTempo(tempo t); int main() { tempo dia1 = {5, 45}; tempo dia2 = {4, 55}; tempo viagem = somaTempo(dia1, dia2); cout << "Total de dois dias: "; mostraTempo(viagem); tempo dia3 = {4, 32}; cout << "Total de três dias"; mostraTempo(somaTempo(viagem, dia3)); system("pause"); return 0; } tempo somaTempo (tempo t1, tempo t2) { tempo total; total.mins = (t1.mins + t2.mins) % MinsPorHora; total.horas = t1.horas + t2.horas + (t1.mins + t2.mins) / MinsPorHora; return total; } void mostraTempo (tempo t) { cout << t.horas << " horas, " << t.mins << " minutos\n"; } Funções e Registros Saída do programa: Total de dois dias: 10 horas, 40 minutos Total de três dias: 15 horas, 12 minutos Funções e Registros Como registros tendem a guardar uma grande quantidade de informações, o ideal é passar o endereço do registro para a função Obtém-se o endereço de um registro usando o operador de endereço & tempo somaTempo(tempo * t1, tempo * t2); ... tempo a = {3, 40}; tempo b = {2, 10}; tempo c = somaTempo(&a,&b); mostraTempo(&c); Funções e Registros Se a função continua retornando um registro, continua-se com a cópia de uma grande quantidade de dados A solução é passar um terceiro argumento para ser modificado pela função void somaTempo(tempo * t1, tempo * t2, tempo * ret); ... tempo a = {3, 40}; tempo b = {2, 10}; tempo c; somaTempo(&a, &b, &c); mostraTempo(&c); Funções e Registros // Usando registros com funções #include <iostream> using namespace std; struct tempo { int horas; int mins; }; const int MinsPorHora = 60; void somaTempo(const tempo * t1, const tempo * t2, tempo * t3); void mostraTempo(const tempo * t); Funções e Registros int main() { tempo dia1 = {5, 45}; tempo dia2 = {4, 55}; tempo dois; somaTempo(&dia1, &dia2, &dois); cout << "Total de dois dias: "; mostraTempo(&dois); tempo dia3 = {4, 32}; tempo tres; somaTempo(&dois, &dia3, &tres) cout << "Total de três dias"; mostraTempo(&tres); system("pause"); return 0; } Saída do programa: void somaTempo (const tempo * t1, const tempo * t2, tempo * ret) { ret->mins = (t1->mins + t2->mins) % MinsPorHora; ret->horas = t1->horas + t2->horas + (t1->mins + t2->mins) / MinsPorHora; } void mostraTempo (const tempo * t) { cout << t->horas << " horas, " << t->mins << " minutos\n"; } Funções e Registros Total de dois dias: 10 horas, 40 minutos Total de três dias: 15 horas, 12 minutos Conclusão Ao passar strings para funções, manipula-se a cadeia de caracteres original e não uma cópia Para proteger a string contra alterações é necessário introduzir const na declaração do parâmetro Ao passar registros para funções, é feita uma cópia do registro Para evitar a cópia de um grande volume de dados é necessário passar o endereço do registro (&)
Compartilhar