Check if a given string matches a wildcard pattern
The code you wrote can be improved in lots of places. I'll go through it from top to bottom.
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

Perfect until now. The #include lines from the C standard library should be in alphabetic order.  I cannot say whether you did this intentionally or not since there's a 50-50 chance. :)
int compareHelper(char *s, char *regex, int star);

This function should be declared as static, since it is local to this file. To do this, replace the int compareHelper with static int compareHelper.
The compare in the function name is wrong. Typically, to compare two things, they have to be of the same or at least a compatible type. The technical type here is char *, but on a more abstract level, s is an arbitrary string while regex is a string with some constraints. Instead of compare, this function should be called match.
The type of the string parameters should be const char * instead of char * since this function does not modify the characters of these strings.
The type of the parameter star should be bool instead of int. To get this type, you have to add #include &lt;stdbool.h&gt; along with the other #include lines.
The name of the parameter regex is wrong. The word regex means regular expression, and this is a fixed term with a precise definition. Your patterns are not regular expressions, therefore the parameter name confuses the reader. Better call it pattern.
int compareStrings(char *s, char *r);

Again, the compare should rather be match, the parameter types should get an additional const, and the return type should be bool instead of int.
int main()
{
    printf(&quot;%d\n&quot;, compareStrings(&quot;aaaacbbb&quot;, &quot;a*b&quot;));
    printf(&quot;%d\n&quot;, compareStrings(&quot;aaaabccccccccb&quot;, &quot;a*b*c&quot;));
    return 0;
}

The empty parentheses () in the function declaration of main mean that the function can take any number of parameters. In C, this is only allowed for legacy code from 1990 and earlier. In modern code, write (void) instead to clearly say that there must be no parameters.
//aaaaacbbb
//a*b

This is the beginning of documentation, which is a good idea. To be useful for other readers, you should write in whole sentences though, for example:

// See if the string s matches the pattern p.
// In the pattern, '*' matches an arbitrarily long sequence of arbitrary characters.


int compareHelper(char *s, char *regex, int star)
{
    if (*s == '\0' &amp;&amp; *regex == '\0')
    {
        return 1;
    } else if (*regex == '*')

After the return 1, the else is unnecessary. I would write the code like this instead:

    if (*s == '\0' &amp;&amp; *regex == '\0') {
        return 1;
    }

    if (*regex == '*') {
        ...


This way, your code has one topic per paragraph. The empty line between the topics clearly marks a pointer where the reader may stop reading and take a deep breath, before starting to read the next paragraph.
The rest of the code looks fine, except for this line:
return compareHelper(s + 1, regex + 1, 0) | compareHelper(s + 1, regex, 1);

The operator | means a logical or, and it always evaluates both operands. This means that compareHelper is called twice, even if the left-hand side already evaluates to true. This is not necessary. To only evaluate the necessary parts of the condition, replace the | operator with ||.
All in all, the code is not too long. What makes it look verbose is that you put each { or } on its own line. You can rewrite it like this:
#include &lt;stdbool.h&gt;

int compareHelper(const char *s, const char *regex, bool star)
{
    if (*s == '\0' &amp;&amp; *regex == '\0')
        return true;

    if (*regex == '*')
        return compareHelper(s, regex + 1, 1); // star is seen hence will be set to true.

    if (!star) {
        if (*s != *regex)
            return false; // star was not seen and they differ in char
        else
            return compareHelper(s + 1, regex + 1, star);
    }

    // seen a star; look for the text after the star
    while (*s &amp;&amp; *s != *regex)
        s++;

    if (*s == '\0')
        return 0;

    return compareHelper(s + 1, regex + 1, 0) || compareHelper(s + 1, regex, 1);
}

Some people will complain that &quot;you must always use braces in if and while statements&quot;, but that's not necessary. If you properly indent your code or let your IDE format the code automatically, the indentation properly expresses the relation of the code pieces to each other. The typical example is:
if (condition)
    goto fail;
    goto fail;

Modern compilers not only translate the source code to machine code, they also check for various possible bugs in the code. Wrong indentation like in the above snippet is one of them.
And here's a complete implementation that also matches character ranges like [A-Z]. The code looks remarkably similar to yours, which is good. If you take that code and remove the part that handles the [A-Z] part, it will be very similar to your code. Actually, taking other people's code and editing it just trying to understand it is an efficient way of thinking about why each of these lines of code is actually necessary.
While reading that code, you may notice that your code might fail for str_match(&quot;a good game&quot;, &quot;a *game&quot;). You should verify that the search after the star looks at every possible position in the string, not only the first time the letter g appears.