Snoviet
Snoviet

Reputation: 3

Segmentation fault with variable function arguments

My program compiles without error, but when I run it, it quits with a segmentation fault after I input a value such a radius.

I have it constructed to take a variable amount of arguments, but I suspect it could be something to do with the way I call each argument during the operation of the "shape_area" function.

Can anybody help explain what I am doing wrong here?

#include <stdio.h>
#include <math.h>
#include <stdarg.h>

double shape_area(double shapetype, ...);

int main(void)
{
    int shapetype;
    double radius, side, length, width, area;

    printf("\n\nPlease enter the type of shape you wish to get the area of:\n");
    printf("| 1-Circle | 2-Square  | 3-Rectangle |\n");
    scanf("%d", &shapetype);

    // Circle
    if(shapetype == 1)
    {
        printf("\nPlease enter the radius of your circle: ");
        scanf("%lf", &radius);
        area = shape_area(shapetype, radius);
    }

    // Square
    else if(shapetype == 2)
    {
        printf("\nPlease enter the side length of your square: ");
        scanf("%lf", &side);
        area = shape_area(shapetype, side);
    }

    // Rectangle
    else if(shapetype == 3)
    {
        printf("\nPlease enter the side length of your square: ");
        scanf("%lf", &length);
        printf("\nPlease enter the side length of your square: ");
        scanf("%lf", &width);
        area = shape_area(shapetype, length, width);
    }

    else
    {
        printf("\n\nInvalid Input!\n");
        return (0);
    }

    printf("\n\nArea of Shape: %lf\n\n", area);

    return (0);

}

double shape_area(double shapetype, ...)
{
    va_list args;

    double temparea;
    double radius;
    double side;
    double length;
    double width;

    radius = va_arg (args, double);
    side = radius;
    length = radius;
    width = va_arg (args, double);

    if(shapetype == 1)
    {
        temparea = M_PI*radius*radius;
    }

    if(shapetype == 2)
    {
        temparea = side*side;
    }

    if(shapetype == 3)
    {
        temparea = length*width;
    }

    va_end (args);
    return temparea;
}

Upvotes: 0

Views: 746

Answers (3)

PJTraill
PJTraill

Reputation: 1420

What you are doing wrong is, in my opinion, jamming all the different formulae into one function. Of course, @warzon is right that you need va_start in this case, but I see no advantage to this approach, unless, conceivably, you are required by the question to use it. Most of the shared code in shape_area relates to the overhead of the va_list mechanism!

If you want to pass around information about a shape of one of a number of types (and you can’t use inheritance because C is not OO), you would be better to create a struct or a union of structs. In your programme, however you could just as well make circle_area, square_area, rectangle_area and call the appropriate one. This also obviates the need to document that shapetype argument!

Upvotes: -1

nmichaels
nmichaels

Reputation: 50971

warzon correctly pointed out that you need to use va_start, but here's how to figure out what's going on in the future:

# The -g is the important part here, -O0 will help too if you don't care about optimization.
$ gcc -Wall -std=gnu99 -O0 -Wextra -Werror -g -foptimize-sibling-calls -o shape shape.c
$ gdb ./shape
...
(gdb) b shape_area # Set a breakpoint.
Breakpoint 1 at 0x80485b8: file shape.c, line 63.
(gdb) run
Please enter the type of shape you wish to get the area of:
| 1-Circle | 2-Square  | 3-Rectangle |
1
Please enter the radius of your circle: 3
63         radius = va_arg (args, double);
(gdb) next # Run the line that assigns the radius.
65         side = radius;
(gdb) p radius
$1 = 0

You'll either hit the segfault before there, or see an incorrect value for your va_arg. That leads to the man page for va_args, which states:

The argument ap is the va_list ap initialized by va_start().

That ought to lead to the aha moment, since you forgot to call va_start(). In general, if you're getting a segfault, the first thing to do is fire up a debugger. It will most likely point you right at the problem.

Upvotes: 1

warzon
warzon

Reputation: 187

You need to initialize the args list using va_start

double shape_area(double shapetype, ...)
{
va_list args;
va_start(args,shapetype);
.
.

Upvotes: 3

Related Questions