44 posts
Another C vs C++ "why is doing this thing": Forward Declarations
Edited by rizoma on Reason: my english sucks
So I got this code, and I forgot to forward declare a function but I got it compiled anyway, I can't just figure it out why! Of course if I compile in c++ I got the expected error: C3861: identifier not found.

It' 6.4 exercise on the K&R book (really exited about using a binary tree and malloc for the fist time *_* even if I used virtual alloc following hmh, I feel more by myself using only a book :) )

Here's the code HAVE FUN @[email protected] :
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 /* binary tree for sorting and count words from a stream of text */ #include #include #include #include /* #include "mylib.c" */ #define MAXWORD 100 struct tNode /* tree node */ { char *word; int count; struct tNode *left; struct tNode *right; }; struct tNode * AddTree(struct tNode *, char *); void TreePrint(struct tNode *); /* here's the declaration I forgot */ #if 0 int GetWord(char *, int); #endif int main() { struct tNode *root; char word[MAXWORD]; root = NULL; while(GetWord(word, MAXWORD) != EOF) if(isalpha(word[0])) root = AddTree(root, word); TreePrint(root); return 0; } struct tNode * TreeAlloc(void); char * DuplicateString(char *); struct tNode * AddTree(struct tNode *p, char *w) { int cond; if(p == NULL) { p = TreeAlloc(); p->word = DuplicateString(w); p->count = 1; p->left = p->right = NULL; } else if((cond = strcmp(w, p->word)) == 0) { p->count++; } else if(cond < 0) { p->left = AddTree(p->left, w); } else if(cond > 0) { p->right = AddTree(p->right, w); } return p; } void TreePrint(struct tNode *p) { if(p != NULL) { TreePrint(p->left); printf("%4d %s\n", p->count, p->word); TreePrint(p->right); } } struct tNode * TreeAlloc(void) { return(struct tNode *) malloc(sizeof(struct tNode)); } char * DuplicateString(char *s) { char *p; p = (char *) malloc(strlen(s) + 1); //+1 for the string terminator obviously! if(p != NULL) strcpy(p, s); return p; } int GetWord(char *word, int lim) { int c, getch(void); void ungetch(int); char *w = word; while(isspace(c = getch())) ; if(c != EOF) *w++ = c; if(!isalpha(c)) { *w++ = '\0'; return c; } for(; --lim > 0; w++) if(!isalnum(*w = getch())) { ungetch(*w); break; } *w = '\0'; return word[0]; } 

by the way I'm using the microsoft compiler. Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64

P.S I also needed to include the malloc.h file which is not included in the original code, otherwise I got this warnings:

w:\ansic\code\ansic.c(91): warning C4312: 'type cast': conversion from 'int' to 'tNode *' of greater size 95 on this page
w:\ansic\code\ansic.c(99): warning C4312: 'type cast': conversion from 'int' to 'char *' of greater size 103 on this page

So if anyone know what's going on...
507 posts
Another C vs C++ "why is doing this thing": Forward Declarations
Edited by ratchetfreak on
If a function is not declared before use then the compiler assumes it is declared with an int return type and vararg paramters.

Why it doesn't error when it sees a definition of that function not matching that assumption I'm not clear about.
Bryan Taylor
55 posts
Another C vs C++ "why is doing this thing": Forward Declarations
The C compiler doesn't do any typechecking on function calls without a declaration. The compiler assumes you know what you are doing. (If you have your warning level set high enough, it *will* warn on this. It's a good idea to do so.)

The reason this *works*, in this case, is that you're returning int, which is the assumed return type of an undeclared function. The only implication on the caller's side is how much stack to allocate for the return value. You call with the correct arguments, so they are passed properly.

Remember that "functions" are not really a machine level construct. A function call is a pointer to some other code, a place to stick the return value, and arguments placed on the stack / in registers according to a particular convention. So long as both sides of the call line up, it "Just Works". (But if you get it *wrong*, you smash the stack and your program dies. So you still want to use forward declarations.)