c - C中未定义的函数引用
问题描述
我在 C 中有几个具有主要功能的文件,例如,我有名为show.c、delete.c add.c (...) 的文件。我还有一个文件,名为interpreter.c,它可能会调用其中一个文件,例如delete.c。这些文件中的大多数都实现了一个主要功能,例如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 编译错误所示
解决方案
您不会“调用源文件”;源文件定义函数和变量,并且在编译时,如果不同文件中定义的文件具有声明(通常在头文件中)或指针(通过动态链接方法,如 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! */
}
您将每个源文件编译为一个目标文件:
gcc -Wall -O2 -c example.c
gcc -Wall -O2 -c helper.c
然后将它们链接到一个可执行程序:
gcc -Wall -O2 example.o helper.o -o program
您可以使用它运行
./program
通常,每个提供在该文件之外可用的函数或变量的 C 源文件都会在头文件中声明它们。这是一个更好的例子。
度.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
这里使用。)
上面的实现则在degree.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;
}
再次将两个源文件编译为目标文件,
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中声明的函数和变量编译并链接到静态(libdegrees.a
)或动态(libdegrees.so
)库,并将头文件安装到标准位置,以便您的程序可以改为使用#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:
为每个程序复制行,列出程序所需的目标文件。
有了这个,编译程序所需要做的就是键入make
.
这个论坛吃标签,而 Makefiles 需要缩进才能使用它们。因此,如果您只是将其复制粘贴到文件中,它将无法正常工作。但是,您可以通过运行来修复它
sed -e 's|^ *|\t|' -i Makefile
它使用文件 Makefile 中的制表符删除每行上的所有初始空格。
如果您使用单独的库,通常是libm
( #include <math.h>
),您只需将-lm
(dash ell em) 添加到该LDFLAGS
行。如果您最终使用动态链接,那就是-ldl
.
如果您要使用 Gtk+ 编写图形程序,您将在该行附加 ` ` pkg-config --cflags gtk+-3.0
(包括反引号 `),并将 ` ` 附加到该行和您的程序中。CFLAGS
pkg-config --libs gtk+-3.0
LDFLAGS
#include <gtk/gtk.h>
推荐阅读
- java - Android Studio - 录音问题 - Java 新手
- python-3.x - 升级到 3.8 后 distutils.util 丢失
- amazon-web-services - 如何在 LoadBalancers 中查找过时的策略
- gemfire - 选择用于 Geode 服务器和定位器启动脚本的参数的最佳策略是什么
- excel - 将最后使用的 4 行复制并粘贴到下一个可用的 4 行中
- python - Django:无法从静态目录导入 JavaScript 文件。(神社2)
- sql - 如何在三个表上应用连接?
- javascript - 在 slim-select 多选 JQuery 插件中,如何设置我们的自定义值或 id 并获取它们并与其他选择选项进行比较以禁用
- javascript - 如何在按钮单击 Reactjs 上增加项目?
- python - 反转链表改变原来的链表