Using the uninitialized pointer s for accessing memory
struct stack* s;
s->size = 5;
s->top = -1;
invokes undefined behavior.
You could allocate an object of the type struct stack dynamically and assign its address to the pointer s. But there is no great sense to allocate the object dynamically.
It is simpler to write
struct stack s = { 5, -1, malloc( 5 * sizeof(int) ) };
Then the function isEmpty that can also be defined simpler like
int isEmpty( const struct stack *ptr )
{
return ptr->top == -1;
}
is called like
if ( isEmpty( &s ) ) {
When the stack will not be required any more you need to free the allocated memory for the integer array like
free( s.arr );
Pay attention to that it is much better to declare the structure stack using a unsigned integer type for the data members size and top. For example
struct stack
{
size_t size;
size_t top;
int *arr;
};
To allocate an object of the structure type dynamically you could write a separate function as for example
struct stack * create_stack( size_t size )
{
struct stack &s = malloc( sizeof( *s ) );
if ( s != NULL )
{
s->size = 0;
s->top = 0;
s->arr = NULL;
if ( size != 0 )
{
s->arr = malloc( size * sizeof( int ) );
if ( s->arr != NULL )
{
s->size = size;
}
else
{
free( s );
s = NULL;
}
}
}
return s;
}
and in main you can write
struct stack *s = create_stack( 5 );
//...
In this case the function isEmpty will look like
int isEmpty( const struct stack *ptr )
{
return ptr->top == 0;
}
and a similar function as for example isFull will look like
int isFull( const struct stack *ptr )
{
return ptr->top == ptr->size;
}
The functions are called in main like
if ( isEmpty( s ) ) {
//...
and
if ( isFull( s ) ) {
//...