**Overflow**

Using `int` can have trouble with integers as small as 64k. Consider a version using a time stamp of 14 digits as one of the fields `20220302101901`. Use some _unsigned_ type, perhaps `uintmax_t`.  Even then I would consider _detecting_ overflow.

**What if non-digit or non-dot?**

Since separators like `,`, and letters are treated pathologically like digits, how about giving those meaning as separators and not use them to computer the version value?

    // while (*v1 && *v1 != '.')  a = 10*a + (*v1++ - '0');
    while (isdigit(*v1))  a = 10*a + (*v1++ - '0');

Pedantically, should use `unsigned char` values with `is...()`

    while (isdigit(*(const unsigned char*)v1))  a = 10*a + (*v1++ - '0');

... or use `(*v1 >= '0' && *v1 <= '9')` if avoiding libraries.

**NULL?**

Since version compare is often done near the beginning of code and the result is _very_ important, I'd like the compare to tolerate more of the usual mistakes like `NULL` or effect some error message.

    int compareVersion(const char * v1, const char * v2) {
      if (v1 == NULL) v1 = "";
      if (v2 == NULL) v2 = "";
      ...