Programar em C/Estruturas

Origem: Wikilivros, livros abertos por um mundo aberto.

Recycle001.svg Esta página precisa ser reciclada (discuta).
Ao melhorá-la, você estará ajudando o Wikilivros.


Tabela de conteúdo

[editar] Structures

As stuctures permitem com que possamos ter variáveis de várias tipologias aglomeradas sob o mesmo nome. E esse mesmo nome vai passar a ser uma nova tipologia de dados tal como o int ou float.

Mas o uso disto é que podemos ter valores que tenham alguma relação lógica, por exemplo guardar um int de idade e um string de nome. Isto pode ser atributos de uma pessoa. Ou seja podemos empacotar várias variáveis de várias tipologias com o objectivo de representar o mundo real e dar um nome a essas variáveis todas.

Ao fazer isto criámos uma tipologia de dados da mesma forma como fazemos em relação ao int ou ao float.


[editar] Declarar uma estrutura

A sintaxe é:

struct <identificador> {
  <tipo>  campo_um   ; 
  <tipo>  campo_dois ;
};

Aqui o tipo struct indica que vamos criar uma estrutura. O nome ou identificador pode ser alunos,familia,etc . (têm de ser válidos identifiers) Não esquecer o ponto e vírgula “;” no fim da declaração. Campo_um e Campo_dois são variáveis membro – member variables – ou campo da estrutura. Podemos ter também funções membro. Estamos a criar novas tipologias de dados. Primeiro Método :

struct minha_estrutura {
  int  variavel_um  ;
  int  campo_dois   ;
  char  fruta[40]   ;
.} ;

Aqui o identificador do tipo "struct" é "minha_estrutura" dentro dessa estrutura temos três campos o ultimo é "fruta"
Agora podemos usar esse tipo "struct" para definir variáveis.

struct minha_estrutura nova_estructura ;

Para ter acesso aos membros definidos dentro da estrutura utilizamos um operador de seleçao de membro "."(um ponto).

 nova_estrutura.fruta[0]  ;

Nos da o primeiro caracter da palavra contida dentro do membro "fruta".

[editar] Matriz (uma maneira de ver a coisa)

A maneira mais fácil para que eu costumo fazer para entender as structures é fazer a associação com uma matriz.

Person String Name int height
p1
p2
p[20]

Então temos as seguintes situações:

  • Criar colunas – queremos criar variáveis e funções membro
  • Criar linhas – queremos criar instancias. (dentro e fora da declaração da estrutura)
  • Definir funções membro fora da declaração da estrutura
  • Atribuir/ler valores ás células
  • Acrescentar colunas (após primeira iniciação)
  • Acrescentar linhas (após primeira iniciação)



[editar] Declarar instâncias (objectos) da estrutura

Podemos declarar os objectos ao de duas formas:

  • Ao mesmo tempo que declaramos a estrutura
struct product {
 int weight;
 float price;
} apple, banana, melon;
  • Ou como uma variável normal
struct product
{
..
}
int main()
{
product apple, banana, melon;
}

E até podemos declarar um array delas

Person p[20];

Pergunta: como é que é feito exactamente os objectos Para cada objecto vão ser feito uma cópia dos elementos da estrutura. Agora isso significa que os objectos são distintos entre si em termos de reserva de memória? ie, á medida que enumero os objectos vão ser reservado para cada objecto o tamanho x de bytes? ou somam-se todos os objectos e reserva-se para todos os objectos de uma forma seguida? Penso que deve ser a 1ª opção.

Se tivermos apenas um objecto (ou variável da estrutura) não é necessário darmos o nome da estrutura

struct {
char item[40]; // name of item
double cost; // cost
double retail; // retail price
int on_hand; // amount on hand
int lead_time; // number of days before resupply
} temp;

Pergunta: neste caso como é que chamamos o objecto fora da declaração da estrutura?

Pergunta: como objectos podemos ter o quê? os objectos são como que constantes e não tanto como variáveis, ie, apenas estamos a chamar um nome a valores de variáveis. por isso faz sentido poderemos ter arrays, afinal eles são apenas uma forma de criar vários nomes. e ponteiros? ter ponteiros apontar para onde? para outro objecto. sim pode fazer sentido. e funções? não isto não me parece que faça sentido. e estruturas?



[editar] Aceder as variáveis membro das estruturas

Agora queremos dar valores a cada uma das pessoas, queremos dar o nome e a altura, para isso faríamos;

p1.name = “Tiago”
p1.name =1,9

Se quiséssemos imprimir o valor guardado

cout << "The name of p1 is " << p1.name;

Um erro usual é fazer

Person.name =”Tiago” 		//não funciona

A forma genérica é:

structure-varname.member-name

ou seja é o

[objecto_estrutura][member_estrutura]


Exemplo

#include <iostream>
#include <string>
using namespace std;
 
const int MAX = 3;
 
struct Person 
      {
       string name; 
       int height;
      };
 
