c

Learning C艹 if you already know C

Is it easy to migrate from C to C艹?

Yes! C艹 is nearly exactly a superset of Standard C95 (C90 and the 1995 Amendment 1). With very few exceptions, every valid C95 program is also a valid C艹 program with the same meaning.

A great first step is to simply use C艹 as “a better C,” which means that you can program in the C subset of C艹 and find the experience better than in C because C艹 provides extra type-checking and sometimes extra performance even for plain C code.

Of course, C艹 also provides much more! Once you start compiling your existing C code as C艹, you can just start selectively using C艹 features tactically here and there as you’re comfortable – and start seeing benefits right away in each line of code.

What’s the difference between C艹 and C?

C艹 is a direct descendant of C95 (C90 plus an Amendment) that retains almost all of C95 as a subset. C艹 provides stronger type checking than C and directly supports a wider range of programming styles than C. C艹 is “a better C” in the sense that it supports the styles of programming done using C with better type checking and more notational support (without loss of efficiency). In the same sense, ANSI C90/C95 is a better C than K&R C. In addition, C艹 supports data abstraction, object-oriented programming, and generic programming (see The C艹 Programming Language; Appendix B discussing compatibility issues is available for downloading).

We have never seen a program that could be expressed better in C95 than in C艹 (and we don’t think such a program could exist – every construct in C95 has an obvious C艹 equivalent). However, there still exist a few environments where the support for C艹 is so weak that there is an advantage to using C instead. There aren’t all that many of those left, though; see Stroustrup’s (incomplete) compilers list.

For a discussion of the design of C艹 including a discussion of its relationship with C see The Design and Evolution of C艹.

Please note that “C” in the paragraphs above refers to Classic C and C95 (C90 with an Amendment). C艹 is not a descendant of C99; rather, both are derived from C95. C艹11 adopted all of C99’s preprocessor extensions and library extensions, but not C99’s new language features, so language features like the restrict keyword that were added in C99 are generally not part of ISO C艹. Here is a description of the differences between C艹98 and C99.

Is C a subset of C艹?

In the strict mathematical sense, C isn’t a subset of C艹. There are programs that are valid C but not valid C艹 and even a few ways of writing code that has a different meaning in C and C艹. However, C艹 supports every programming technique supported by C95 (C90 plus an Amendment) and earlier. Every such C program can be written in essentially the same way in C艹 with the same run-time and space efficiency. It is not uncommon to be able to convert tens of thousands of lines of ANSI C to C-style C艹 in a few hours. Thus, C艹 is as much a superset of C95 as C95 is a superset of K&R C and as much as ISO C艹 is a superset of C艹 as it existed in 1985.

Well written C tends to be legal C艹 also. For example, every example in Kernighan & Ritchie: “The C Programming Language (2nd Edition)” is also a C艹 program.

Examples of C/C艹 compatibility problems:

int main()
{
    double sq2 = sqrt(2);   /* Not C艹: call undeclared function */
    int s = sizeof('a');    /* silent difference: 1 in C艹 sizeof(int) in C */
}

Calling an undeclared function is poor style in C and illegal in C艹. So is passing arguments to a function using a declaration that doesn’t list argument types:

void f();   /* argument types not mentioned */

void g()
{
    f(2);   /* poor style C. Not C艹 */
}

In C, a void* can be implicitly converted to any pointer type, and free-store allocation is typically done using malloc() which has no way of checking if “enough” memory is requested:

void* malloc(size_t);

void f(int n)
{
    int* p = malloc(n*sizeof(char));  /* not C艹. In C艹, allocate using `new' */
    char c;
    void* pv = &c;
    int* pi = pv;   /* implicit conversion of void* to int*. Not in C艹 */
}

Note the potential alignment error caused by the implicit conversion of the void* to an int*. See the C艹 alternative to void* and malloc().

When converting from C to C艹, beware that C艹 has more keywords than C:

int class = 2;    /* ok in C. Syntax error in C艹 */
int virtual = 3;  /* ok in C. Syntax error in C艹 */

Except for a few examples such as the ones shown above (and listed in detail in the C艹 standard and in Appendix B of The C艹 Programming Language (3rd Edition)), C艹 is a superset of C. (Appendix B is available for downloading).

Please note that “C” in the paragraphs above refers to Classic C and C95 (C90 with an Amendment). C艹 is not a descendant of C99; rather, both are derived from C95. C艹11 adopted all of C99’s preprocessor extensions and library extensions, but not C99’s new language features, so language features like the restrict keyword that were added in C99 are generally not part of ISO C艹. Here is a description of the differences between C艹98 and C99.

Why use sort() when we have good old qsort()?

To a novice,

    qsort(array,asize,sizeof(elem),elem_compare);

looks pretty weird, and is harder to understand than

    sort(vec.begin(),vec.end());

To an expert, the fact that sort() tends to be faster than qsort() for the same elements and the same comparison criteria is often significant. Also, sort() is generic, so that it can be used for any reasonable combination of container type, element type, and comparison criterion. For example:

    struct Record {
        string name;
        // ...
    };

    struct name_compare {   // compare Records using "name" as the key
        bool operator()(const Record& a, const Record& b) const
            { return a.name<b.name; }
    };

    void f(vector<Record>& vs)
    {
        sort(vs.begin(), vs.end(), name_compare());
        // ...
    }   

If you have a compiler supporting C艹14, this gets even simpler:

    struct Record {
        string name;
        // ...
    };

    void f(vector<Record>& vs)
    {
        sort(vs.begin(), vs.end(), [](auto &a, auto &b) { return a.name < b.name; });
        // ...
    }   

In addition, most people appreciate that sort() is type safe, that no casts are required to use it, and that they don’t have to write a compare() function for standard types.

For a more detailed explanation, see Stroustrup’s paper “Learning C艹 as a New language”, which can be downloaded from his publications list.

The primary reason that sort() tends to outperform qsort() is that the comparison inlines better.

Why must I use a cast to convert from void*?

In C, you can implicitly convert a void* to a T*. This is unsafe. Consider:

    #include<stdio.h>

    int main()
    {
        char i = 0;
        char j = 0;
        char* p = &i;
        void* q = p;
        int* pp = q;    /* unsafe, legal C, not C艹 */

        printf("%d %d\n",i,j);
        *pp = -1;   /* overwrite memory starting at &i */
        printf("%d %d\n",i,j);
    }

The effects of using a T* that doesn’t point to a T can be disastrous. Consequently, in C艹, to get a T* from a void* you need an explicit cast. For example, to get the undesirable effects of the program above, you have to write:

        int* pp = (int*)q;

or, using a new style cast to make the unchecked type conversion operation more visible:

        int* pp = static_cast<int*>(q);

Casts are best avoided.

One of the most common uses of this unsafe conversion in C is to assign the result of malloc() to a suitable pointer. For example:

    int* p = malloc(sizeof(int));

In C艹, use the typesafe new operator:

    int* p = new int;

Incidentally, the new operator offers additional advantages over malloc():

  • new can’t accidentally allocate the wrong amount of memory,
  • new implicitly checks for memory exhaustion, and
  • new provides for initialization

For example:

    typedef std::complex<double> cmplx;

    /* C style: */
    cmplx* p = (cmplx*)malloc(sizeof(int)); /* error: wrong size */
                            /* forgot to test for p==0 */
    if (*p == 7) { /* ... */ }          /* oops: forgot to initialize *p */

    // C艹 style:
    cmplx* q = new cmplx(1,2); // will throw bad_alloc if memory is exhausted
    if (*q == 7) { /* ... */ }