Что можно сделать в c, но не С++?

Каковы вещи, которые можно сделать в C, но не на С++, и какие из этих функций вы пропускаете больше всего при кодировании на С++?

Немногие вещи, о которых я могу думать:

  • Мы можем назначить любой тип указателя на указатель void без добавления в c, но не в С++.
  • Объявлять имена переменных, которые являются ключевыми словами в С++, но не C;)

EDIT: Спасибо @sbi за указание:
1. должно быть: мы можем назначить указатель void любому типу указателя в C, но не в С++

Ответ 1

Примечание. Думаю, я поплачу за это, но тогда это вопрос на С++ для разработчиков С++, поэтому...

Каковы вещи, которые можно сделать в C, но не на С++, и какие из этих функций вы пропускаете больше всего при кодировании на С++?

Как разработчик С++, я ничего не пропускаю от C, будь то C99 или иначе.

Я не пишу это просто из злости. Это вопрос для разработчиков на С++, которые пропускают некоторые функции C/C99, потому что игнорируют основные возможности С++. Я действительно верю, что вопрос и его ответы игнорируют жизнеспособные или лучшие альтернативы в С++ (и нет, комментарий "С++ vector - отвратительный" - просто ложная причина).

Вот почему я буду обсуждать здесь каждую из предполагаемых "недостающих функций"...

Массивы переменной длины?

Массивы переменной длины - это языковая функция C99. Его основными преимуществами являются:

  • распределение в стеке
  • переменная длина при создании
  • нет необходимости освобождать

Для наиболее распространенных случаев std::vector выполнит эту работу и будет иметь больше функций. Например, если я ошибаюсь, массив переменной длины имеет следующие недостатки:

  • выделение в стеке означает, что вы не можете вернуть VLA из функции, где она была объявлена ​​
  • VLA не может быть изменен, что означает, что если он слишком мал, то вы ввернуты
  • VLA должен быть объявлен в области прототипа или в блочной области. Он не может быть внешним или статическим. И вы не можете объявить его как член структуры.
  • У него не может быть инициализатор

Вектор может быть изменен и может быть возвращен. И с С++ 0x (и ссылками r-значения) вы можете вернуть вектор, используя семантику перемещения, что означает, что не нужен бесполезный временный объект. Вы можете поместить его в struct/class, он может быть extern или static. вы можете инициализировать его значением по умолчанию, содержимым массива, контейнером или с С++ 0x с списком инициализаторов.

И даже после этого, если вы действительно хотите что-то вроде VLA, на С++, средний разработчик С++ может писать векторный векторный контейнер на основе стека. И для этого не потребуется полное обновление языкового комитета.

Просто для удовольствия, я случайно отправил ответ с простой доказательством концепции класса VLA, подобного С++.

С++ вектор - это большая альтернатива, с большим количеством функций. И в редком случае VLA действительно действительно необходимо, его функции могут эмулироваться определяемым пользователем классом.

Литье void * в T *?

Что касается приведения любого void * в другой типизированный указатель, это не является признаком C, отсутствующим в С++: Это выбор метода слабопечатывания и сильной типизации

И это не так, как если бы это было невозможно сделать на С++, так как вы можете сделать это с помощью броска. Точка этого различия заключается в уменьшении риска ошибки на языке, где void * не так полезен, как в другом: в моем текущем проекте строк С++ 100k у меня есть нулевые вхождения void *.

Обозначенные инициализаторы?

Конструкторы предлагают лучшую альтернативу.

Конечно, вы не получаете возможность инициализировать непосредственно данные в структуре, но тогда инкапсуляция данных означает, что большую часть времени данные в моих объектах являются частными, поэтому вся концепция использования назначенных инициализаторы для их инициализации были бы смешными.

Что касается POD-подобных структур, то конструктор легко писать и может обрабатывать случаи, которые назначенные инициализаторы никогда не будут делать (например, инициализация членов с ненулевыми значениями по умолчанию или даже вызывающие функции).

Поскольку С++ фокусируется на инкапсуляции данных, конструкторы предлагают лучшую альтернативу назначенным инициализаторам.

Изменить 2011-11-05:

После повторного чтения этого раздела я хочу уточнить одну точку: назначенные инициализаторы могут быть полезны для очень ограниченных случаев (т.е. POD), что означает, что, хотя я их не пропущу (по просьбе в вопросе), я бы не прочь их иметь.

Составные литералы?

Этот синтаксический сахар снова предполагает, что вы знаете как точную реализацию структуры, так и публичный доступ к ее членам, чего вы обычно хотите избежать в С++.

