Ir para o conteúdo
PsySc0rpi0n

Ficheiros .c e .h

Mensagens Recomendadas

PsySc0rpi0n

Boas...

Há muito que aqui não vinha... Mas de vez em quando a gente precisa e cá nos vemos!

Este último ano não tive muito tempo para fazer o que quer que fosse em C, quer por brincadeira (hobby, aprender, evoluir), quer por necessidade (trabalhos).

No entanto estes últimos dias voltei a tentar entender como estruturar um programa, dividindo o código por vários files. E é sobre isto que gostava de obter algumas respostas, sugstões, regras ou as chamadas thumbs-up rules, para estruturar um programa de forma correcta,s e assim se pode dizer.

Indo directamente para o que me aqui trouxe, imaginemos que eu estou a fazer um programa que usa várias funções para lidar com algarismos e com caractéres alfabéticos, separadamente. Algo como ter isto, hipoteticamente falando:

- funções numéricas -

função1

função2

função3

estas funções lidam apenas com números

- funções alfabéticas -

função_a

função_b

função_c

estas lidam apenas com caractéres alfabéticos.

depois terei algumas macros (defines) afectas a ambos tipos de funções, numéricas e alfabéticas. E poderei eventualmente ter algum código para gerir erros esperados também afectos a cada um dos tipos de funções.

Em termos de ficheiros .c e .h, o que iria colocar em cada um, na vossa perspectiva? Haveria algum tipo de regras/thumbs-up rules que nos pudessem ajudar a decidir o que colocar em cada file? Como se estruturaria um programa deste género?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

a estruturação de uma aplicação segue somente uma regra:

- o mesmo código não pode ser "linkado" mais do que uma vez.

essa é a única regra que existe. todo o resto é bom senso ...

o que quero dizer é que deverás separar o código pelos ficheiros .c de forma que faça sentido.

os ficheiros .h servem só para colocar as declarações (e as macros) que pretendes tornar "públicas" para que outros ficheiros .c possam "saber" que existe.


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok, vamos tentar concretizar para ver se eu consigo perceber uma forma lógica de compreender isto!

Vamos imaginar que eu quero fazer um programa que precisa de várias funções para 2 ou 3 situações específicas.

Por exemplo, quero fazer um programa que leia uma string e/ou um número.

Consoante o que foi inserido, string ou número, vou ter a possibilidade de:

string:

contar os caractéres da string inserida (função_a)

inverter a string (função_b)

ordenar ascendente (função_c)

ordenar descendente (função_d)

número

apresentar o quadrado do valor inserido (função_1)

apresentar o factorial do valor inserido (função_2)

apresentar o dobro do valor inserido (função_3)

apresentar metade do valor inserido (função_4)

O meu pensamento inicial seria criar um file main.c e um main.h, um file string.c e um string.h e um numero.c e um numero.h...

Então no file main.c ficariam as routinas de pedir ao user os números e/ou strings, apresentar resultados e o include para o file main.h.

No file main.h ficariam os cabeçalhos das funções usadas no file main.c.

No file string.c ficariam as funções 'a', 'b', 'c' e 'd' e o include do file string.h

No file string.h ficariam os cabeçalhos das funções usadas no file string.c e os includes necessários.

e o mesmo para os files numbers.c e numbers.h.

Eu comecei a tentar fazer isto mas chego sempre a um impasse porque dou comigo sempre a precisar de várias coisas em vários sítios e nunca consigo chegar ao fim...

Vou colocar aqui o código que tenho e os files que tenho, embora isto ainda não esteja a funcionar nem sequer está a meio. Mas só assim consigo explicar as dificuldades porque estou a passar para estruturar isto de forma simples!

Para já o programa apenas declara uma variável 'char', aloca memória para ela, pede uma string e inverte-a. Mas já detectei vários provlemas, não a nível de código, mas relacionados com a separação dos conteúdos pelos files, nomeadamaente em relação à alocação de memória e à libertação da mesma porque tenho a função para alocar memória no file main.c mas preciso desta função no file string.c também e assim começa já a haver mistura de conteúdos e depois acabo com includes de files .h onde não estava programado esses includes estarem.

Mas só vendo como tenho as coisas, acho eu!

/* -- main.c --*/
#include "main.h"

char* alloc_mem (void){
char *mem_block;
if ((mem_block = malloc (BUFFER * sizeof (char))) == NULL){
perror ("Memory error!");
exit (0);
}
return mem_block;
}

