C Programming: seg faults, printf, and related quirks [duplicate]
Asked Answered
P

7

3

As many young programmers do, I learned the usefulness of inserting numerous print-to-console statements of "here1," "here2," and so on at different points in code to figure out when my programs are going awry. This brute force debugging technique has saved me many, many times throughout my CS studies. However, when I started programming in C, I stumbled onto an interesting problem. If I were to try and run

void* test;

printf("hello world");
test[5] = 234;

Of course I get a segfault for not malloc'ing memory for testChar. However, you would think logically that "hello world" would be printed before the seg fault happens, since that is the flow of the code, but in my experience, it is always the case that the seg fault happens first, and "hello world" is never printed to the console at all. (I wasn't able to test this exact example, but I have run into this sort of situation many times using gcc on a linux box.) I'm guessing this has to do with either the compiler rearranging some things and/or printf using some sort of buffer that is flushed asynchronously and therefore not being immediate. This is entirely speculation on my part because I honestly don't know why it happens. In any other language that I have used, no matter what problem the "testChar =..." line caused, the "hello world" would still be printed, and thus I could determine where the problem is.

My question is why does this happen when I'm programming C? Why isn't the hello world printed first? And secondly, is there a better C programming debugging technique than this that accomplishes the same basic thing? As in, an easy/intuitive way to find the line of code that is a problem?

Edit: I gave a working example by accident haha. What I have now should cause a segfault. It's funny how usually when I don't want a segfault I get one, and now when I actually wanted one I wrote legal code!

Pycnidium answered 10/6, 2009 at 13:7 Comment(0)
I
10

The code you posted is perfectly legal and should not cause a segfault - there is no need to malloc anything. Your problem must lie somewhere else - please post the smallest example of code that causes the problem.

Edit: You have now edited the code to have a totally different meaning. Still, the reason that "hello world" is not displayed is that the the output buffer has not been flushed. Try addinig

fflush( stdout );

after the printf.

Regarding locating the source of the problem you have a couple of choices:

  • liberally sprinkle printfs through your code using the __FILE__ and __LINE__ C macros
  • learn to use your debugger - if your platform supports core dumps, you can use the core image to find where the error is.
Indra answered 10/6, 2009 at 13:9 Comment(4)
I was think that. scratches head though I was going mental.Tsai
Sorry about that lol, I fixed my code so that it's a real problem. I needed anything that would cause a segfault.Pycnidium
"oh crap this is bad" will be stored in symbols area(?) and pointer is stored in testChar ... you can see it with debugger.Tergal
Though I marked this answer as accepted, I would also like to point out stefanB's answer below which gives great advice on how to use a specific debugger to find segfaults: #975948Pycnidium
S
5

printf writes to stdout, which is buffered. Sometimes that buffer doesn't get flushed before your program crashes so you never see the output. Two ways to avoid this:

  1. use fprintf( stderr, "error string" ); since stderr is not buffered.
  2. add a call to fflush( stdout ); after the printf call.

As Neil and others have said, the code as written is fine. That is, until you start modifying the buffer that testChar points to.

Skelp answered 10/6, 2009 at 13:12 Comment(2)
Why do I not have to malloc for the char poitner? In a sense, is the compiler automatically malloc'ing memory somehwere for the "oh crap this is bad" string and then pointing testChar to it?Pycnidium
Basically, yes, except that the memory it allocates for the string is contained in the program itself and is read-only. If you try to modify it, your application will crash.Skelp
T
5

"As in, an easy/intuitive way to find the line of code that is a problem?"

Use gdb (or any other debugger).

To find where your program seg faults you compile it with -g option (to include debugging symbols) run your application from gdb, it will stop on seg fault.

You can then look at backtrace with bt command to see at which point you got the seg fault.

example:

> gdb ./x
(gdb) r
Starting program: /proj/cpp/arr/x 
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000000
0x000019a9 in willfail () at main.cpp:22
22          *a = 3;
(gdb) bt
#0  0x000019a9 in willfail () at main.cpp:22
#1  0x00001e32 in main () at main.cpp:49
(gdb) 
Tergal answered 10/6, 2009 at 13:14 Comment(0)
R
3

The output is buffered by default, the segfault occurs before the output is actually written to stdout. Try:

fprintf(stderr, "hello, world\n");

(stderr is unbuffered by default.)

Reverential answered 10/6, 2009 at 13:14 Comment(0)
S
1

This code shouldn't segfault. You're just assigning a pointer to a literal string to a pointer variable. Things would be different if you were e.g. using strcpy to copy stuff with an invalid pointer.

The message not appearing may be due to buffered I/O. Print a newline character \n or call fflush to flush the output buffer.

Suffragist answered 10/6, 2009 at 13:11 Comment(1)
Just adding \n doesn't flush the buffer. You need to call fflush manually to do it.Skelp
N
0

You have two problems. The first is that your (original) code won't segfault. It's perfectly valid to assign that string constant to a char pointer. But let's leave that aside for now and pretend you had put something there that would segfault.

Then it's usually a matter of buffers, the one in the C runtime library and the one in the OS itself. You need to flush them.

The easiest way to do that was (in UNIX, not entirely certain about the fsync in Linux but you should be guaranteed that this wil happen eventually unless the system itself goes down):

printf ("DEBUG point 72\n"); fflush (stdout); fsync (fileno (stdout));

I've done this often in UNIX and it ensures that the C runtime libraries are flushed to UNIX (fflush) and the UNIX buffers are sync'ed to disk (fsync), useful if stdout is not a terminal device or you're doing it for a different file handle.

Nosedive answered 10/6, 2009 at 13:12 Comment(0)
U
0
void* test;

printf("hello world");
test[5] = 234;

Its likely that "hello world" is being buffered by the system somewhere and is not immediately printed to the screen. Its stored waiting for a chance for whatever process/thread/whatever is in charge of screen writing can have a chance to process it. And while its waiting (and possibly buffering other data to output) you're function is finishing. It comes over the illegal access and segfaults.

Unmoor answered 10/6, 2009 at 13:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.