开发者

What can be done in c but not c++? [closed]

As it c开发者_如何学JAVAurrently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance. Closed 11 years ago. Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.

What are the things that can be done in C but not in C++, and which of these features do you miss the most while coding in C++?

Few things I can think of:

  1. We can assign any type of pointer to void pointer without cast in c but not in c++.
  2. Declare variable names that are keywords in C++ but not C ;)

EDIT : Thanks @sbi for pointing out:

1. should be: We can assign void pointer to any type of pointer in C but not in C++


Note: I guess I'll get flamed for this, but, then, it is a C++ question for C++ developpers, so...

What are the things that can be done in C but not in C++, and which of these features do you miss the most while coding in C++?

As a C++ developer, I miss nothing from C, be it C99 or otherwise.

I do not write this just out of spite. This is a question for C++ developers who miss some C/C99 features because they ignore basic features of C++. I do believe the question and its answers ignore viable or better alternatives in C++ (and no, the "C++ vector are nasty" comment is just a bogus reason).

This is why I will discuss here each one of the supposed "missing features"...

Variable-length arrays?

Variable length arrays is a language feature of C99. Its key advantages are:

  1. allocation on stack
  2. variable length on creation
  3. no need to deallocate

For the most common cases, std::vector will do the job, and have more features anyway. For example, unless I'm wrong, the variable-length array have the following disadvantages:

  1. allocation on the stack means you can't return a VLA from the function where it was declared
  2. the VLA can't be resized, meaning that if it's too small, then you're screwed
  3. VLA must be either declared at prototype scope, or block scope. It can't be extern, or static. And you can't declare it as a member of a struct.
  4. It cannot have an initializer

The vector can be resized, and can be returned. And with C++0x (and the r-value references), you can return a vector using move semantics, meaning no useless temporary object is needed. You can put it in a struct/class, it can be extern, or static. you can initialize it with a default value, the content of an array, a container, or, with C++0x, with an initializer list.

And even after that, if you really want something like the VLA, in C++, it is possible for an average C++ developer to write a stack-based vector-like container. And it would not need a full language committee update for that.

Just for the fun, I happened to post an answer with a simple proof-of-concept of a C++ VLA-like class.

C++'s vector is most of the time a better alternative, with more features. And in the rare case a VLA is really really needed, its features could be emulated by a user-defined class.

Casting void * into T *?

As for casting any void * into another typed pointer, this is not a feature of C missing from C++: This is a choice of weak-typing vs. strong-typing

And it's not as if it was impossible to do it in C++, as you can do it with a cast. The point of this difference is to decrease a bug risk in a language where void *is not as useful as in another: In my current C++ 100k lines project, I have zero occurrences of void *.

Designated initializers?

Constructors offers a better alternative.

Of course, you don't get the possibility of initializing directly the data in the struct, but then, data encapsulation means that most of the time, the data in my objects are private, so, the whole concept of using designated initializers to initialize them would be ridiculous.

As for POD-like structures, well, a constructor is easy to write, and can handle cases designated initializers won't ever do (like initializing members with non-zero values by default, or even calling functions).

Because C++'s focus on data encapsulation, constructors offer a better alternative to designated initializers.

Edit 2011-11-05:

After re-reading this section, I want to clarify one point: Designated initializers can be useful for very limited cases (i.e. PODs), meaning that while I don't miss them (as asked in the question), I wouldn't mind having them.

Compound Literals?

This syntactic sugar supposes, again, that you know both the exact implementation of the struct, and have a public access to its members, which is something you usually want to avoid in C++.

Once again, Compound Literals are not something that can't be handled by a function, method or even constructor, with the advantage of bonus features, as described above.

Declare variable names that are keywords in C++ but not C?

I know how you feel: Each time I have the possibility of using interface, final or synchronized in C++, I get Java shivers, too...

:-P

Type-generic macro?

The problem in C is that you have quite a bunch of functions, doing the same semantic thing to different types, meaning the each function must have a different name. For example, according to the OpenGroup, the following functions do exist:

  • double sin(double x);
  • float sinf(float x);
  • long double sinl(long double x);
  • etc.

Source: http://www.opengroup.org/onlinepubs/009695399/functions/sin.html

But their names are a real pain to remember, so someone had an idea. Something about a macro which would use a compiler built-in extension to call the right one according to the types of the used parameters.

This is all the magic of C99's <tgmath.h>.

And the idea seemed so awesome that they even added a proposition to offer this feature for all functions in the next C standard, with something like:

#define sin(x) __tgmath(x,,,     \
float, sinf, long double, sinl,  \
/* etc. */                       \
, , sin)(x)