int main (int argc, char** argv){
char* string = NULL;

string = alloc_mem ();
fgets (string, BUFFER, stdin);
if (string [strlen (string) - 1] == '\n')
string [strlen (string) - 1] = '\0';
printf ("The string is: %s\n", string);
string = invert_string (string);
printf ("The inverted string is: %s\n", string);
free (string);
return 0;
}

/* -- main.h --*/
#include 
#include 
#include 

char* alloc_mem (void);

/* -- string.c -- */
#include "str_control.h"

char* invert_string (char* string){
char *tmp_str = NULL;
short int len = 0, i = 0;

len = strlen (string);
tmp_str = alloc_mem ();

for (i = len; i >= 0; i--)
tmp_str [len - i] = string [i - 1];

tmp_str [len + 1] = '\0';
return tmp_str;
}

char* up_sort (char* string){
char* tmp_string = NULL;

tmp_string = alloc_mem ();
}

/* -- string.h --*/
#include 
#include 
#include 


#define BUFFER 256

char* invert_string (char* string);
char* up_sort (char* string);
char* down_sort (char* string);

Com isto assim estruturado não vai dar nada de jeito. Proque a função alloc_mem vai ser precisa no main.c e no string.c e depois não sei se posso ficar com isto assim porque não sei ao certo onde posse/devo usar o free (variavel) para libertar a memória!

Editado por pwseo
removidas tags spoiler (desnecessárias)

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

Ok, vamos tentar concretizar para ver se eu consigo perceber uma forma lógica de compreender isto!

Vamos imaginar que eu quero fazer um programa que precisa de várias funções para 2 ou 3 situações específicas.

Por exemplo, quero fazer um programa que leia uma string e/ou um número.

Consoante o que foi inserido, string ou número, vou ter a possibilidade de:

string:

contar os caractéres da string inserida (função_a)

inverter a string (função_b)

ordenar ascendente (função_c)

ordenar descendente (função_d)

número

apresentar o quadrado do valor inserido (função_1)

apresentar o factorial do valor inserido (função_2)

apresentar o dobro do valor inserido (função_3)

apresentar metade do valor inserido (função_4)

não é normal o ficheiro main.c ter associado um ficheiro main.h, isto porque nesse ficheiro (main.c) normalmanete só existe a função : main

todo o resto é padrão ser separado por ficheiros de forma congruente. isto é, funções que estão relacionadas de uma forma lógica, deverão estar associadas num ficheiro .c, e "apresentadas" no seu respectivo .h.

deste modo, olhando para a esplicação que tens, o que eu teria seria:

main.c

int main(int argc, char** argv) {
   // do some magic ...
}

texto.h

#pragma once

int   func_text_a(char* str);
char* func_text_b(char* str);
char* func_text_c(char* str);
char* func_text_d(char* str);

texto.c

#include "texto.h"

int func_text_a(char* str)
{
   // do some magic ...
}

char* func_text_b(char* str)
{
   // do some magic ...
}

char* func_text_c(char* str)
{
   // do some magic ...
}

char* func_text_d(char* str)
{
   // do some magic ...
}

texto.h

#pragma once

int func_num_a(int val);
int func_num_b(int val);
int func_num_c(int val);
int func_num_d(int val);

texto.c

#include "texto.h"

int func_num_a(int val)
{
   // do some magic ...
}

int func_num_b(int val)
{
   // do some magic ...
}

int func_num_c(int val)
{
   // do some magic ...
}

int func_num_d(int val)
{
   // do some magic ...
}

se pretendes ter uma função de alocação de memória com "gestão de erros", então terás mais um par de ficheiros:

mem.h

#pragma once

#include <stdlib.h>

#define MEM_ERROR_OK            0
#define MEM_ERROR_NO_MEMORY     -1

void* mem_alloc(const size_t size);

int   mem_get_error(void);
int   mem_clear_error(void);

mem.c

int mem_error = MEM_ERROR_OK;

void* mem_alloc(const size_t size)
{
   void* result;
   if ((result = malloc(size)) == NULL)
   {
       mem_error = MEM_ERROR_NO_MEMORY;
   }

   return result;
}

int mem_get_error(void)
{
   return mem_error;
}

int mem_clear_error(void)
{
   return mem_error = MEM_ERROR_OK;
}


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok ,mas então nos files text.c e num.c tenho que ter os includes dos respectivos files '.h' e também o include do file mem.h, certo?

