I have not touched C for long really long time. My first language was C. But then we have been taught C++, Java and C# (in college days). Now mostly my work involve Java and groovy. And suddenly I have to do C again. I am not familiar with how industry uses C as I never was in project doing stuff in C.
I started in eclipse CDT, with MinGW on Windows 10. My new program grew big quickly. And my lack of experience was unrevealed to me. When I run, my program used to crash showing Windows dialog saying "MyProgram.exe has stopped working". Now I had no clue whats going wrong. Compilation was clean with no error, but only one warning. Now since from Java world, I was like warnings are not that fatal. So I simply just ignored it (I thats my lack of C experience). And went on debugging found the cause. Realised that the warning was indeed about the cause. Suffered mental frustation of wasting hours in debugging.
So here is code replicating issue in my original code:
1 #include "stdio.h"
2 #include "limits.h"
3
4 typedef struct TempStruct TempStruct;
5
6 struct TempStruct
7 {
8 int a;
9 TempStruct *next;
10 };
11
12 int function1(TempStruct *param)
13 {
14 return param == NULL;
15 }
16
17 int function2(TempStruct **param)
18 {
19 if(function1(param))
20 {
21 return INT_MIN;
22 }
23 *param = (*param)->next;
24 return 0;
25 }
26
27 int main()
28 {
29 TempStruct *tempStructObj = NULL;
30 function2(&tempStructObj);
31 printf("Does not reach here!!!");
32 return 0;
33 }
My C-noob eyes did not see anything wrong in it. Good that I knew how to do debugging. I got following in debugging:
In main()
this is done: *tempStructObj = NULL
. So, I was expecting function1()
to return 1, making function2()
returning from line 21.
The issue was that function1()
takes TempStruct*
. But on line 19, I passed it **param
. So inside function1()
, param
wasnt NULL
. So it returned false
(0
I guess). So function2()
did not returned from line 21. It executed (*param)->next
and hence the program crashed.
My questions:
Error: Multiple errors reported...
. This stuff occurred occasionally, but not always, say once in 5 debugging sessions. What can be the reason behind such ad hoc behavior?Q5. In point 3 of above diagram, what is that (0x62ff2c
) value of param
inside function1()
? If I keep signature of function1()
correctly as int function1(TempStruct **param)
and change inside reference correctly to *param
, *param
is correctly 0x0
(i.e. NULL
):
Edit
Q1. No, they are warnings as they are legit C code. It could be possible that you want such code. You can use -Werror
on gcc to make warnings to errors. Also add some other flags for turning on more warnings like -Wall -Wpedantic -Wextra -Wshadow -Wconversion -Wno-sign-compare
etc. This'd be a bit closer to what you're probably used to when using Java ;-)
Q2. Atleast on Linux you have coredumps, iirc Windows was minidumps. These can be loaded together with the corresponding executable into a debugger. Then you can access backtraces, values etc.
Q3.
Like above I asked about settings to make eclipse report the issue as fatal one or making crashes to generate report so that stuff can be fixed quickly instead of hours long debuggins.
Log yourself. Also there can be macros for easing this.
Do you use any better (and possibly involving smaller learning curve) alternative to (eclipse CDT + MinGW + Windows) that will provide more powerful debugging so that I can avoid such errors.
IDE is pretty irrelevant for C imho. Use Linux with native GCC instead, MinGW is nice but it can be daunting (my experience). Ofcourse MS VSC++ can also compile C but its just for C++ compatible thus not really specific to one standard.
Q4. Well, multiple errors occured which are listed. If it's difficult to reproduce it might be a problem in your setup, this is exactly the experience I had with MinGW on Windows.
Q5. It's the address -- you have a pointer to a pointer, so the first ("outer") pointer is that address, pointing to another pointer which is NULL
.
Or more verbosely:
tempStructObj
is a pointer to NULL
(ie. an int_ptr
which holds the value 0x0
.
To function2
you pass another int_ptr
which holds the semi-random value/address of the automatic variable int_ptr tempStructObj
is stored
Ie. you have such:
Address &tempStructObj
: tempStructObj
in the RAM.
When you then call function1
, you pass the value of this (not-NULL
) pointer. Of course the comparison is thus always false.
You'd need to compare
*param
with NULL
.
Even more: If you compile with GCC (on Linux) and use really verbose flags you get:
gcc -std=c99 -Wall -Wpedantic -Wextra -Wshadow -Wconversion -Wno-sign-compare -o main main.c
main.c: In function ‘function2’:
main.c:19:18: warning: passing argument 1 of ‘function1’ from incompatible pointer type [-Wincompatible-pointer-types]
if(function1(param))
^
main.c:12:5: note: expected ‘TempStruct * {aka struct TempStruct *}’ but argument is of type ‘TempStruct ** {aka struct TempStruct **}’
int function1(TempStruct *param)
^
So exactly the problem you had ^^
Also I'd remove the function1
altogether, it's completely unnecessary and just obfuscates the code. Also I'd use a different name for the struct
and the typedef
, appending the latter with a _t
. Also I'd move it into one shorter piece of code.
On a side note: add a \n
in the printf()
-call.
Edited code:
#include <stdio.h>
#include <limits.h>
typedef struct TempStruct_s {
int a;
struct TempStruct_s *next;
} TempStruct_t;
int function(TempStruct_t **param)
{
if(!*param) {
return INT_MIN;
}
*param = (*param)->next;
return 0;
}
int main()
{
TempStruct_t *tempStructObj = NULL;
function(&tempStructObj);
printf("Does not reach here!!!\n");
return 0;
}