Source: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1340.htm

Now, the shocking news: This features is available in C++ since decades: This is called function overloading.

For example, the functions above are declared in C++ as:

  • double sin (double x );
  • float sin (float x );
  • long double sin (long double x );
  • etc.

So, the "Type-generic macro" is a hacked implementation, which strives to (partially) emulate the more generic C++ function overloading.

And guess what: You can even add your own overloads for your own user-defined types.

Conclusion

As shown above, each time I study a C99 feature, the conclusion is: "Hey, I already could do that in C++!" (and usually with the word "better" somewhere in the sentence).

Seriously, as a C++ developer, what I miss right now is to be able to use C++0x at work. For example, the following C++0x features:

  • auto
  • constexpr
  • initialized lists
  • r-value references
  • lambdas
  • nullptr
  • etc.

The whole "C missing features from C++" is an overrated concept which, I suspect, is more interesting to C developers (and C-with-classes developers) than C++ developers.


You might find the webpage Incompatibilities Between ISO C and ISO C++ interesting.

I mostly miss a number of C99 features that are not in C++:

  • Compound literals;
  • Designated initializers;
  • Variable argument macros (included in C++0X).


Not a feature in C, but arguably a killer feature of C is that the simple syntax of C89 makes it easy to write a compiler. Well, compared to a C++ compiler anyway.


  1. We can assign any type of pointer to void pointer without cast in c but not in c++.

Any pointer is convertible to a void* in C++ as well. You're losing information that way and the compiler won't stop you from doing that. It's the opposite that's a problem, because that way you're gaining information that the compiler can't check.

I think C allows this, while C++ definitely does not.


If we ignore the obvious source of differences - C99 - restricting ourselves to C89/90, and also discard the trivial variants, like C++ keywords, there will still be some differences between C and C++

(1) You already mentioned the the ability to convert void * to any concrete pointer type without a cast.

(2) Function types with "unspecified" parameters, i.e. () in function type declaration. In C you can do this

void foo(int, int);
void bar(double);

int main() {
  void (*pf)();

  pf = foo;
  pf(1, 2); /* valid call */

  pf = bar;
  pf(5.0);  /* valid call */
}

This would not be possible in C++. Of course, one can also say that the general non-prototype function declarations is a feature of C, which is not present in C++ (applies to C99 as well).

(3) Some differences in array initialization with string literals: the trailing \0 is allowed to fall off in C, but not in C++

char str[2] = "ab"; /* valid C, not valid C++ */

(4) Tentative definitions in C, although they are mostly of no consequence.

(5) Another mostly inconsequential "feature": in C you can use value-returning functions that "forget" to actually return anything

int foo() {
}

The code is legal in both C and C++, but in C++ such function would unconditionally produce undefined behavior. In C the function would produce undefined behavior only if you actually attempted to use the returned value

foo(); /* fine in C, undefined behavior in C++ */

(6) Some other stuff I'll add later if I remember it.


You can have variable-length array in C but not in C++. I believe this will be quite useful instead of doing a new[] for this.


There are a few subtle differences in syntactic sugar and type abuse, but they're trivial to work around.

The most important capability of C is to generate completely free-standing programs with absolutely no external dependencies. This is why operating system kernels are almost universally written in C. C was actually designed for implementing operating systems. It is possible to write OS kernels in a restricted subset of C++, but enforcing those restrictions only happens at link time, if at all, so it's much more of a pain to deal with than the minor syntax differences.


In C, you can implicitly convert between void pointers and other pointers, but you must do an explicit conversion in C++.

void* void_ptr;
int* int_ptr;

int_ptr = void_ptr;  // Invalid in C++, but not in C
void_ptr = int_ptr;  // Valid in C and C++
void_ptr = (void*)int_ptr; // Valid in C and C++
int_ptr = (int*)void_ptr;  // Valid in C and C++


What i like in C is the ability to say look at something like a = b; and know exactly what it's doing. In C++, anyone can override operators, meaning a simple statement like that could end up calling some massive copy constructor (or worse, something totally irrelevant). You see a = b; in C++, you have to guess (or go and look up) whether someone's done that just to be annoying.


As a fan of C, I think there might be another point in the concept of coding style, and that would be functional programming in contrast to object oriented programming known as OOP! for those who write their codes as statemachines it is a very interesting concept! Consider opengl as a good example. Also the speed of the running code is very better in C because of lesser memory references. You might also enjoy to hear that many programmers like to write their codes in C because of simplicity of design and how they are used to it (might not be that easy on syntax). Oh, and when you want to write codes very near to hardware level you should use pure C.


One of the missed features of C99 is the VLA, for which C++ is supposed to have no equivalent.