E quando usar a função para alocar memória para as variáveis necessárias em text.c e num.c, onde faço os free (var...)??? Não pode ser nem no final da função mem_alloc por causa do return nem antes do return senão perco o espaço alocado, certo?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

Ok ,mas então nos files text.c e num.c tenho que ter os includes dos respectivos files '.h' e também o include do file mem.h, certo?

sim

E quando usar a função para alocar memória para as variáveis necessárias em text.c e num.c, onde faço os free (var...)??? Não pode ser nem no final da função mem_alloc por causa do return nem antes do return senão perco o espaço alocado, certo?

com em toda a memória alocada, fazes o free quando já não necessitas dela


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

sim

E outra questão...

Se eu precisar de funções do file string.c no file main.c, não tenho que fazer o include do file.h no file main.c???

com em toda a memória alocada, fazes o free quando já não necessitas dela

Mas posso fazer free de uma variável que está dentro da func_a dentro da função main???? Não pois não? Ou seja, o conteúdo da variável perde-se quando a função termina mas a memória alocada continua alocada, certo? Por isso estou com esta dúvida!

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

Se eu precisar de funções do file string.c no file main.c, não tenho que fazer o include do file.h no file main.c???

se necessitas das funções de um ficheiro, então terás de fazer include do ficheiro header (.h) que tem a alusão dessas funções

Mas posso fazer free de uma variável que está dentro da func_a dentro da função main???? Não pois não? Ou seja, o conteúdo da variável perde-se quando a função termina mas a memória alocada continua alocada, certo? Por isso estou com esta dúvida!

podes fazer a libertação de uma região de memória alocada em qualquer lugar do código, desde que o ponteiro dado como argumento da função "free" seja correcto


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Pois... Então acho que no meu caso terei que usar ponteiros para ponteiros, no caso das strings, quando usar a função alloc_mem, em vez de usar a sugestão que o pwseo me deu de retornar o bloco de memória alocado na função alloc_mem.

Ou seja, partindo do princípio que eu tenho um file nem.c e um mem.h, e que vou precisar das funções destes files no file string.c e main.c, quando eu precisar de fazer algo com uma string, eu vou precisar de alocar memória a partir do file string.c e para isso vou ter que usar um ponteiro para a string para depois poder ligar a libertar a memória alocada para a string, certo? Ou tenho outra alternativa?

Exemplo

main.c

#include "string.h"
#include "mem.h"

int main (){
char* string = NULL;
fgets (string,...)
func_a (string); //do file string.c
free (string) ;
} 

string.c

#include "string.h"
#include "mem.h"

char* func_a (char* string){
   alloc_mem (string);
   ... 
   return string;
}

mem.c

#include" mem.h"

