при записи в теже потоки мы таки не адрес хотим, а блок байтов.
Это неверно. При работе с потоками мы манипулируем символами, которые не связаны напрямую с байтами и могут быть любого размера. Вы почему-то перешли от C к C++ и от памяти к операциям ввода/вывода. Я теряю нить спора.
Вот и опять пришли к выводу — void* не потому что так правильно, а потому что того требует совместимость с различными платформами и языками.
Именно потому, что так правильно. Совместимость — один из критериев корректности кода, но это лишь одна причина использовать void*. Вам привели уже десяток аргументов, но вы всё ещё считаете правильным другой вариант по неясным для меня причинам.
Вот и я хочу конкретики — memcpy копирует мне байты
Да не копирует он байты. Он машинными словами копирует, я уже говорил. А если вы имеете ввиду именно физический смысл происходящих процессов, то тогда любая функция превращается в операцию над байтами. Давайте везде просить массивы байт? А что, в итоге-то процессор всегда ими оперирует. А можно просить массив битов. Тоже с физической точки зрения верно — memcpy же скопирует вам биты.
В этом и проблема с функциями вида (char*, int) — непонятно, принимают они просто кусок памяти, или строку. Но в данном случае, учитывая что в названии функции есть str, я бы предположил что именно строку. А в случае, если бы там было (void*, size_t) — тут без вариантов, только кусок памяти.
Ну, те же функции типа memcpy кастуют этот указатель обычно к машинному слову, то есть чему-то типа int*. Да и многие другие функции кастуют такую память вовсе не к массиву байт. Но даже если бы я и хотел принять байты в открытом виде у меня всё ещё возникла бы проблема — в C нет типа для байтов. Массив char — вообще худшее что можно сделать — в зависимости от платформы и настроек компилятора он может быть либо знаковым либо беззнаковым. А uint8_t — это беззнаковый тип, о котором известно только что он занимает не менее 8 бит. На какой-нибудь платформе, где байты содержат меньше бит (например 7) это будет не однобайтовый а двухбайтовый тип.
C этим подходом есть несколько проблем:
1. В C нет типа byte. Вместо него есть множества других типов: char, signed char, unsigned char, uint8_t, int8_t и тд и тп.
2. void* — это не «указатель на ничего», как вы выразились, а банально адрес в памяти. Такая сигнатура показывает, что функции не важен тип данных. Функции не нужны байты, как вы говорите, ей нужна память. Представление ей не важно. Например, ей не важна знаковость типа, которая иначе всегда вылазит.
Так с какого она прячет свои намерения за каким-то непонятным void*
Как-раз таки своим void* функция и сообщает нам о своих намерениях. Им она говорит пользователю: «Мне нужна память и мне плевать что там у тебя. Просто дай мне адрес.» Хочу также добавить, что тип void* известен и понятен любому, кто хоть немного знает C и не я не понимаю, почему вы считаете его «каким-то непонятным».
Во второй можно передать указатель на что угодно, с каким угодно типом и размером типа.
Так в этом и суть объявления. Функции неважно, какой тип. Пример обычный, суть в том, что функция проводит операции над памятью и ей не важен тип. Суть совета в том, что когда вы хотите объявить такую функцию, то иногда может возникнуть соблазн написать uint8_t* — как бы массив байт. Автор говорит о том, что такой подход заведомо неверный и надо писать void*.
Например, если вы хотите объявить какую-то свою memcpy то следует в сигнатуре указывать не uint8_t* (по сути, байтовый массив), а void*.
О чём вообще тут говорить? #pragma once не имеет отношение к стандарту, а значит использовать её (как и всё, что не имеет отношение к стандарту) следует лишь в крайнем случае, когда нет альтернатив. В данном случае альтернатива есть и она работает лучше. Вывод простой — #pragma once — использовать не следует вообще никогда.
Речь в статье идёт именно о случаях, когда можно передать любые данные, функции типа memcpy(), где операции производятся над памятью, независимо от структуры и это реальный кейс. В остальных случаях естественно надо указывать именно те типы, которые вы хотите использовать. Другие примеры подобных функций: send, encrypt, md5sum и тп.
Таджик в вашей аналогии нарушает закон. Китаец в нашей ситуации — нет. Он вполне добросовестный гражданин и никакими дырками не пользуется. Он предоставляет мне абсолютно законно тот же товар по цене ниже, чем вы. Почему я должен покупать у вас? Тут бы больше подошла следующая аналогия: на рынке стоит таджик и продаёт морковку, а рядом стоите вы, купившие у этого же такжика морковку и просите на 20% больше. Почему я должен покупать у вас?
Конечно, моя аналогия тоже не корректна, потому что на самом деле вы предоставляете за эти 20% дополнительные услуги — у вас купить гораздо быстрее, у вас можно посмотреть товар, у вас есть гарантия. Сфокусируйтесь на своих преимуществах и люди будут покупать у вас, а не у хитрых «таджиков».
Послушайте, почему вы считаете, что вам все всё должны только потому что вы — русский? Слышали когда-нибудь про свободную конкуренцию? Вы хотите быть посредником при продаже и хотите, чтобы ваше посредничество оплачивали просто потому что вы русский? А что, если вы мне не нужны? И услуги ваши при покупки какого-то товара мне тоже не нужны? Почему вы мне навязываете свои услуги? Считаете, что раз платите в России налоги, то вам все должны платить?
Давайте я открою магазин где буду продавать, ну допустим, статуэтки коней из навоза. Буду платить налоги, всё честь по чести. Трудиться буду в поте лица. А потом, если бизнес пойдёт не так хорошо как мне хотелось бы, потребую обязать всех покупать раз в месяц мою продукцию. Что бы вы на это сказали? Покупали бы? Польза же для страны, новое производство. Налоги опять же, да и предприятие русское. Или, всё-таки, на рынке выживать должны те, кто производят что-то полезное?
Я не ставлю минусы тем, с кем общаюсь, за исключением редких случаев, типа оскорбления. Я вообще нечасто их ставлю. Не переживайте из-за них, это просто выражение согласия или несогласия людей с тем, что вы написали, а не оценка вам.
Ну, хабр это же не pastbin, тут люди обычно не код ищут а статьи на конкретную тему, узнать что-то новое про конкретную область. Так вот, к теме С++ данная статья имеет крайне слабое отношение, и ничего нового про С++ из этой статьи узнать не получиться, равно как и про Objective C. Про С — возможно, про алгоритмы — наверняка. ИМХО, правильными хабами для этой статьи были бы C и Алгоритмы, но никак не C++.
Только не обижайтесь, ради бога. Никак не хотел вас задеть своим замечанием.
Послушайте, я никак не трактую ваш текст, я просто сделал замечание, с которым, я думаю, согласится любой, для кого C++ — основной язык разработки. Код на С != код на С++, равно как С != С++. Заходя в хаб С++ я ожидаю увидеть код на С++, а заходя в хаб С — код на С. При этом я прекрасно отдаю себе отчёт в том, что почти любой код из хаба С скомпилируется как С++.
Обычно, указывают что код на C++ когда он явно на C++ и ничем больше не скомпилируется. Так можно ещё поставить теги C++11, C++14, C++17. А что? Компиляторами, поддерживающими эти стандарты он тоже скомпилируется.
Вообще, не стоит воспринимать моё замечание так в штыки. Вы просили пояснений — я их дал, я не собирался с вами спорить.
В заголовке указано: на Си (С++), Objective C не подходит ни под одну из категорий?
Да потому что широко распространено заблуждение, что если вы можете что-то написать на C то код на C++ будет выглядеть также. Это заблуждение и это чаще всего неверно. То, что код для С компилируется плюсовым компилятором почти всегда и так всем известно, это не делает его кодом на С++. Этот же код, например, отлично скомпилировался бы и Objective C компилятором, может и этот хаб/тег добавите?
Именно потому, что так правильно. Совместимость — один из критериев корректности кода, но это лишь одна причина использовать void*. Вам привели уже десяток аргументов, но вы всё ещё считаете правильным другой вариант по неясным для меня причинам.
Да не копирует он байты. Он машинными словами копирует, я уже говорил. А если вы имеете ввиду именно физический смысл происходящих процессов, то тогда любая функция превращается в операцию над байтами. Давайте везде просить массивы байт? А что, в итоге-то процессор всегда ими оперирует. А можно просить массив битов. Тоже с физической точки зрения верно — memcpy же скопирует вам биты.
1. В C нет типа byte. Вместо него есть множества других типов: char, signed char, unsigned char, uint8_t, int8_t и тд и тп.
2. void* — это не «указатель на ничего», как вы выразились, а банально адрес в памяти. Такая сигнатура показывает, что функции не важен тип данных. Функции не нужны байты, как вы говорите, ей нужна память. Представление ей не важно. Например, ей не важна знаковость типа, которая иначе всегда вылазит.
Как-раз таки своим void* функция и сообщает нам о своих намерениях. Им она говорит пользователю: «Мне нужна память и мне плевать что там у тебя. Просто дай мне адрес.» Хочу также добавить, что тип void* известен и понятен любому, кто хоть немного знает C и не я не понимаю, почему вы считаете его «каким-то непонятным».
Вместо каноничного
Почему второй вариант лучше?
Например, если вы хотите объявить какую-то свою memcpy то следует в сигнатуре указывать не uint8_t* (по сути, байтовый массив), а void*.
Конечно, моя аналогия тоже не корректна, потому что на самом деле вы предоставляете за эти 20% дополнительные услуги — у вас купить гораздо быстрее, у вас можно посмотреть товар, у вас есть гарантия. Сфокусируйтесь на своих преимуществах и люди будут покупать у вас, а не у хитрых «таджиков».
Давайте я открою магазин где буду продавать, ну допустим, статуэтки коней из навоза. Буду платить налоги, всё честь по чести. Трудиться буду в поте лица. А потом, если бизнес пойдёт не так хорошо как мне хотелось бы, потребую обязать всех покупать раз в месяц мою продукцию. Что бы вы на это сказали? Покупали бы? Польза же для страны, новое производство. Налоги опять же, да и предприятие русское. Или, всё-таки, на рынке выживать должны те, кто производят что-то полезное?
Только не обижайтесь, ради бога. Никак не хотел вас задеть своим замечанием.
Обычно, указывают что код на C++ когда он явно на C++ и ничем больше не скомпилируется. Так можно ещё поставить теги C++11, C++14, C++17. А что? Компиляторами, поддерживающими эти стандарты он тоже скомпилируется.
Вообще, не стоит воспринимать моё замечание так в штыки. Вы просили пояснений — я их дал, я не собирался с вами спорить.
Конечно же, нет.