繁体   English   中英

C 中未定义的 function 参考

[英]Undefined function reference in C

I've several files with main functions in C, for example, I've files called show.c , delete.c add.c (...). 我还有一个文件,名为interpreter.c ,它可能会调用其中一个文件,例如delete.c 这些文件中的大多数都实现了一个主要的 function,例如delete.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

int main (int argc, char *argv[]) 
{
    int fd, rm;
    char *caminho = argv[1]; // argumento inserido no terminal
    char caminhod[30]="../TPSOFinal/";

    strcat(caminhod,argv[1]);
    
    fd = open(caminhod, O_RDONLY);

    rm=unlink(caminhod);

    // Verifica se o caminho inserido no input existe
    if(rm == 0){
        write(1,"Ficheiro eliminado!!!\n", 22);
        return 0;
    }
    else{
        write(1,"Erro ao eliminar ficheiro !!!\n", 29);
        perror("Erro");
    }
    
    return 0;
    close(fd);
}

口译员:

#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <readline/readline.h>
#include <readline/history.h>


#define LER_BUFFER 1024
#define TBUFF 64
#define DELIM "\t\r\n\a"

int mostra(char **args);
int conta(char **args);
int acrescenta(char **args);
int apaga(char **args);
int informa(char **args);
int lista(char **args);
int manual(char **args);
int termina(char **args);


char *comando[] =
{
    "mostra <caminho>",
    "conta  <caminho>",
    "acrescenta <caminho> <caminho destino>",
    "apaga <caminho>",
    "informa <caminho>",
    "lista <caminho>",
    "manual",
    "termina",
    " ",
};


int (*fcomandos[]) (char**) =
{
    &mostra,
    &conta,
    &acrescenta,
    &apaga,
    &informa,
    &lista,
    &manual,
    &termina
};
    
int ncomandos()
{
    return sizeof(comando)/sizeof(char*);
}


void processa(char *linha, char **argv)
{
    while(*linha != '\0')
    {
        while(*linha == ' ' || *linha == '\t' || *linha == '\n')
        {
            *linha++ = '\0';      //troca caracteres especiais
        }
        *argv++ = linha;              //guarda posição

        while (*linha != '\0' && *linha != ' ' && *linha != '\t' && *linha != '\n')
        {
            linha++;
        }
    }
    *argv = NULL;
}
char *lerlinha (void)
{
    char *linha = NULL;
    ssize_t tam = 0;
    getline (&linha, &tam, stdin);

    return linha;
}

char **separa (char *linha)
{
    int tam = TBUFF, pos = 0;
    char **palavras = malloc (tam *sizeof(char*));
    char *palavra;

    if (!palavras)
    {
        perror("Erro");
        exit(EXIT_FAILURE);
    }
    
    palavra = strtok (linha, DELIM);

    while (palavra != NULL)
    {
        palavras [pos] = palavra;
        pos ++;
        
        if (pos >= tam)
        {
            perror ("Erro");
        }
        
    }
    palavra = strtok(NULL, DELIM);
    
    palavras [pos] = NULL;
    return palavras;
}

int launch (char **args)
{
    pid_t pid, wpid;
    int estado;

    pid = fork();
    
    if (pid == 0)
    {
        if(execvp(args[0],args)==-1){ perror ("Erro!"); }
        
        exit (EXIT_FAILURE);
    }

    if (pid <0)
    {
        perror ("Erro!");
    }
    else
    {
        do{wpid = waitpid(pid, &estado, WUNTRACED);}
        while (!WIFEXITED(estado)&& !WIFSIGNALED(estado));
    }

    return 1;

    
}
//Testa se os comandos existem
int mostra (char **args)
{
    if (args[1] == NULL)
    {
        perror("sem argumentos ");
    }
    else if (chdir (args[1]) != 0)
    {
        perror ("Erro!");
    }
    return 1;
}

int conta ( char ** args)
{
    if (args[1] == NULL)
    {
        perror("Sem argumentos "); 
    }
    else if (chdir (args[1])!= 0)
    {
        perror ("Erro!");
    }
    return 1;
}

// Manual dos comandos
int manual (char **args)
{
    int i;
    printf("\n\nMiguel Oliveira\n");
    printf("10260 - LESI\n");
    printf("Sistemas Operativos e Sistemas Distribuidos\n");
    printf("\nLista de Comandos\n");

    for (i=0; i<ncomandos(); i++)
    {
        printf("%s\n", comando[i]);
    }
    return 1;
}

int termina (char **args)
{
    return 0;
}


//Executa os comandos
int executar (char **args)
{
    int i;
    
    if (args[0] == NULL)
    {
        return 1;
    }

    for (i=0; i<ncomandos(); i++)
    {
        if (strcmp(args[0], comando[i])==0)
        {
            return (*fcomandos[i])(args);
        }
    }
    return launch(args);
}