Еще раз, Compound Literals - это не то, что невозможно обработать функцией, методом или даже конструктором, с преимуществом бонусных функций, как описано выше.

Объявлять имена переменных, являющиеся ключевыми словами в С++, но не C?

Я знаю, как вы себя чувствуете: каждый раз, когда у меня есть возможность использовать interface, final или synchronized в С++, я получаю также Java дрожь...

: - P

Типовой макрос?

Проблема в C состоит в том, что у вас есть довольно много функций, выполняющих одну и ту же семантическую вещь для разных типов, то есть каждая функция должна иметь другое имя. Например, согласно OpenGroup, существуют следующие функции:

  • double sin(double x);
  • float sinf(float x);
  • long double sinl(long double x);
  • и др.

Источник: http://www.opengroup.org/onlinepubs/009695399/functions/sin.html

Но их имена - настоящая боль, чтобы помнить, поэтому у кого-то была идея. Что-то о макросе, которое будет использовать встроенное расширение компилятора для вызова правильного в соответствии с типами используемых параметров.

Это все волшебство C99 <tgmath.h>.

И идея казалась такой awesome, что они даже добавили предложение предложить эту функцию для всех функций в следующем стандарте C, с чем-то как:

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

Источник: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1340.htm

Теперь, шокирующие новости: эти функции доступны на С++ с десятилетия: это называется перегрузкой функций.

Например, указанные выше функции объявлены в С++ как:

  • double sin (double x );
  • float sin (float x );
  • long double sin (long double x );
  • и др.

Итак, "типичный макрос" - это взломанная реализация, которая стремится (частично) эмулировать более общую перегрузку функций С++.

И угадайте, что: вы можете даже добавить свои собственные перегрузки для своих пользовательских типов.

Заключение

Как показано выше, каждый раз, когда я изучаю функцию C99, вывод заключается в следующем: "Эй, я уже мог сделать это на С++!" (и обычно со словом "лучше" где-то в предложении).

Серьезно, как разработчик С++, то, что я пропустил прямо сейчас, должен использовать С++ 0x на работе. Например, следующие функции С++ 0x:

  • auto
  • constexpr
  • инициализированные списки
  • ссылки r-value
  • лямбды
  • nullptr
  • и др.

Все "C недостающие функции из С++" - это переоцененная концепция, которая, я подозреваю, более интересна разработчикам C (и разработчикам C-классов), чем разработчикам на С++.

Ответ 2

Вы можете найти веб-страницу несовместимость между ISO C и ISO С++.

В основном я пропускаю несколько функций C99, которые не находятся в С++:

  • Составные литералы;
  • Назначенные инициализаторы;
  • Переменные макросы аргументов (включены в С++ 0X).

Ответ 3

Не функция C, но, возможно, функция убийцы C состоит в том, что простой синтаксис C89 упрощает запись компилятора. Ну, по сравнению с компилятором С++.

Ответ 4

  • Мы можем назначить любой тип указателя на указатель void без добавления в c, но не в С++.

Любой указатель конвертируется в void* в С++. Вы теряете информацию таким образом, и компилятор не остановит вас от этого. И наоборот, проблема, потому что таким образом вы получаете информацию, которую компилятор не может проверить.

Я думаю, что C разрешает это, в то время как С++ определенно не делает.

Ответ 5

Если мы проигнорируем очевидный источник различий - C99 - ограничившись C89/90, а также отбросим тривиальные варианты, такие как ключевые слова С++, все еще будут некоторые различия между C и С++

(1) Вы уже упоминали о возможности преобразования void * в любой конкретный тип указателя без трансляции.

(2) Типы функций с "неуказанными" параметрами, т.е. () в объявлении типа функции. В C вы можете сделать это

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 */
}

Это невозможно в С++. Разумеется, можно также сказать, что общие непротиворечивые объявления функций являются признаком C, которого нет в С++ (относится и к C99).

(3) Некоторые отличия в инициализации массива со строковыми литералами: trailing \0 разрешено отпадать в C, но не в С++

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

(4) Ориентировочные определения в C, хотя они в основном не имеют никакого значения.

(5) Еще одна несущественная "особенность": в C вы можете использовать возвращающие значение функции, которые "забывают", чтобы фактически вернуть что-либо

int foo() {
}

Код является законным как в C, так и в С++, но в С++ такая функция безоговорочно произведет поведение undefined. В C функция создавала бы поведение undefined только в том случае, если вы действительно пытались использовать возвращаемое значение

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

