LINUX.ORG.RU

Отличия C и C++

 ,


1

2

Есть два эквивалентных примера:

#include <stdatomic.h>

int main() {
  int v[2], * p0 = v, * _Atomic p1 = v;
  if (p0 != p1) return 1;
  if (++p0 != ++p1) return 2;
  if (++p0 != (atomic_fetch_add_explicit(&p1, 1, memory_order_relaxed), p1)) return 3;
}

#include <atomic>
using namespace std;

int main() {
  int v[2], * p0 = v; atomic<int*> p1 = v;
  if (p0 != p1) return 1;
  if (++p0 != ++p1) return 2;
  if (++p0 != (p1.fetch_add(1, memory_order_relaxed), p1)) return 3;
}

C-версия делает return 3. Из-за того, что atomic_fetch_add_explicit(&p1, 1, memory_order_relaxed) компилируется в lock addq $0x1,-0x10(%rsp). В C++ lock addq $0x4,-0x10(%rsp) - как и должно быть, учитывает тип. Это баг какой-то или так специально сделано? Зачем?

  int v[2], * p0 = v, * _Atomic p1 = v;

...

  if (++p0 != ++p1) return 2;

Ты уже объявил p1 как atomic, и вот этот инкремент компилируется в это:

	movl	$4, %eax
	lock xaddq	%rax, 8(%rsp)

Непонятно, чего ты еще хочешь добиться. atomic_fetch_add_explicit не определён сишкой для указателей, только для целых. По арифметике целых он и складывает.

wandrien ★★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от qweururu

Только на ЛОРе есть такое развлечение: читать друг другу маны вслух. И этот сайт всё еще бесплатный!

These built-in functions perform the operation suggested by the name, and return the value that had previously been in *ptr. Operations on pointer arguments are performed as if the operands were of the uintptr_t type. That is, they are not scaled by the size of the type to which the pointer points.

wandrien ★★★
()
Ответ на: комментарий от qweururu

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

anonymous
()
Ответ на: комментарий от wandrien

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

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

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от lovesan

вызову разные функции на разных языках
почему работает по-разному?

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

anonymous
()
Ответ на: комментарий от anonymous

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

monk ★★★★★
()
Ответ на: комментарий от anonymous

Общую и в Си++ нельзя. В Си++ можно сделать или шаблон для произвольного типа или перегрузку функции по типу (так как тип там часть имени). В Си тоже так можно (оба варианта).

monk ★★★★★
()
Ответ на: комментарий от monk

покажи пожалуйста как в си можно перегрузку функции по типу и шаблон для произвольного типа? если найдёшь время.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от AndreyKl

покажи пожалуйста как в си можно перегрузку функции по типу

Да также:

int _Z3fooi(int x) { return x + 1; }
int *_Z3fooPi(int *x) { return x + 1; }

или если не требуется совместимость с Си++

int foo_int(int x) { return x + 1; }
int *foo_intptr(int *x) { return x + 1; }

и шаблон для произвольного типа?

#define def_foo(type)  type foo_##type(type) { return x + 1; }

typedef int *intptr;

def_foo(intptr);
def_foo(int);
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk
int _Z3fooi(int x) { return x + 1; }
int *_Z3fooPi(int *x) { return x + 1; }



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

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

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

В Си++ тип функции является частью имени. А реальное имя вот такое с _Z. Синтаксический сахар позволяет обращаться по короткому имени, если известен тип (и провоцирует загадочные ошибки, если перегрузка тип угадала неправильно). Тогда уж можно сделать надстройку, чтобы вызывать функцию по началу имени. Вместо atomic_fetch_add_explicit писать a_f_a_e(...).

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

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

monk ★★★★★
()
Ответ на: комментарий от monk

В Си++ тип функции является частью имени.

По моему, это уход в технические детали в ущерб, в данном случае, сути обсуждаемого вопроса, к сожалению.

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

Хотя да, под капотом у с++ просто разные функции.

AndreyKl ★★★★★
()
Ответ на: комментарий от AndreyKl

В си такого поведения достигнуть не удастся, поэтому мы говорим что перегрузки в си нет.

Ну если очень хочется…

#define foo(x) _Z3fooi(x)

int x = foo(5);

#define foo(x) _Z3fooPi(x)

int *p = foo(&x);

Чтобы автоматически подменял — не удастся. В Си происходит только то, что прописано явно.