//Interpretador
void  interpretador (void)
{
    char  *linha;             
    char  **args;
    int estado;            

    do
    {                   
        printf("%% "); 
        linha = lerlinha();
        args = separa(linha);
        estado = executar(args);

        free(linha);
        free(args);  
        
    } while (estado);
}

int main (void)
{
    interpretador();

    return EXIT_SUCCESS;
}

我试图研究类似的问题,我发现了一些可能的解决方案,但无法解决我的问题,如底部GCC 编译错误所示

您不会“调用源文件”; 源文件定义函数和变量,并且在编译时,如果不同文件中定义的文件具有声明(通常在 header 文件中)或指针(通过动态链接方法,如 POSIX dlsym()),则它们可以相互使用。

考虑这个最小的例子。 首先, example.c

#include <stdlib.h>
#include <stdio.h>

/* We expect someone else to define these */
extern int one(void);

int main(void)
{
    printf("one() returned %d.\n", one());
    return EXIT_SUCCESS;
}

helper.c

int one(void)
{
    return 2;  /* TODO: It's not one! */
}

您将每个源文件编译为 object 文件:

gcc -Wall -O2 -c example.c
gcc -Wall -O2 -c helper.c

然后将它们链接到一个可执行程序:

gcc -Wall -O2 example.o helper.o -o program

您可以使用它运行

./program

通常,每个提供可在该文件之外使用的函数或变量的 C 源文件都会在 header 文件中声明它们。 这是一个更好的例子。

度数.h

#ifndef   DEGREES_H
#define   DEGREES_H

double radians_to_degrees(double);
double degrees_to_radians(double);

#endif /* DEGREES_H */

#ifndef#define#endif用作警卫,因此如果您多次#include文件,则函数只会被声明一次。 (如果看到多个声明,编译器会报错。另外,我们不需要在这里使用extern 。)

那么上面的实现就是度数。c

#ifndef  PI
#define  PI  3.14159265358979323846
#endif

double  degrees_to_radians(double degrees)
{
    return degrees * PI / 180.0;
}

double  radians_to_degrees(double radians)
{
    return radians * 180.0 / PI;
}

在同一个项目中的程序myprog.c中,您将使用上述代码:

#include <stdlib.h>
#include <stdio.h>
#include "degrees.h"

int main(void)
{
    printf("45 degrees is %.6f radians.\n", degrees_to_radians(45.0));
    printf("2 radians is %.3f degrees.\n", radians_to_degrees(2.0));
    return EXIT_SUCCESS;
}

再次将两个源文件编译为 object 文件,

gcc -Wall -O2 -c degrees.c
gcc -Wall -O2 -c myprog.c

然后链接到一个程序,比如myprog

gcc -Wall -O2 degrees.o myprog.o -o myprog

然后您可以运行:

./myprog

也可以将 degree.h 中声明的函数和变量编译并链接到static ( libdegrees.a ) 或动态 ( libdegrees.so ) 库,并将 header 文件安装到标准位置,以便您的程序可以而是使用#include <degrees.h>和通过-ldegrees链接到库的程序,但在您能够很好地处理多个文件之前,最好不要这样做。

在那之前,您可能会发现以下Makefile很有用

CC      := gcc
CFLAGS  := -Wall -O2
LDFLAGS :=
PROGS   := myprog

all: clean $(PROGS)

clean:
    rm -f *.o $(PROGS)

%.o: %.c
    $(CC) $(CFLAGS) -c $^

myprog: degrees.o myprog.o
    $(CC) $(CFLAGS) $^ -o $@

您可以在PROGS行中添加多个程序,用空格分隔,并复制myprog:为每个行,列出程序所需的 object 文件。

有了这个,编译程序所需要做的就是输入make

这个论坛吃标签,而 Makefiles 需要缩进才能使用它们。 因此,如果您只是将其复制粘贴到文件中,它将无法正常工作。 但是,您可以通过运行来修复它

sed -e 's|^  *|\t|' -i Makefile

它使用文件 Makefile 中的制表符删除每行上的所有初始空格。

如果您使用单独的库,通常是libm ( #include <math.h> ),您只需将-lm (dash ell em) 添加到LDFLAGS行。 如果您最终使用动态链接,那就是-ldl

如果您要使用 Gtk+ 编写图形程序,您将 append ` pkg-config --cflags gtk+-3.0 `(包括反引号 `)到CFLAGS行,以及 ` pkg-config --libs gtk+-3.0 ` LDFLAGS行和#include <gtk/gtk.h>到您的程序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM