Почему в C++ всё именно так?

Тут будет сразу несколько вопросов, некоторые из них могут относиться не только к плюсам.
1. Почему типы const char* и char* не совместимы?
char* foo = "foo";
const char* bar = foo; // Ошибка

Почему так происходит? И то, и то — строки, но почему они не совместимы?
2. Можно ли считать код ниже конструктором копирования?
int zero = 0;
0 ведь имеет тип int, правильно?
3. Чем код выше отличается от этого:
int zero(0);
Или от этого
int zero = int(0);
Или от этого
int zero{0};
Или вообще от этого
int zero = int{0};
Какое различие между этими вариантами инициализации переменной и что из этого лучше использовать?
4. В продолжение к вопросу выше. Почему мы используем = в инициализации массивов?
int array[] { 1, 2, 3};
Почему не принято делать так?
5. Зачем нужны указатели на void, что они делают? Да как вообще можно указать на, то чего нет? Я слышал, что void на самом деле занимает 1 байт. Это реально так?
6. Это относится не только к плюсам, но всё же. Зачем нужны два вида инкремента/декремента (постфиксный и префиксный)?
7. Почему этот код выводит 1?
#include <iostream>

class Empty {};

int main(int argc, char** argv)
{
	std::cout << sizeof(Empty{});
}

8. Кое-где вычитал, что return из main вызывает std::exit. Если это так, то как оно работает если не подключать стандартные библиотеки? Откуда берётся функция? (Тоже самое можно спросить про std::terminate, std::abort)
9. Как вообще реализована std::abort? Каким образом она работает?
10. Каким образом работает ввод/вывод? Как функции стандартной библиотеки могут поместить что-то куда-то, если ещё ничего для этого не реализовано?
  • Вопрос задан
  • 337 просмотров
Решения вопроса 3
@12rbah
https://stackoverflow.com/questions/9834067/differ... ответ на первый вопрос
на ваши вопросы по инициализации
1
2

Ну а так вы можете почти все ваши вопросы в гугл забить и он вам выдаст ответ.
Ответ написан
gbg
@gbg Куратор тега C++
Баянист. Тамада. Услуги.
Ответы на ваши вопросы в основном можно отрыть в стандарте языка, или путем нескольких месяцев практического решения задач, без этого моя болтовня вам понимания не добавит.

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

1) Какая ошибка? Почему любители задавать вопросы по C++ думают, что у каждого программиста в голове компилятор?
2) Нет, у примитивных типов нет конструкторов и методов
3) Если коротко, так как int - примитивный тип, у вас все случаи в итоге выражают одно и тоже - объявление и определение одновременно с инициализацией.
4) Наверное, вы уже поняли, что использовать везде int - плохая возможность для иллюстрации.
5) void* - это какой-то адрес в памяти. Так как C++ работает в том числе и низкоуровневыми данными, это задача программиста верно интерпретировать то, что находится в памяти по этому адресу. Огромное количество системных функций из POSIX или WINAPI, или OpenGL будут возвращать вам такой указатель.

6) Потому что они ведут себя разным образом. k = 8; p = 8; a = 13; k+=++a; a=13; p+=a++; /*k=22; p=21*/;
Поэтому нужны оба.

7)Потому что так написано в стандарте - sizeof пустой фигни = sizeof(char)
8) std всегда с вами.
9) как реализовали разработчики компилятора
10) как реализовали разработчики компилятора
Ответ написан
@res2001
Developer, ex-admin
Показался интересным 7 вопрос.
Дело в том, что вы можете создать экземпляр пустого объекта и можете взять его адрес.
Но если размер объекта будет 0, то адрес будет взять не возможно (объект не занимает места в памяти, соответственно нет и адреса), что противоречит правилам языка.
Поэтому пустые объекты на самом деле не пустые и занимают в памяти 1 байт.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@Mercury13
Программист на «си с крестами» и не только
1. Const-корректность придумал Бьярне Строуструп, и только ≈1999 её внесли в Си без крестов. А поскольку память, занятую строковым литералом, изменять запрещается (а в современных ОС литералы сидят в особом неизменяемом сегменте), понятное дело, новым типом для них стал const char*. Поскольку Си99 для совместимости позволял писать char* text = "foo";, а разработка Си(++) застопорилась на десятилетие, учебники по Си очень долго писали такую нерекомендуемую строчку.

3. Для маленьких объектов это одно и то же, но для больших немалая разница. Эта программа компилируется начиная с Си++17.
#include <iostream>

class WrapInt
{
public:
    int value = 0;
    WrapInt(int x) : value(x) {}
    WrapInt(WrapInt&) = delete;
private:
};

int main()
{
    WrapInt x = 42;
}


int zero = 0; до C++14 — создать временный объект, вызвать конструктор перемещения, а остальное делает оптимизатор. С Си++17 — вызов конструктора, если он не explicit. Мне казалось, что в очень старых версиях DJGPP был задействован op=, но ХЕЗ, MinGW и Си++03 вызывают именно конструктор.

int zero = int(0); — что удивительно, то же самое, только конструктор любой! Хотя выглядит как явное создание временного объекта.

int zero(0); — вызов конструктора во всех версиях Си++.

int zero{0}; — универсальный инициализатор Си++11, также вызов конструктора.

4. int array[] = { 1, 2, 3}; — все версии Си и Си++. int array[] { 1, 2, 3}; — универсальный инициализатор Си++11.

6. Эдсгер Дейкстра рассказал, почему массивы должны нумероваться с нуля, а целые диапазоны — задаваться в формате [a,b). Итак, у нас есть диапазон [a,b) и массив с запасом. Как сдвинуть b и добавить в диапазон один элемент? — array[b] = newElem; ++b;. Керниган и Ритчи решили объединить это чудо в одну строку array[b++] = newElem;, к тому же в процессорах действительно бывают пред- и постинкрементные операции.

7. void — это псевдотип, чьи элементы создавать нельзя. Указатели можно передвигать с шагом, который равен размеру типа — то есть прибавляем 1, реально прибавляется 1, 2, 4, 8 или сколько там. Для void*, который является простым указателем на память, шаг 1 байт. Или 1 слово, если машина оперирует более крупными словами — такие были, но из-за переусложнённой работы с текстом от них отказались. Но инкременты-декременты void*, если и возможны, то нежелательны, и вот почему.
Чтобы «соединить ежа с ужом» (длинное слово и доступ по байтам), x86 говорит: работа с невыровненными данными (например, 4 байта по адресу 21) будет происходить медленно и по неопределённому алгоритму. Itanium вообще, ЕМНИП, к невыровненным данным не обращается, как это устроено в компиляторах: char* → int* говорит, что обращаться нужно медленно и программно, void* → int* — быстро и аппаратно.

8. libc можно и не подключать, хоть это и сложно. А return из main НЕ вызывает std::exit. Линкер создаёт код обвязки, который открывает все глобальные объекты, вызывает main), закрывает все глобальные объекты и выходит из программы по-системному.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы