Unexplained segmentation fault

Hi, The following code reads 20 characters from one file and writes them (appends them) to the other file. The code works in Turbo C++ on windows but it shows segmentation fault on Linux. I am using Ubuntu 10.10 and gcc compiler.
Please tell me where I was wrong.

#include<stdio.h> 
 
void main() 
{ 
char* buf; 
char c; 
FILE * fp; 
FILE * fp2; 
int k; 
fp=fopen("sample","r"); 
fp2=fopen("sample1","a"); 
buf=fgets(buf,20,fp); 
while(buf!=NULL) 
{ 
k=fputs(buf,fp2); 
printf("%s\n",buf); 
buf=fgets(buf,20,fp); 
} 
 
}

Thanks for your help.:slight_smile:

char *buf;

This doesn't create memory for you. All a pointer is is an integer describing where memory is. And just like an uninitialized integer, an uninitialized pointer can be anything at all. So you could be trying to use any memory at all, which, in DOS, could mean accidentally overwriting bits of your operating system and hanging the computer or worse... In Linux though, your process resides in its own protected memory space, and if you try to use memory you didn't ask for, it can tell the difference and kills it.

IOW, this code was always wrong. It didn't crash in DOS mode only because DOS mode is physically incapable of detecting that problem. If you compiled this as a 32-bit Windows program it'd crash too.

If you want something that actually has memory, you can declare it as a buffer on the stack:

char buf[20];

or you can give the pointer something to point to like

char *buf=malloc(20);

...
// before main returns.  Especially important in DOS!
free(buf);

malloc() needs malloc.h in Turbo C++, or stdlib.h for compilers that aren't 30 years out of date.

The rest of your code looks fine. :slight_smile: You should check if fp and fp2 are NULL though -- if they didn't succeed in opening for some reason, your program goes ahead and tries to use them anyway, which will crash too -- or at least really ought to crash.

It might be better to program in Linux than DOS. Linux is a less forgiving environment, you'll catch some mistakes immediately which in DOS might not do anything immediately but could cause very weird side-effects later. Linux is even capable of detecting which line it crashed in if you compile with -ggdb and run the program with gdb...

1 Like

Hi.

Sometimes lint-like tools can catch these errors. For example, on Debian GNU/Linux, there is a utility splint. The output from running it on your code is:

% splint t1.c
Splint 3.1.2 --- 23 Aug 2008

t1.c:4:1: Function main declared to return void, should return int
  The function main does not match the expected type. (Use -maintype to inhibit
  warning)
t1.c: (in function main)
t1.c:13:16: Unallocated storage buf passed as out parameter to fgets: buf
  An rvalue is used that may not be initialized to a value on some execution
  path. (Use -usedef to inhibit warning)
t1.c:13:25: Possibly null storage fp passed as non-null param: fgets (..., fp)
  A possibly null pointer is passed as a parameter corresponding to a formal
  parameter with no /*@null@*/ annotation.  If NULL may be used for this
  parameter, add a /*@null@*/ annotation to the function parameter declaration.
  (Use -nullpass to inhibit warning)
   t1.c:11:8: Storage fp may become null
t1.c:16:23: Possibly null storage fp2 passed as non-null param:
               fputs (..., fp2)
   t1.c:12:9: Storage fp2 may become null
t1.c:7:8: Variable c declared but not used
  A variable is declared but never used. Use /*@unused@*/ in front of
  declaration to suppress message. (Use -varuse to inhibit warning)

Finished checking --- 5 code warnings

Best wishes ... cheers, drl

1 Like

Adding -Wall to gcc's commandline can cause it to warn you about a lot of them too.

1 Like

Thank you all for replying. Problem is solved. used malloc(20). Thanks for suggesting better debugging methods.

Arrgh! No! Not that! :wink:

Seriously, in order to be perfectly clear: while a pointer may be an integer-type value, a pointer is NOT an int, nor a long, nor an unsigned int or unsigned long. A pointer is a pointer. Just because it might be the same number of bytes as a some specfic integer-type variable for whatever architecture you're coding in, that doesn't mean that relationship will hold for other architectures.

Along the same lines, size_t is size_t, NOT unsigned int.

If you screw that up and get it ingrained into your programming practices, when you move to another architecture (Can you say "64 bits"? Thank you, I knew you could!), you'll have some serious problems.

1 Like

And nowhere is this more true than Borland Turbo C++, which has several kinds of pointers, of differing sizes, due to 16-bit segment weirdness.

1 Like