Buscar

05. Funções com Strings e Registros

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 (&)

Continue navegando