【问题标题】:Warning: implicit declaration of function — why does my code work anyway?警告:函数的隐式声明——为什么我的代码仍然可以工作?
【发布时间】:2014-01-16 18:07:56
【问题描述】:

我经历了以下线程:

可能我的问题是相关的。但是,虽然他们提供了在使用函数之前应该声明函数原型的解决方案,但我想探索当函数名称不匹配时会发生什么。在我的测试中,它仍然可以正常工作。

主 C 文件

#include "node.h"
int main(){
    nd *head=NULL;
    nd *tail=NULL;

    create_node(&head, &tail, 10);
    create_node(&head, &tail, 20);
    create_node(&head, &tail, 15);
    create_node(&head, &tail, 35);
    create_node(&head, &tail, 5);
    create_node(&head, &tail, 25);
    print_list(head, tail);
    create_node(&head, &tail, 55);
    create_node(&head, &tail, 52);
    create_node(&head, &tail, 125);

    printf("%d\n",tail->data);
    printf("%d\n",head->data);
    print_list(head, tail);
    return 0;
}

node.h 文件

#ifndef NODE_H
#define NODE_H

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

typedef struct node{
    int data;
    struct node *next;
    struct node *prev;
}nd;

void insert_node(nd **head, nd **tail, int data);

void print_list(nd *head, nd *tail);

#endif

node.c文件

#include "node.h"
void create_node(nd **head, nd **tail, int d){

    nd *temp=(nd *) malloc(sizeof(nd));
    temp->data=d;
    temp->next=NULL;
    temp->prev=NULL;
    /* Start of the Queue.              */
    if(*head==NULL && *tail==NULL){
        *head=temp;
        *tail=temp;
    }
    /* Linking with tail of the Queue.              */
    else if((*tail)->next==NULL){
        (*tail)->next=temp;
        temp->prev=*tail;
        *head=temp;
    }
    /* Adding remaining elements of the Queue.      */
    else{
        (*head)->next=temp;
        temp->prev=*head;
        *head=temp;
    }
}

void print_list(nd *head, nd *tail){
    if(NULL==head){
        printf("Queue is empty\n");
    }
    else{
        printf("Printing the list\n");
        nd *temp;
        for(temp=tail;temp!=NULL;temp=temp->next){
            printf("%d ",temp->data);
        }
        printf("\n");
    }
}

输出

Printing the list
10 20 15 35 5 25 
10
125
Printing the list
10 20 15 35 5 25 55 52 125 

node.h 中声明的函数的名称是insert_node,而在node.c 中它是create_node。有人可以分享一些关于它为什么运行的见解吗?但它会引发警告:

警告:函数的隐式声明

【问题讨论】:

  • 之所以有效,是因为main 调用create_node 并且create_nodenode.c 中实际声明的内容。参数类型恰好足够通用,以至于它们都可以。标头中的名称错误会导致警告。如果node.c 中的create_node 实际上被称为insert_node,则链接将失败并说它找不到定义为create_node 的函数。
  • 您的问题是“为什么我的编译器不将警告视为错误?”有一个标志。
  • 这不是“工作正常”。如果调用未声明的函数会产生一些输出,那么无论如何它都是错误的(因为那时行为是未定义的)。不过,它可以假装“工作正常”。这并不意味着它确实可以正常工作。
  • 如果使用 gcc,请将 -Wall -Wextra -Werror 添加到 CFLAGS
  • @H2CO3 接受的答案解释说,对于这种情况,带有参数的默认编译器行为是可以的。虽然隐式声明可能并不总是正确的,但在这种情况下,它看起来不仅仅是“假装工作正常”;编译器所做的假设对于 this 案例是正确的,因此 this 案例可以正常工作。

标签: c function warnings implicit-declaration


【解决方案1】:

首先,您已经声明了一个名为insert_node 的函数,但这并不重要。可以声明函数,但不定义它们(即不提供它们的代码),只要您不使用该函数。这在现实生活中经常发生:头部定义了很多函数,然后在链接时只需要提供实际使用的函数。

警告涉及create_node。由于在编译主 C 文件时没有函数声明,因此编译器对其参数类型进行了一些假设。它提升所有参数:小于int 的整数类型(例如charshort)被提升为intfloats 晋升为double;指针类型不会被转换。使用您的代码,这恰好可以工作,因为

  • 您总是传递正确类型的参数;
  • 没有提升任何参数类型。

如果您将data 参数的类型更改为long,则编译器将生成代码以调用假定int 类型的函数,但该函数需要long 参数。在intlong 具有不同大小的平台上,您可能会收到垃圾数据、崩溃或其他不当行为。

如果您将data 参数的类型更改为char,则编译器将生成代码以调用假定int 类型的函数,但该函数需要char 参数。同样,您可能会发现代码使用了错误的数据、崩溃等。

C 通常会给你足够的绳子让你上吊。如果你以错误的方式剪断了一根绳子,它可能会碰巧起作用。或者它可能不会。

【讨论】:

  • 值得一提的是,您在此处描述的所有内容仅在 C89 中是正确的。 C99 使对未声明函数的调用无效。
  • @H2CO3 好点,但由于代码无效并且编译器已发出诊断信息,C89 编译器可以保持完全相同的行为方式,并且在这一点上符合 C99。
  • 对不起,我不明白。这不可能在 C89 和 C99 下表现相同。在 C89 中,如果提升的参数类型匹配,则 必需 才能工作,而在 C99 中它是 UB。 (我也不明白您所说的“C89 编译器将符合 C99”是什么意思,这也不可能。您能否详细说明一下?)
  • @H2CO3 使用 C89 编译器,这个例子必须工作——但问题是这个例子如何工作。对于 C99,此示例不必工作,但仍允许编译器使其工作。
【解决方案2】:

在您的示例中,您有一个隐式声明 create_node 并声明了一个未实现的函数 insert_node

调用create_node 的原因将在您链接到的先前帖子中介绍。

insert_node 未实现这一事实对您的程序无关紧要,因为没有任何东西试图调用它。如果您将一行更改为调用insert_node,它将在没有警告的情况下编译,但随后无法链接insert_node 的未解决符号错误。

我相信你知道这一点,但这里正确的方法是标准化 create_nodeinsert_node 之一,并在整个程序中使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多