c - 导入 C 函数
问题描述
以下导入语句有什么问题?我觉得我在这里搞砸了一些非常基本的东西:
// main.c
#include <stdio.h>
#include "1_square.h"
int main(int argc, char *argv[]) {
int a=4;
int a_squared=square(a);
printf("Square of %d is %d\n", a, a_squared);
return 0;
}
// 1_square.h
int square(int num);
// 1_square.c
int square(int num) {
return num*num;
}
$ gcc 0_main.c -o 0_main && ./0_main
/usr/bin/ld: /tmp/cc6qVzAM.o: in function
main': 0_main.c:(.text+0x20): undefined reference to
square' collect2: error: ld returned 1 exit status
还是gcc
需要参考1_square
才能构建它?
解决方案
您需要将该1_square.c
文件添加到您的构建命令中:
gcc -o 0_main 0_main.c 1_square.c && ./0_main
您需要函数的定义及其声明。
从评论:
那么为什么它需要
1_square.c
在命令行中,然后1_square.h
在0_main.c
标题中呢?
就像 John Bollinger 在评论中指出的那样,唯一的1_square.h
共同点1_square.c
是它们的名字,这对编译器没有意义。就 gcc 而言,它们之间没有内在的关系。
让我们首先将所有内容放在一个源文件中:
/** main.c */
#include <stdio.h>
int square( int num );
int main( int argc, char **argv )
{
int a = 4;
int a_squared = square( a );
printf( "Square of %d is %d\n", a, a_squared );
return 0;
}
int square( int num )
{
return num * num;
}
在 C 语言中,函数必须在源代码中调用之前声明;声明介绍了函数名称、它的返回类型以及它的参数的数量和类型。这将让编译器在翻译过程中验证函数调用是否正确编写,如果不是,则发出诊断,而不是等到运行时抛出错误。在这种特殊情况下,我们指定该square
函数采用单个int
参数并返回一个int
值。
这并没有说明square
函数本身或它是如何运行的——这是稍后由函数定义提供的。square
函数定义也用作声明;如果调用者和被调用函数都在同一个翻译单元(源文件)中,那么您可以将定义放在调用之前,而完全不必弄乱单独的声明:
/** main.c */
#include <stdio.h>
int square( int num )
{
return num * num;
}
int main( int argc, char **argv )
{
int a = 4;
int a_squared = square( a );
printf( "Square of %d is %d\n", a, a_squared );
return 0;
}
这实际上是可取的,因为您不必在两个不同的地方更新函数签名。这意味着您的代码“向后”读取,但老实说,这是一种更好的方法。
但是,如果您将函数分离到单独的源文件中,那么您需要有一个单独的声明:
/** main.c */
#include <stdio.h>
int square( int num );
int main( int argc, char **argv )
{
int a = 4;
int a_squared = square( a );
printf( "Square of %d is %d\n", a, a_squared );
return 0;
}
/** square.c */
int square( int num )
{
return num * num;
}
当它在处理时,编译器对函数的定义main.c
一无所知——它甚至不知道文件存在。之前是否编译都没有关系,反之亦然;编译器一次对一个文件进行操作。编译器唯一知道的函数是.square
square.c
main.c
square.c
square
main.c
但是,必须为单独文件中定义的每个函数手动添加声明.c
是一件痛苦的事情 - 您不想为printf
、scanf
、fopen
等编写单独的声明。因此,按照惯例,我们创建一个.h
与.c
存储声明的文件:
/** main.c */
#include <stdio.h>
#include "square.h"
int main( int argc, char **argv )
{
int a = 4;
int a_squared = square( a );
printf( "Square of %d is %d\n", a, a_squared );
return 0;
}
/** square.h */
int square( int num );
/** square.c */
int square( int num )
{
return num * num;
}
按照惯例,我们还会在文件中添加包含保护.h
- 这可以防止每个翻译单元多次处理文件的内容,如果您#include "square.h"
包含另一个也包含square.h
.
/** square.h */
#ifndef SQUARE_H // the contents of this file will only be
#define SQUARE_H // processed if this symbol hasn't been defined
int square( int num );
#endif
同样按照惯例,我们将.h
文件包含在文件中.c
以确保我们的声明与我们的定义一致 - 如果不符合,编译器会抱怨:
/** square.c */
#include "square.h"
int square( int num )
{
return num*num;
}
在两者main.c
都square.c
被编译之后,它们的目标代码将被链接到一个可执行文件中:
main.c ------+-----> compiler ---> main.o ----+--> linker ---> main
| |
square.h ----+ |
| |
square.c ----+-----> compiler ---> square.o --+
我们必须编译这两个C 文件并将它们的目标代码链接在一起以拥有一个工作程序。毫无疑问,您的 IDE 让这一切变得简单——您只需将源文件添加到项目中,它就会正确构建它们。 gcc
让您在一个命令中完成所有操作,但您必须列出.c
项目中的所有文件。
如果您从命令行运行,则可以使用该make
实用程序来简化操作。您需要创建一个看起来像这样的 Makefile:
CC=gcc
CFLAGS=-std=c11 -pedantic -Wall -Werror
main: main.o square.o
all: main
clean:
rm -rf main *.o
然后,您需要做的就是make
在命令行键入,它会使用内置的编译规则main.c
和square.c
.
推荐阅读
- google-cloud-platform - 如何在数据流模板中运行多个查询?
- flutter - Flutter Navigator 2.0 中 route.didPop(result) 何时等于 false
- python - Python Pandas 显示两个数据帧之间的位置变化
- postgresql - PSQL 将映射应用到数组
- wpf - 有时 ListView 中的图像会消失
- typescript - 从 TypeScript 4.1 中的参数计算其中一个键的返回类型
- python - 计算两个不同列中的单词并按行求和
- mongodb - 如何在项目聚合中删除一个文档
- php - 日志未显示在 codeigniter 3 的日志文件夹中
- javascript - 当我的机器人加入新服务器并离开到文本文件时记录