(6) Некоторые другие вещи, которые я добавлю позже, если я его запомню.

Ответ 6

У вас может быть массив переменной длины в C, но не на С++. Я считаю, что это будет очень полезно, вместо того чтобы делать new[] для этого.

Ответ 7

В C вы можете неявно конвертировать между указателями void и другими указателями, но вы должны сделать явное преобразование в С++.

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++

Ответ 8

Есть несколько тонких различий в синтаксическом сахаре и злоупотреблении типом, но они тривиальны для работы.

Самая важная возможность C - создавать полностью свободностоящие программы без каких-либо внешних зависимостей. Вот почему ядра операционной системы почти повсеместно написаны на C. C. Фактически он предназначен для реализации операционных систем. Можно написать ядра ОС в ограниченном подмножестве С++, но принудительное выполнение этих ограничений происходит только во время соединения, если это вообще так, поэтому гораздо больнее иметь дело с малыми различиями синтаксиса.

Ответ 9

Что мне нравится в C, так это способность говорить о чем-то вроде a = b; и точно знать, что он делает. В С++ любой может переопределить операторы, что означает, что простой оператор вроде этого может вызвать некоторый массивный конструктор копий (или, что еще хуже, что-то совершенно неуместное). Вы видите a = b; в С++, вы должны угадать (или пойти и искать), сделал ли кто-то это просто раздражение.

Ответ 10

Как поклонник C, я думаю, что в концепции стиля кодирования может быть и другое, и это будет функциональное программирование в отличие от объектно-ориентированного программирования, известного как ООП! для тех, кто пишет свои коды как statemachines, это очень интересная концепция! В качестве хорошего примера рассмотрим opengl. Кроме того, скорость выполнения кода очень хороша в C из-за меньших ссылок на память. Вам также может понравиться, что многие программисты любят писать свои коды на C из-за простоты дизайна и того, как они используются для него (возможно, это не так просто в синтаксисе). О, и когда вы хотите писать коды очень близко к аппаратным уровням, вы должны использовать чистый C.

Ответ 11

Одной из пропущенных функций C99 является VLA, для которой С++ не должен иметь эквивалента.

Некоторые даже поставили под сомнение возможность написания в С++ объекта, подобного VLA.

Вот почему я добавил этот ответ: несмотря на то, что он немного вышел из темы, он по-прежнему демонстрирует, что с помощью правильной библиотеки разработчик С++ все же может иметь доступ к объектам, имитирующим функции C99. И, таким образом, эти функции не так пропущены, как полагали.

Основной код:

#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) ;
    }
}

Как вы видите, объект MyVLA знает свой размер (что намного лучше, чем использование оператора sizeof на VLA C99).

И, конечно же, класс MyVLA ведет себя как массив и инициализируется значением size_t (которое может меняться во время выполнения). Единственный сбой связан с характером функции alloca(), то есть конструктор должен использоваться только косвенно через макрос MY_VLA:

Ниже, код для класса, в файле 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) ;
}

Макрос - это беспорядок, и, вероятно, он может быть лучше написан. Сам класс, вероятно, не исключение, но это может быть сделано. Во всяком случае, потребуется больше кода для использования (т.е. Обрабатывать копию/назначение, делать новые/удалять приватными, добавляя const, где это возможно, и т.д.). Я предполагаю, что конец не оправдывает время, которое я потратил на это. Таким образом, это останется доказательством концепции.

Точка "С++ может эмулировать VLA C99, и она может работать даже как массив объектов С++!", мне кажется, мне удалось это продемонстрировать.

Я разрешаю читателю скопировать-вставить компиляцию кода, чтобы увидеть результаты (я скомпилировал его на g++ 4.4.3, на Ubuntu 10.04.

Ответ 12

В C вы можете определить имя переменной для удаления. Вы не можете сделать это на С++.

Ответ 13

Основное различие между C и С++ заключается в том, что С++ является объектно-ориентированным, а C - функцией или процедурой. Парадигма объектно-ориентированного программирования сосредоточена на написании программ, которые являются более читабельными и поддерживаемыми. Это также помогает повторно использовать код, упаковывая группу похожих объектов или используя концепцию модели программирования компонентов. Это помогает логически мыслить, используя концепцию реальных понятий объектов, наследования и полиморфизма. Следует отметить, что есть также некоторые недостатки таких функций. Например, использование полиморфизма в программе может замедлить производительность этой программы.

С другой стороны, функциональное и процедурное программирование фокусируется прежде всего на действиях и событиях, а модель программирования фокусируется на логических утверждениях, которые запускают выполнение программного кода.