Warning: implicit declaration of function — why does my code work anyway?
Asked Answered
Z

2

5

I have gone through the following threads:

Possibly my issue is linked. But while they offer the solution that the function prototype should be declared before the function is used, I wanted to explore what happens when the function name is not matching. In my test, it still works fine.

Main C file

#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 file

#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 file

#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");
    }
}

Output

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

The name of the function declared in the node.h is insert_node whereas in node.c it is create_node. Can someone share some insight on why is it running? It throws a warning though:

Warning: implicit declaration of function

Zindman answered 16/1, 2014 at 18:7 Comment(9)
It works because main calls create_node and create_node is what's actually declared in node.c. The parameter types happen to be generic enough that they are all OK. The error in the name in the header results in the warning. If the create_node in node.c were actually called insert_node, the link would fail and say it couldn't find a function defined as create_node.Wuhan
Is your question "Why doesn't my compiler treat warnings as errors?" There's a flag for that.Dimissory
It's not "working fine". If calling undeclared functions produces some output, then it's erroneous, no matter what (since the behavior is undefined then). It can pretend to "work fine", though. It doesn't mean that it indeed works correctly.Tentage
Add -Wall -Wextra -Werror to the CFLAGS if using gccVicarial
@H2CO3 The accepted answer explains that the default compiler behavior with the arguments is OK for this case. While implicit declarations might not always be right, it looks like in this case it's not just "pretending to work fine"; the assumptions that the compiler makes are correct for this case, so this case works correctly.Kimberykimble
@JoshuaTaylor No. Implicit declarations are illegal -- they were allowed back in 1989, but C99 and last but not least the current standard, C11, made them prohibited. As a note in the C99 standard says, "Thus, an undeclared identifier is a violation of the syntax". So, the compiler translated a syntactically incorrect program -- this is a constraint violation, so the resulting executable has undefined behavior. I see where you're coming from, but this is not the case where default promotion rules and accidentally matching formal and actual argument types make the behavior well-defined.Tentage
@H2CO3 Ah, good clarification! Then, if I understand you correctly, the accepted answer is describing what's probably happening, even though it should be an error, since it's a "violation of the syntax"? (On rereading the comments on the answer, I guess a violation of the syntax doesn't lead to "compiler error" but rather "compiler can do what it wants"?)Kimberykimble
@JoshuaTaylor The accepted answer is correct if we read it in the context of C89. The comments of the author of the answer are also correct -- upon encountering a particular UB, the compiler is free to emit whatever code it wants, including the possibility that it does the same thing as it would have done with correct code. Some compilers do that. However, generally asserting that "it works" is wrong, since generally it is UB. As to the syntax error: AFAIK diagnostics (=compiler errors or warnings) are required, and I don't know if a compiler is actually permitted to continue...Tentage
@JoshuaTaylor ...translation after encountering a syntax error, but if it does compile a program with (almost) any kind of error inside, then the behavior of that program will be undefined. I think I'll have to read up about this in the Standard.Tentage
H
6

First, you've declared a function called insert_node, but that doesn't matter. It's ok to declare functions, but not define them (i.e. not provide their code), as long as you don't use the function. This happens often in real life: headers define a lot of functions, and then at link time only the functions that are actually used need to be provided.

The warning concerns create_node. Since there is no declaration of the function when you compile the main C file, the compiler makes some assumptions about its parameter types. It promotes all arguments: integer types smaller than int (e.g. char and short) are promoted to int; floats are promoted to double; pointer types are not converted. With your code, this happens to work because

  • you're always passing arguments of the right type;
  • none of the argument types are promoted.

If you changed the type of the data parameter to long, then the compiler would generate code to call the function assuming an int type but the function would expect a long argument. On a platform where int and long have different sizes, you might get garbage data, a crash, or other misbehavior.

If you changed the type of the data parameter to char, then the compiler would generate code to call the function assuming an int type but the function would expect a char argument. Again, you might find that the code uses wrong data, crashes, etc.

C generally gives you enough rope to hang yourself. If you cut a piece of rope in the wrong way, it might just happen to work. Or it might not.

Hightower answered 16/1, 2014 at 18:19 Comment(4)
It might be worth mentioning that all you described here is only true in C89. C99 renders calls to undeclared functions invalid.Tentage
@H2CO3 Good point, but since the code is invalid and the compiler has emitted a diagnostic, a C89 compiler can keep behaving in exactly the same way and it'll comply to C99 on that point.Allopathy
Sorry, I don't understand that. This cannot possibly behave the same way under C89 and C99. In C89, it is required to work if the promoted argument types match, whereas it's UB in C99. (I also don't see what you mean by "the C89 compiler will comply to C99", that's not possible either. Could you please elaborate on that?)Tentage
@H2CO3 With a C89 compiler, this example has to work — but the question was about how this example could work at all. With C99, this example doesn't have to work, but a compiler is still allowed to make it work.Allopathy
P
5

In your example, you have an implicit declaration of create_node and declare an unimplemented function insert_node.

Calls to create_node work for reasons that'll be covered in the previous posts you link to.

The fact that insert_node isn't implemented doesn't matter for your program because nothing tries to call it. If you changed a line to call insert_node, it would compile without warning but then fail to link with an unresolved symbol error for insert_node.

I'm sure you know this, but the correct approach here is to standardise on one of create_node or insert_node and use that throughout your program.

Prophesy answered 16/1, 2014 at 18:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.