首页 > 解决方案 > 导入 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才能构建它?

标签: c

解决方案


您需要将该1_square.c文件添加到您的构建命令中:

gcc -o 0_main 0_main.c 1_square.c && ./0_main

您需要函数的定义及其声明。

从评论:

那么为什么它需要1_square.c在命令行中,然后1_square.h0_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一无所知——它甚至不知道文件存在。之前是否编译都没有关系,反之亦然;编译器一次对一个文件进行操作。编译器唯一知道的函数是.squaresquare.cmain.csquare.csquaremain.c

但是,必须为单独文件中定义的每个函数手动添加声明.c是一件痛苦的事情 - 您不想为printfscanffopen等编写单独的声明。因此,按照惯例,我们创建一个.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.csquare.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.csquare.c.


推荐阅读