monk ★★★★★
()
Atomically replaces the value pointed by obj with the result of addition of arg to the old value of obj, and returns the value obj held previously.

 — Важное значение имеет что

returns the value obj held previously.

Программа делает все что следует — не понимаю сути вопроса/негодования...

upd. А блин не узрел там оператора «запятая»... Ну да — получается что добавляет неверное число))) если вместо 1 поставить 4 — будет ретурн не 3.

safocl ★★
()
Последнее исправление: safocl (всего исправлений: 1)
Ответ на: комментарий от monk

всюду пихать auto, пусть компилятор сам угадывает, что программист имел в виду.

тут все в точности наоборот — программист угадывает какой тип вывел под auto компилятор — для компилятора все очевидно.

safocl ★★
()
Ответ на: комментарий от monk

В Си++ тип функции является частью имени. А реальное имя вот такое с _Z

имя функции по стандарту — к примеру хотя бы такое «Multiple functions in the same scope may have the same name, as long as their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications are different.»
а ты уже говоришь про манглирование — которое является деталями реализации, а не частью стандарта языка программирования — ABI ялвяется несущественным для кода C++

safocl ★★
()
Ответ на: комментарий от safocl

«Multiple functions in the same scope may have the same name, as long as their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications are different.»

Так это и значит, что «their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications» являются частью полного имени, определяющего функцию.

Вот аналог: «In Hungarian notation, an identifier starts with a group of lower-case letters which are mnemonics for the type or purpose of that variable, followed by whatever name the programmer has chosen». То есть для разных функций «name» может быть одинаковый, если «group of lower-case letters» отличается.

ABI ялвяется несущественным для кода C++

Ни одной реализации Си++, где «their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications» не являлись бы частью имени, не существует.

monk ★★★★★
()
Ответ на: комментарий от safocl

для компилятора все очевидно

Для компилятора может вообще не существовать типа для auto. А человек, когда пишет код, всегда подразумевает некий тип (возможные значения и операции с ними).

monk ★★★★★
()
Ответ на: комментарий от monk

Так это и значит, что «their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications» являются частью полного имени, определяющего функцию.

да не — это ты уже про реализацию, а не ЯП С++.

safocl ★★
()
Ответ на: комментарий от safocl

Вот функция с auto:

auto compose(auto f, auto g) { return [=](auto x) { return f(g(x)); }; }

Если утверждаешь, что компилятор просто заменяет auto на тип, напиши эту функцию с явно указанным типом.

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от safocl

Это про язык. Про то, что идентификатор языка состоит из частей «name» и «their parameter lists and, for non-static member functions, cv/ref(since C++11)-qualifications».

То, что вторая часть вычисляется из типов аргументов, несущественный синтаксический сахар.

monk ★★★★★
()
Ответ на: комментарий от monk

ты явно путаешь разные контексты использования ключевого слова auto — который слева это будет авто вывод типа ретурна, — это нешаблонное действие. auto у аргов функции раскрывается в шаблонные типы — следовательно у тебя шаблон функции и он будет следовать правилам инстанциирования шаблонов. Никаких тут противоречий нет — ты просто явно не понимаешь что значат эти разные использования слова auto.
А я вообще не про такое говорил — я говорил про то, что для компилятора не может не существовать типа — тоесть тип в любом случае будет известен на этапе компиляции — будет выведен.

safocl ★★
()
Ответ на: комментарий от safocl

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

Ты же в предыдущем предложении пишешь, что тип auto в приведённом примере не будет выведен. Хотя с точки зрения человека там вполне определённые типы: f и g не произвольные значения.

monk ★★★★★
()
Ответ на: комментарий от safocl

«который слева это будет авто вывод типа ретурна, — это нешаблонное действие. auto у аргов функции раскрывается в шаблонные типы — следовательно у тебя шаблон функции и он будет следовать правилам инстанциирования шаблонов»

monk ★★★★★
()
Ответ на: комментарий от safocl

и какие же там типы? если сможешь сказать — я прям «приклоню колени»…

f - функция от одного аргумента произвольного типа

g - функция от одного аргумента типа, совпадающего с типом возвращаемым f

результат является функцией от одного аргумента с типом, совпадающим с типом аргумента f и возвращающий значение с типом, совпадающим с типом возвращаемым g

monk ★★★★★
()