Some even questioned the possibility of writting in C++ a VLA-like object.

This is why I added this answer: Despite being slightly out-of-topic, it still demonstrates that, with the right library, a C++ developer can still have access to objects mimicking C99 features. And thus, that the features are not so missed than believed.

The main code is:

#include <iostream>
#include <string>
#include "MyVLA.hpp"

template <typename T>
void outputVLA(const std::string & p_name, const MyVLA<T> & p_vla)
{
    std::cout << p_name << "\n   MyVla.size() : ["
              << p_vla.size() << "]\n" ;

    for(size_t i = 0, iMax = p_vla.size(); i < iMax; ++i)
    {
        std::cout << "   [" << i << "] : [" << p_vla[i] << "]\n" ;
    }
}

int main()
{
    {
        MY_VLA(vlaInt, 5, int) ;

        outputVLA("vlaInt: Before", vlaInt) ;

        vlaInt[0] = 42 ;
        vlaInt[1] = 23 ;
        vlaInt[2] = 199 ;
        vlaInt[3] = vlaInt[1] ;
        vlaInt[4] = 789 ;

        outputVLA("vlaInt: After", vlaInt) ;
    }

    {
        MY_VLA(vlaString, 4, std::string) ;

        outputVLA("vlaString: Before", vlaString) ;

        vlaString[0] = "Hello World" ;
        vlaString[1] = "Wazaabee" ;
        vlaString[2] = vlaString[1] ;
        vlaString[3] = "Guess Who ?" ;

        outputVLA("vlaString: After", vlaString) ;
    }
}

As you see, the MyVLA object knows its size (which is a lot better than the use of the sizeof operator on C99 VLAs).

And of course, the MyVLA class behaves like an array, and is initialized by a size_t value (which can change at runtime). The only glitch is due to the nature of the function alloca(), meaning the constructor should be used only indirectly, through the macro MY_VLA:

Below, the code for the class, in the file MyVLA.hpp

#include <alloca.h>

template <typename T>
class MyVLA
{
    public :
        MyVLA(T * p_pointer, size_t p_size) ;
        ~MyVLA() ;

        size_t          size()                          const ;
        const T &       operator[] (size_t p_index)     const ;
        T &             operator[] (size_t p_index) ;

    private :
        T * m_begin ;
        T * m_end ;
} ;

#define MY_VLA(m_name, m_size, m_type)                                                      \
m_type * m_name_private_pointer = static_cast<m_type *>(alloca(m_size * sizeof(m_type))) ;  \
MyVLA<m_type> m_name(m_name_private_pointer, m_size)

template <typename T>
inline MyVLA<T>::MyVLA(T * p_pointer, size_t p_size)
{
    m_begin = p_pointer ;
    m_end = m_begin + p_size ;

    for(T * p = m_begin; p < m_end; ++p)
    {
        new(p) T() ;
    }
}

template <typename T>
inline MyVLA<T>::~MyVLA()
{
    for(T * p = m_begin; p < m_end; ++p)
    {
        p->~T() ;
    }
}

template <typename T>
inline size_t MyVLA<T>::size() const
{
    return (m_end - m_begin) ;
}

template <typename T>
inline const T & MyVLA<T>::operator[] (size_t p_index) const
{
    return *(m_begin + p_index) ;
}

template <typename T>
inline T & MyVLA<T>::operator[] (size_t p_index)
{
    return *(m_begin + p_index) ;
}

The macro is a mess, and could probably be better written. The class itself is probably not exception-safe, but it could be made to be. Anyway, it would need more code to be usable (i.e. handle the copy/assignment, make new/delete private, adding const wherever possible, etc.). My guess is that the end does not justify the time I would spend on it. So it will remain a proof-of-concept.

The point being "C++ can emulate C99 VLAs, and it could work even as a C++ objects array!", I guess I succeeded to demonstrate that.

I let the reader copy-paste-compile the code to see the results (I compiled it on a g++ 4.4.3, on Ubuntu 10.04.


In C you can define a variable's name to delete. You cannot do that in C++.


The main difference between C and C++ is that C++ is object oriented while C is function or procedure oriented. Object oriented programming paradigm is focused on writing programs that are more readable and maintainable. It also helps the reuse of code by packaging a group of similar objects or using the concept of component programming model. It helps thinking in a logical way by using the concept of real world concepts of objects, inheritance and polymorphism. It should be noted that there are also some drawbacks of such features. For example using polymorphism in a program can slow down the performance of that program.

On the other hand, functional and procedural programming focus primarily on the actions and events, and the programming model focuses on the logical assertions that trigger execution of program code.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