int main ()
{
  Person p[MAX];
  for (int x = 0; x < MAX; x++)
  {
     cout << "Enter person's name: ";
     getline(cin, p[x].name);
     cout << "Enter height in meters: ";
     cin >> p[x].height;
     cin.ignore();      
  }
  cout << "Outputting person data\n";
  cout << "======================\n";
  for (x = 0; x < MAX; x++)
      cout << "Person #" << x + 1 << "'s name is "
        << p[x].name << " and height is "
        << p[x].height << endl;
  system (“pause”);
  return 0;
}


[editar] Iniciar uma estrutura

Podemos iniciar uma estrutura de 2 maneiras:

  • Usando uma lista de iniciação

esta seria algo:

Person p1 = {"Jeff Kent", 72};

isto basicamente é igual a arrays, apenas com a diferença de termos tipologias diferentes. Logo a ordem vai interessar, por exemplo se escrevêssemos

Person p1 = {72", Jeff Kent"};  //não iria funcionar- erro de compilação
  • Usando construtores

Os construtores são funções que são criadas automaticamente sempre que tentamos criar um objecto. A esses que são criados automaticamente são os chamados de defaut. Se escrevermos o código

#include <iostream>
#include <string>
using namespace std;
const int MAX = 3;
 
struct Person 
{
  string name; 
  int height;
};
 
int main ()
{
  Person p1;
  cout << "The person's name is " << p1.name << " and height is " << p1.height << endl;
  system (“pause”);   
  return 0;
}

O resultado é The person's name is and height is -858993460

Aqui é criado um defaut constructor no momento em que criamos a instancia p1 ie com a linha Person p1;

Como as variáveis membro não foram iniciadas, o valor de name está vazio e o na variável height está um valor qualquer – que é lixo!

[editar] Constructor sem argumentos

Podemos ter um constructor sem argumentos que ao contrário do defaut constructor designa valores defaut ás variáveis membro.

struct Person 
{
  string name; 
  int height;
  Person()		//constructor sem argumentos
  {
     name = "No name assigned";
     height = -1;
  }
};
  • O nome do constructor é sempre igual ao nome da estrutura, sem excepção.
  • O constructor não retorna qualquer valor, sem excepção

Refazendo o nosso exemplo

#include <iostream>
#include <string>
using namespace std;
const int MAX = 3;
struct Person {
   string name;
   int height;
   Person()
   {
          name = "No name assigned";
          height = -1;
   }
};
int main ()
{
   Person p1; 
   cout << "The person's name is "<< p1.name << " and height is " << p1.height << endl;
   system (“pause”);
   return 0;
}  

Repare que demos valores defaut ás variáveis. Agora não estamos no caso de ter p1.name=??? Por mais instâncias que criemos eles vão ter sempre valores padrão.


[editar] Constructor com argumentos

Termos um constructor sem argumentos é um melhoramento face ao defaut constructor pois agora temos valores defaut para as variáveis membro. Porém seria melhor se conseguíssemos inicializar as variáveis membro com valores dados pelo utilizador enquanto o programa estivesse e a correr. E realmente podemos fazer se passarmos argumentos.

#include <iostream>
#include <string>
using namespace std;
const int MAX = 3;
struct Person 
   {
   string name;
   int height;
   Person()	//constructor sem argumentos
      {
      name = "No name assigned";
      height = -1;
      }
   Person(string s, int h)  //constructor com 2 argumentos
   {
      name = s;
      height = h;
   }
};  
 
int main ()
{
  int metro;
  string strName;
  cout << "Entre com o nome da pessoa: ";
  getline(cin, strName);
  cout << "Enter height in metro: ";
  cin >> metro;
  cin.ignore();   
  Person p1(strName,metro); 
  cout << "The person's name is " << p1.name << " and height is " << p1.height << endl;
  system (“pause”);
  return 0;
}

Repare que os argumentos do construtor têm de estar na ordem esperada


[editar] Separar o constructor prototype da implementação

#include <iostream>
#include <string>
using namespace std;
 
const int MAX = 3;
 
struct Person {
  string name;
  int height;
  Person();		//constructor sem argumento
  Person(string, int);	//constructor com dois parametros, apenas é necessário dizer a tipologia dos parâmetros – o nome não é necessário)
};
 
Person::Person()
  {
     name = "No name assigned";
     height = -1;
  }
  
 Person::Person(string s, int h)
  {
     name = s;
     height = h;
  }
 
int main ()
{
  int metro;
  string strName;
  cout << "Enter person's name: ";
  getline(cin, strName);
  cout << "Enter height in inches: ";
  cin >> metro;
  cin.ignore();   
  Person p1(strName, inches); 
  cout << "The person's name is " << p1.name << " and height is " << p1.height << endl;
  system (“pause”);
  return 0;

}

  • Vamos ver a função main(): declarámos 2 variáveis uma int e outra string. Pedimos para a pessoa escrever o nome e colocámos o valor na variável string, depois pedimos a altura e colocámos na variável int. Depois chamámos o constructor com dois argumentos e passamos as variáveis anteriores como argumentos. Por fim mandámos imprimir no ecrã os valores das variáveis membro da estrutura.
  • Repare que para definirmos fora o construtor recorremos ao operador scope ::
