IIC2333 / foro-2019-1

Foro oficial del curso IIC2333 - Sistemas Operativos y Redes, semestre 2019-1.
2 stars 0 forks source link

Error al modularizar #2

Closed JIGutierrez closed 5 years ago

JIGutierrez commented 5 years ago

Hola! Intenté partir la tarea testeando programas pequeños de C, y luego de muchos problemas, llegué a uno que no he podido superar...

Pienso crear alguna especie de lista dinamica (más que nada para ver si podía jajaja)

tengo mi main.c:

#include "linkedlist.c"
int main(){
  printf("main test\n");
  return 0;
  add(0, 0);
}

mi linkedlist.c

#include <stdio.h>

typedef struct Node{
  int  *value;
  int  *next;
} Node;

extern void add(Node *head, int *new_value){
  printf("add test\n");
  return;
}

y luego mi make

scheduler: main.c linkedlist.c
    gcc -o scheduler main.c linkedlist.c -I.

Este me resulta en un error

"/tmp/ccew0KIQ.o: In function \`add':
linkedlist.c:(.text+0x0): multiple definition of \`add'
/tmp/ccixUqyi.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'scheduler' failed
make: *** [scheduler] Error 1"

He leido que recomiendan usar un archivo header, sin embargo no entiendo muy bien que es lo recomendado. Agradecería sugerencias! Gracias!

frvalenzuela commented 5 years ago

No leí toda la issue completa. Pero en la ayudantía tenía errores parecidos mientras la seguía en mi PC y se arregló cuando en vez de hacer # include “linkedlist.c”

Usaba #include “linkedlist.h” en el main.c

Ojalá ayude en algo !

JIGutierrez commented 5 years ago

Logré solucionarlo: Se necesita un archivo aparte con el mismo nombre, ".h".

main.c

#include "linkedlist.h"
#include <stdio.h>
int main(){
  printf("holi\n");

  add(0, 0);
  return 0;
}

linkedlist.h

#ifndef LINKED_LIST
#define LINKED_LIST
void add(int *head, int *new_value);
#endif

linkedlist.c

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

typedef struct Node{
  int  *value;
  int  *next;
} Node;

void add(int *head, int *new_value){
  printf("hi\n");
  return;
}
Geeermy commented 5 years ago

¡Hola!

Dos cosas:

1) #include solo funciona con archivos header, es decir, los ".h". Estos por lo general solo tienen las definiciones macro de las funciones que piensas utilizar, mientras que los ".c" asociados contienen el detalle de lo que harán. Por ejemplo:

example.h

void print_f2(char* word);

example.c

#include "example.h"
#include <stdio.h>
void print_f2(char* word){
    printf("%s\n",word);
};

void print_hi(){
    printf("hi\n");
}

Fíjate que example.c tiene dos métodos, pero al final el que podría utilizar importando example.h sería solo print_f2 y no print_hi, dado que es la única definida en el header. Así puedo tener métodos adicionales en el ".c" que puedo utilizar de forma auxiliar pero que no necesariamente quiero que tengan a disposición otros programas que importen el ".h". Aprovecho de contestar a pesar de haber resuelto tu problema para que no quede como que haya sido "magia" jeje.

Referencia: https://www.tutorialspoint.com/cprogramming/c_header_files.htm

2) Por favor, NO CIERREN LAS ISSUES, tu duda puede ser la de otro/a compañero/a.

¡Que estén bien! :-)

cruz commented 5 years ago

Hola (no sé por qué no había visto esta issue hasta ahora).

Solo para complementar algunas cosas. El #include de C es, literalmente, un "copia y pega este archivo aquí antes de compilar". Muchísimo menos elaborado que los import. Por ello al hacer:

#include "linkedlist.c"
int main(){
  printf("main test\n");
  return 0;
  add(0, 0);
}

y después compilar con

gcc -o scheduler main.c linkedlist.c -I.

estás pasando en la práctica dos veces la función add para compilar. Una vez en main.c donde viene copy/pasteada desde linkedlist.c, y una vez en el linkedlist.c que le pasas en la línea de compilación. Técnicamente podrías haberlo solucionado sacando el linkedlist.c de la línea de compilación, pero eso es una mala práctica porque hace más difícil trabajar con múltiples archivos y hace que haya que recompilar todo cada vez.

Las soluciones que han mencionado aquí evitan justamente eso. El uso de .h en los #include es una buena práctica de C donde los header solo deben incluir las declaraciones (prototipos) de las funciones, pero no el código que las implementa, y los .c que son los que tienen el código, solo se entregan una vez en la línea de compilación.

Finalmente, la técnica de usar macros de definición (o guardias) en linkedlist.h:

#ifndef LINKED_LIST
#define LINKED_LIST
... ...
#endif

se usa para evitar inclusiones repetidas de headers. Es típico que un .h incluya a otros y no es difícil que un .h termine siendo incluído más de una vez. Por ejemplo, dos .h distinto podrían a su vez hacer un #include<stdio.h> y tendríamos errores diciendo que printf está declarada dos veces. Para evitar los problemas de dobles definiciones de funciones, tipos y variables, que esto trae, se define al principio de cada .h una macro (que no se asigna a nada, pero se define que existe), de manera que si en algún momento se vuelve a hacer #include "linkedlist.h", entonces el preprocesador (que ejecuta antes del compilador) encontrará que la macro LINKED_LIST ya está definida e ignorará todo lo que haya hasta el #endif. Más referencias aquí: https://stackoverflow.com/questions/1653958/why-are-ifndef-and-define-used-in-c-header-files