char** alloc_mem (char** mem_block){
   if (mem_block=malloc (size)
       ... 
}

Será algo deste género?

Editado por thoga31
Tags code + GeSHi

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok, estou a tentar fazer como disseste @HappyHippyHippo mas estou a ter algumas dificuldades. Primeiro estou a ter um erro que diz:

*** Error in `./string': double free or corruption (top): 0x00000000009be120 ***

Aborted

Nota: ainda não resolvi como fazer o free das diversas possíveis alocações de memória!

E depois não estou a ver bem como se usam as funções

int mem_get_error(void)
{
return mem_error;
}
int mem_clear_error(void)
{
return mem_error = MEM_ERROR_OK;
}

Acho que já percebi a parte das duas funções do mem.c... São para usar caso eu precise em qualquer altura se houve algum erro de alocação de memória, certo?

Agora vou aqui colocar como tenho o código e que está a funcionar, excepto aquele erro do free duplicado!

main.c

#include
#include "text.h"
#include "mem.h"

int main (int argc, char** argv){
char* string = NULL;

string = mem_alloc (BUFFER * sizeof (char));
fgets (string, BUFFER, stdin);
if (string [strlen (string) - 1] == '\n')
string [strlen (string) - 1] = '\0';
printf ("The string is: %s\n", string);
string = invert_string (string);
printf ("The inverted string is: %s\n", string);
free (string);
return 0;
}

text.c

#include
#include "text.h"
#include "mem.h"

/*****Invert String*****/
char* invert_string (char* string){
char *tmp_str = NULL;
short int len = 0, i = 0;

len = strlen (string);
tmp_str = mem_alloc (BUFFER * sizeof (char));

for (i = len; i >= 0; i--)
tmp_str [len - i] = string [i - 1];

tmp_str [len + 1] = '\0';
string = tmp_str;
free (tmp_str);
return string;
}

text.h

#pragma once

#include

#define BUFFER 256

char* invert_string (char* string);
char* up_sort (char* string);
char* down_sort (char* string);
char* copy_string (char* string);

mem.c

#include "mem.h"

/*****Memory Control*****/
int mem_error = MEM_ERROR_OK;

void* mem_alloc (const size_t size){
void* result;
if ((result = malloc(size)) == NULL){
mem_error = MEM_ERROR_NO_MEMORY;
}
return result;
}

int mem_get_error(void){
return mem_error;
}

int mem_clear_error(void){
return mem_error = MEM_ERROR_OK;
}

mem.h

#pragma once

#include

#define MEM_ERROR_OK 0
#define MEM_ERROR_NO_MEMORY -1

void* mem_alloc(const size_t size);

int mem_get_error(void);
int mem_clear_error(void);

Preciso de perceber onde colocar os free (var) para libertar a memória correctamente!

Neste momento estou a ter aquele erro de free duplicado...

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

o que achas que estás a fazer no seguinte código ?

char* invert_string (char* string){
 // ...
 string = tmp_str;
 free (tmp_str);
 return string;
}


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

[Acho que] estou a tentar fazer o ponteiro 'string' apontar para o mesmo bloco de memória para onde aponta o ponteiro 'tmp_str'... Era uma tentativa de copiar o conteúdo da variável "tmp_str" para "string" para depois poder libertar a memória da variável tmp_str... e depois então libertar a memória alocada para a "tmp_str"!

Mas agora que respondi à pergunta, acho que me surgiu outra dúvida...

No entanto acho que já percebi o que está a acontecer no caso do free duplicado.

Como coloquei "string" a apontar para o mesmo bloco para onde aponta "tmp_str", e depois faço free (tmp_str) e depois free (string), estou no fundo a libertar o mesmo bloco de memória duas vezes!

Agora a outra dúvida que me surgiu é se tenho as duas variáveis a apontar para um determinado bloco de memória e a certa altura faço o free desse bloco de memória, como é que eu consigo obter na mesma o resultado esperado?

Ou seja, quando fazemos free (variável) é o mesmo que estarmos a limpar o conteúdo desse bloco de memória, não? Ou o free não é assim que trabalha?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

Mas agora que respondi à pergunta, acho que me surgiu outra dúvida...

Era esse o propósito da questão

Agora a outra dúvida que me surgiu é se tenho as duas variáveis a apontar para um determinado bloco de memória e a certa altura faço o free desse bloco de memória, como é que eu consigo obter na mesma o resultado esperado?

obviamente o código está errado, mas não quer dizer que o método está mal.

o teu pretendido era "copiar" uma string para outra e não alterar o ponteiro para a região de memória que guarda a string

a outra solução é "trocar" os ponteiros de memória, isto porque:

- em "tmp_str" ficarás com o ponteiro para a região de memória previamente apontado por "string"

- em "string" ficarás com o ponteiro para a região de memória com a string alterada

- a região de memória libertada com a chamada free(tmp_str) já não é a mesma que a da variável "string" e contem a string original (a que já não interessa)

- a função retorna o ponteiro para a região de memória que interessa, logo é possível usar pela função "chamadora" desta.

Ou seja, quando fazemos free (variável) é o mesmo que estarmos a limpar o conteúdo desse bloco de memória, não? Ou o free não é assim que trabalha?

o free não limpa nada, o que faz é notificar ao sistema que a região de memória se encontra livre. o que nada te garante é a persistência dos dados existentes nessa região de memória porque já na próxima linha de código essa região de memória poderá ter sido alocada e contêm dados diferentes


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

...

a outra solução é "trocar" os ponteiros de memória, isto porque:

- em "tmp_str" ficarás com o ponteiro para a região de memória previamente apontado por "string"

- em "string" ficarás com o ponteiro para a região de memória com a string alterada

- a região de memória libertada com a chamada free(tmp_str) já não é a mesma que a da variável "string" e contem a string original (a que já não interessa)

- a função retorna o ponteiro para a região de memória que interessa, logo é possível usar pela função "chamadora" desta.

...

Não sei se percebi!

A ideia é então trocar os ponteiros de memória para onde eles apontam, uma com a outra, certo?

Ou seja, se "string" aponta para "ab123cd" e se "tmp_string" aponta para "ef456gh", é fazer "string" apontar para "ef456gh" e "tmp_string" apontar para "ab123cd"... É isto?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

A ideia é então trocar os ponteiros de memória para onde eles apontam, uma com a outra, certo?

Ou seja, se "string" aponta para "ab123cd" e se "tmp_string" aponta para "ef456gh", é fazer "string" apontar para "ef456gh" e "tmp_string" apontar para "ab123cd"... É isto?

acho que percebeste a ideia fundamental, mas não percebeste exactamente o que é que disse e porque.

pensa assim :

tens um ponteiro para uma região de memória A onde se encontra a string original.

tens um segundo ponteiro para uma segunda região de memória B onde se encontra a string alterada.

o que disse foi (de uma forma que pensava que irias perceber mais rapidamente o porquê) :

- liberta a memória da string original, e retornar o ponteiro da string alterada, sendo que no código que chama a função, terás de receber esse ponteiro como sendo o novo valor do ponteiro da string.


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Então mas assim não me fica depois a memória alocada para a variável tmp_string por libertar???

/*****Invert String*****/
char* reverse_string (char* string){
 char *tmp_str = NULL;
 short int i = 0;
 size_t len = 0;
 len = strlen (string);
 tmp_str = mem_alloc (BUFFER);
 for (i = len; i > 0; i--)
   tmp_str [len - i] = string [i - 1];
 tmp_str [len + 1] = '\0';

 free (string); //libertar a string original
 return tmp_str;
}

e no código que chama a função, terei que receber esse ponteiro como sendo o novo valor do ponteiro da string:

#include <stdio.h>
#include "text.h"
#include "mem.h"
int main (int argc, char** argv){
   char* string = NULL;
   string = mem_alloc (BUFFER * sizeof (char));
   fgets (string, BUFFER, stdin);
   if (string [strlen (string) - 1] == '\n')
  string [strlen (string) - 1] = '\0';
   printf ("The string is: %s\n", string);
   string = reverse_string (string); //estou a receber o pontiero da string alterada no código que chama a função
   printf ("The inverted string is: %s\n", string);
   return 0;
}

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

e porque tiraste o último free do código do main ?

para perceberes melhor o código, vamos alterar um niquito os ficheiro mem.h e o mem.c:

#pragma once

#include <stdlib.h>

#define MEM_ERROR_OK            0
#define MEM_ERROR_NO_MEMORY     -1

void* mem_alloc(const size_t size);
void  mem_free(void* ptr);

int   mem_get_error(void);
int   mem_clear_error(void);

#include <stdio.h>
#include "mem.h"

int mem_error = MEM_ERROR_OK;

void* mem_alloc(const size_t size)
{
   void* result;
   if ((result = malloc(size)) == NULL)
   {
       mem_error = MEM_ERROR_NO_MEMORY;
   }
   else
   {
       printf("Memoria alocada : %p\n", result);
   }

   return result;
}

void mem_free(void* ptr)
{
   if (!ptr)
   {
       return;
   }

   printf("Memoria libertada : %p\n", ptr);
   free(ptr);
}

int mem_get_error(void)
{
   return mem_error;
}

int mem_clear_error(void)
{
   return mem_error = MEM_ERROR_OK;
}

agora usa a função mem_free para libertar a memória e vê o que te aparece.

vamos ver se ficas a perceber exatamente o que quero dizer


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Mas queres que use o mem_free no file main.c onde o tinha originalmente o free (string) ou que mantenha o code do post #19 e use mem_free em vez do free () apenas na função invert_string ()???

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

quero que usas a função mem_free sempre que necessites de libertar memória para perceberes o que se passa

e obviamente, quero que voltes a colocar o free (agora mem_free) na função main que tinhas originalmente


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok, acho que já percebi!

1º - Alocar memória para guardar a string original (

string = mem_alloc (BUFFER);

no file main.c)

2º - Alocar memória para guardar a string alterada (

tmp_str = mem_alloc (BUFFER);

, no file text.c)

3º - libertar a memória apontada por "string" (

mem_free (string);

, no file text.c)

4º - "string" fica agora a apontar para onde apontava antes "tmp_string", onde está já a string invertida (

string = reverse_string (string);

, no file main.c)

5º - lebertar a memória apontada por "string" que era antes a memória apontada por "tmp_string" (

mem_free (string);

)

Foi o que percebi!

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

e viste as posições de memória a serem reservadas e libertadas com o código que te dei ?

Sim, vi, por isso é que acabei por perceber o mecanismo!

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.