1

I have a question about my code. This is my answer to programming project 1 in Chapter 10 of KNK's C Programming a Modern Approach (2nd Ed.).

The question is asking us to write a stack to see if a series of parentheses and/or braces are properly nested. The question explicitly asks the student to modify the code in 10.2 - an earlier part of the chapter. My compiler is giving me the error warning control may not reach end of non-void function. The compiler is referring to the calling of the char pop(void) function.

My only issue is this I don't see what is wrong with

 char pop(void)
{
  if (is_empty())
     stack_underflow();
  else
    return contents[--top];
}

For if the stack pointer returns to zero then the program terminates and if not then a character is returned, namely contents[--top]. Not only that - this is exactly how the example in section 10.2 instructs the student to write the pop function. So it would seem to suggest KNK got it wrong unless it's an issue with a new version of C (the book uses C99). Clearly I'm missing something.

As a result I'm not even sure if my current code is correct as I cannot even run it in the first place.

Ideas for where I'm going wrong would be appreciated.


#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define STACK_SIZE 100

/* function declarations */
bool is_empty(void);
bool is_full(void);
void push(char);
char pop(void);
void stack_overflow();
void stack_underflow();

/* external variables */
char contents[STACK_SIZE] = {0};
int top = 0;

int main(void)
{ 
  char ch;
  
  printf("Enter parentheses and/or braces: ");
  scanf("%c", &ch);
  
  while(ch != '\n')
  { 
    if(ch == '{' || ch == '(')  push(ch);
    else if(ch == '}' && pop() != '{')
    { 
      printf("Parentheses/braces are not nested properly\n");
      return 0;
    }
    else if(ch == ')' && pop() != '(')
    { 
      printf("Parentheses/braces are not nested properly\n");
      return 0;
    }
    scanf("%c", &ch);
  }
  
  if(is_empty())
    printf("Parentheses/braces are nested properly\n");
  else
    printf("Parentheses/braces are not nested properly\n");

  return 0;
}

bool is_empty(void)
{
  return top == 0;
}

bool is_full(void)
{
  return top == STACK_SIZE;
}

void push(char ch)
{
  if (is_full())
    stack_overflow();
  else
    contents[top++] = ch;
}

char pop(void)
{
  if (is_empty())
     stack_underflow();
  else
    return contents[--top];
}

void stack_overflow(void)
{
  printf("Stack overflow\n");
  exit(0);
}

void stack_underflow(void)
{
  printf("Parentheses/braces are not nested properly\n");
  exit(0);
}

tommie997
  • 129
  • 6
  • 1
    If you declare a function to return a value, you must *always* actually return a value. – Some programmer dude May 05 '23 at 22:14
  • Does that mean I need to write an arbitrary return value under stack_underflow()? Also I know in int main() you can technically leave out the return 0; so I'm not 100% sure what the rule is - maybe main is just an exception. – tommie997 May 05 '23 at 22:28
  • [Why does flowing off the end of a non-void function without returning a value not produce a compiler error?](https://stackoverflow.com/q/1610030) – 001 May 05 '23 at 22:36
  • 3
    `stack_underflow()` calls `exit()` and therefore never returns. You could either use a compiler extension like gcc's `__attribute((noreturn))__` on that function, or add a dummy `return` that never gets executed. – pmacfarlane May 05 '23 at 22:47
  • 1
    As an alternative, just remove the `else` keyword from the `if` (but keep the `return`). – Some programmer dude May 06 '23 at 06:24
  • Thank you @Someprogrammerdude that seems to make sense as well. – tommie997 May 06 '23 at 23:55
  • And thank you @pmacfarlane! The idea of using a compiler extension is definitely not mentioned in the book but I have used a dummy return that never gets executed. I'm learning more about C every day... – tommie997 May 06 '23 at 23:57

1 Answers1

2

The function says it's returning a char but if is_empty() then you don't return anything. Here are a few options:

  1. Return a dummy value that is never reached:
 char pop(void) {
  if (is_empty()) {
     stack_underflow();
     return '\0'; // silence warning; never reached
  }
  return contents[--top];
}
  1. Change the interface so you can tell caller about an error instead of existing:
typedef enum stack_error {
     STACK_OK,
     STACK_OVERFLOW,
     STACK_UNDERFLOW
} stack_error;

stack_error pop(char *output);

If you don't like how it now looks:

char ch;
if(pop(&ch != OK) {
    // ...
}

You can wrap that in a macro but it gives caller the freedom to select what behavior they want.

  1. gcc has support for an extension to tell the compiler that stack_overflow() doesn't return and it does silence the warning with your code as is:
void stack_underflow() __attribute__ ((noreturn));
  1. With C 2017 you can use the _Noreturn function specifier, and if you include <stdnoreturn.h> noreturn. It, too, silence the warning. This would be my preferred option at this point in time.
_Noreturn stack_underflow();
  1. With the upcoming C 2023, _Noreturn is obsolescent feature (6.7.12.6) and you instead use the syntax:
[[noreturn]] stack_underflow();

As an aside I would avoid the global variables and store those in a struct. Maybe even go the extra step of hiding the implementation entirely. I also suggest you use a "stack_" namespace prefix:

#include <stdlib.h>

typedef struct stack stack;
stack *create_stack(size_t size);
char stack_pop(stack *s);
// ...
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • 1
    Thank you, we have not covered structs yet and the book uses C99 so anything in C2017 or C2023 is not expected in the solution but thank you for your thorough answer! – tommie997 May 06 '23 at 23:51