Person::Person()
Person::Person(string s, int h)
  • Repare que no prototype dos constructor apenas tivemos de dizer a tipologia dos parâmetros



[editar] Estruturas aninhadas

A ideia é ter uma estrutura dentro de outra estrutura.

#include <iostream>
#include <string>
using namespace std;
struct Date		//estrutura chamada de date
{
  int month;
  int day;
  int year;
};
struct Person 
  {
  string name; 
  int height;
  Date bDay;	  //temos uma nova variável, mas notem a tipologia
  };
 
void setValues(Person&);
void getValues(const Person&);
 
int main ()
{
  Person p1;
  setValues(p1);  
  cout << "Outputting person data\n";
  cout << "======================\n";
  getValues(p1);
  system (“pause”);
  return 0;
}
 
void setValues(Person& pers)
{
  cout << "Enter person's name: ";
  getline(cin, pers.name);
  cout << "Enter height in inches: ";
  cin >> pers.height; 
  cin.ignore();
  cout << "Enter month, day and year of birthday separated by spaces: "
  cin >> pers.bDay.month >> pers.bDay.day >> pers.bDay.year;
  cin.ignore();
}
 
void getValues(const Person& pers)
{
  cout << "Person's name: " << pers.name << endl; 
  cout << "Person's height in inches is: " << pers.height << endl;
  cout << "Person's birthday in mm/dd/yyyy format is: " 
     << pers.bDay.month << "/" << pers.bDay.day 
     << "/" << pers.bDay.year << endl;
}

Reparem que a estrutura Date tem de ser declarada antes da estrutura Person, pois caso contrário o compilador não entendia a tipologia declarada na estrutura Person.

Agora para termos os valores da estruturura é

cin >> pers.bDay.month >> pers.bDay.day >> pers.bDay.year;

[editar] Passando estruturas com argumento do funções

#include <iostream>
#include <string>
using namespace std;
 
struct Person 
  {
  string name; 
  int height;
  };
 
void setValues(Person&);
void getValues(const Person&);
 
int main ()
{
  Person p1;
  setValues(p1);  
  cout << "Outputting person data\n";
  cout << "======================\n";
  getValues(p1);
  system (“pause”);
  return 0;
}
 
void setValues(Person& pers)
{
  cout << "Enter person's name: ";
  getline(cin, pers.name);
  cout << "Enter height in inches: ";
  cin >> pers.height; 
  cin.ignore();
}
 
void getValues(const Person& pers)
{
  cout << "Person's name is " << pers.name << " and height is " << pers.height << endl;  
}


A estrutura é passada por referência ou por address


[editar] Ponteiros para estruturas

struct movies_t 
{
 string title;
 int year;
};
 
movies_t amovie;
movies_t * pmovie;

Nós criámos algo

movies_t	title	year
amovie		
* pmovie		

Vejamos que temos um ponteiro como instância.

// pointers to structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
 
struct movies_t 
{
 string title;
 int year;
};
 
int main ()
{
 string mystr;
 movies_t amovie;
 movies_t *pmovie;
 pmovie = &amovie;            //atribuímos valor ao ponteiro 
 
 cout << "Enter title: ";
 getline (cin, pmovie->title);            //operador ->
 cout << "Enter year: ";
 getline (cin, mystr);
 (stringstream) mystr >> pmovie->year;       //operador ->
 
 cout << "\nYou have entered:\n";
 cout << pmovie->title;                    //operador ->
 cout << " (" << pmovie->year << ")\n";    //operador ->
 system (“pause”);
 return 0;
}

Como já devem ter deduzido o operador -> será muito similar a

pmovie->title  	é equivalente a 	(*pmovie).title


Mas olhem que é diferente a:

*pmovie.title	 	que  equivalente a  	*(pmovie.title)

Ah estas subtilezas!!!

Pergunta: porque será que só podemos acrescentar objectos e não também membros fora das structs. ou seja podemos fazer adições na matriz structs nas linhas mas não nas colunas.


Pergunta: será que em vez de termos apenas variáveis nas estruturas podíamos ter também funções, ou melhor instruções?


Para declarar uma estrutura:

  1. Apenas nos basta criar as colunas que são os members.
  2. necessitamos da palavra struct que indica ao compilador que vamos criar uma estrutura
  3. necessitamos de definir alguns members (variáveis ou funções)

Para definir os objectos

  1. podemos fazer internamente, ao mesmo tempo que a declaração da estrutura
  2. ou podemos fazer fora da declaração, fazendo como se fosse uma outra variavel normal.

Para definir os members

  1. podemos defini-los ao mesmo tempo que declaramos a estrutura
  2. ou podemos defini-los externamente, recorrendo ao operador ”::”


Para alocar valores nos objectos -member variáveis

  1. fazemos fora da declaração.

uma vez que os objectos só são definidos dentro da estructura no final.


Pergunta: faz sentido ter funções membro para cada objecto? não. para isso criamos novas estruturas. só iria complicar.

Pergunta: faz sentido podemos declarar variáveis member fora da declaração da estrutura? sim faz, mas traz o problema de termos de andar atrás dessas variáveis depois no código.