LINUX.ORG.RU

А как на C решаются проблемы одной переменной на несколько типов?

 


0

3

Возник вопрос. Можно ли в C использовать переменную произвольного типа в зависимости от контекста? Пример кода.

void test(int type) {

 if (type==1) {
  char *data = "Hello world";
 }
 if (type==2) {
  int data = 123;
 }
// ...
}

Экспериментировал с void. Работает лишь наполовину

void test(int type) {

 void *data;
 if (type==1) {
  char *data = "Hello world";
  printf("Data: %s\n",data); // Тут data - правильные
 }
 if (type==2) {
  int data = 123;
 }
// ...
 printf("Data: %s\n",data); // А вот тут data - поломанные
}

Первый printf выводит как положено, Hello world.

А вот второй printf вне условия, выводит �ÐUH��H�� H�}�H�

Благодарю.

★★★★★
Ответ на: комментарий от u-235

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

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

ну и делай это ручками. напиши функцию - add_number_to_string и внутри нее бери первый символ, если строка не пустая. переводи его в какое-нить число(если сможешь) прибавь свое число, переведи сумму обратно в символ, и запиши на первое место. и сломай че-нить. типа строка читаться не будет в силу испорченной кодировки.

кто мешает-то? почему это надо вводить прям в какой-нить язык?

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

Да нет, вводить конечно не надо. Но я не уверен, что такие конструкции как строки тоже прям вот надо вводить в язык. Тянет на библиотеку может быть.

А они введены.
struct позволяет создать объект любой сложности (в т.ч. какой-нибудь строковый тип).

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

Зависит от языка, если он прикладной - то можно и даже нужно ввести, если системный широкого профиля, - то не нужно, то есть в с++ сделано верно, То бишь это библиотечные типы, причем разные. Бери какой нужно и пиши себе.

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

У меня на сях где-то была реализация черно-красного дерева. Писал когда-то давно по приколу. Я конечно в своем уме и не буду средствами языка писать: число + дерево. Но и как бы язык мне это не запретит. И это правильно.

Моя претензия к питону в том, что он:

  1. Скрывает как на самом деле работает машина.

  2. Вводит искусственные ограничения. Видимо, чтобы дети не убились.

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

Короче, мне не нравится питон.

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

и мне не нравится питон. питон это скриптуха, с неправильным синтаксисом на отступах.

А мне табы не нравятся.
Многие редакторы с ними правильно и работать не могут.
Исходный текст то на экране показывается с учётом количества символов отведённых табам.
Так вот, когда пытаешься что-то изменить в промежутке между табом и следующим символом, то не все текстовые редакторы умеют правильно это сделать.
Речь о количестве пробелов между табом и следующим значащим символом.
Если скажем хочу «подтянуть» текст на несколько символов, то таб должен быть убран и вместо него добавлено нужно количество пробелов.
Так вот не все текстовые редакторы умеют такую «мелочь» правильно сделать.
Уж молчу когда производится работа с блоком текста.
Правда обычно в редакторах имеется настройка на использование табов или замены их пробелами.

anonymous
()

Это зависит от типа переменной. Например, ты можешь преобразовывать переменную bool к любому типу, но не сможешь преобразовать long long ни к какому другому.

int(x) + long(y) = long(x + y)
char(z) + bool(t) = char(z + t)

и т.д.

Всё зависит от размера переменной - меньший тип переменной может преобразовать к большему, но не наоборот.

C не различает типы переменных - всё числа. Пример:

#include <stdio.h>

int main () {
    int val = 67;
    printf("%d %c\n", val, val);
    return 0;
}
iskander9908
()
Ответ на: комментарий от hibou

Средства программирования не должны мне в этом мешать.

А вот это очень сложный вопрос. Куда сложнее чем может показаться.

Если говорить про суровую коммерческую разработку, а не для науки, души или искусства - то есть же очень большая проблема. Есть такие люди - 321сты, как много их среди программистов. Да, все этим грешат. Поэтому, если это бездуховный наемный труд, то надо приставлять к рабу другого негра с плеткой, который будет стегать по мягкому месту за определенные вещи. Вот компилятор всякой джавы и питона, и раста и есть такой негр. Раст против 321стов, короче.

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

Всё зависит от размера переменной - меньший тип переменной может преобразовать к большему, но не наоборот.

Неправда.

C не различает типы переменных - всё числа. Пример

Тоже неправда. То что происходит в примере - прямое следствие ABI, и вообще говоря - UB.

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

Тут вот осторожней надо. printf %c, конечно сделает что ты хочешь, напечатает символ. Но для компилятора val объявлена как инт. Он и положит инт на стек для функции принтф. А вот функция заберет со стека чар. Другой размер совсем.

Здесь правда одно но. Аргументы первые 4 передаются через регистры.Именно это спасет программу от краха. Если бы твой %с был 5-м и далее… то ты бы испортил стек. С дальнейшими трудно отследимыми последствиями.

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

А вот функция заберет со стека чар. Другой размер совсем.

И вот после этого, люди спрашивают, а почему в C++ всю дорогу вместо printf использовались потоки. Ну совсем непонятно же зачем, ага

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

С современными многобайтовыми кодировками конечно вообще не все так однозначно. Это действительно уже АБИ и зависит от реализации наверное.

Главный посыл тут надо быть осторожнее. Не делать того, что может где-то сломаться.

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

Неправда.

Отчасти. Передадутся только младшие биты типа нашей переменной - это не ожидаемое поведение программы.

Тоже неправда. То что происходит в примере - прямое следствие ABI, и вообще говоря - UB.

Всё - биты, множество битов образуют числа. Читая переменную val, программа читает первые восемь байт, если мы используем тип char, аналогично и с другими типами - читаются только младшие биты соответствующего типа (они могут как совпадать с типом переменной, так и нет)

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

В коде iskander9908 все правильно, при передаче va_args типы повышаются до больших: «Each argument of integer type undergoes integer promotion, and each argument of type float is implicitly converted to the type double.».

bugfixer правильно заметил, и многобайтные символы тут непричем, char всегда будет как максимум размером с int.

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

Спасибо!

У меня так - «Нэт табуляторов, нэт проблэм».
Что касается выравнивания в комментариях, содержащих таблицы, то отступы выполняю пробелами.

В целом это дело вкуса.
Что касаемо чужих текстов программ, то просто произвожу замену табуляторов на пробелы (обычно в редакторах имеется в меню для редактирования такой пункт).

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

C не различает типы переменных - всё числа. Пример:

Бред какой то, вот тебе работающий код которые показывает, что С все различает:

printf("%s\n", type_name(10.0f)); // печатает float
printf("%s\n", type_name(true)); // печатает bool

https://godbolt.org/z/3bjPzaPad

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

Всё - биты, множество битов образуют числа. Читая переменную val, программа читает первые восемь байт, если мы используем тип char, аналогично и с другими типами - читаются только младшие биты соответствующего типа (они могут как совпадать с типом переменной, так и нет)

Указатель это не число, вот наглядный пример: https://gcc.godbolt.org/z/PGcKss5Yz

А идентичные биты указателей, не означает что указатели равны. А у NULL вообще нету истинного битового представления.

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

А идентичные биты указателей, не означает что указатели равны. А у NULL вообще нету истинного битового представления.

Да и адрес это вовсе не адрес физической памяти …

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

Нет, я все правильно написал, как видно из вывода программы справа («Числа от указателей равны»), адреса указателей совпадают, то есть у pb и pa одинаковые числа-значения, но их сравнение именно как указателей дает false.

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

Код выше опровергает твое утверждение. Стандарт опровергает твое утверждение. Существуют архитектуры где «указатель» (если берем ассемблер) хранит не только адрес, или вообще является дескриптором.

Хотя на x86 это действительно так, с оговоркой что не в физической оперативной памяти. Но это уже мы ушли за пределы С. Для виртуальной машины С это утверждение неверно.

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

Удачное совпадение, которое зависит от компилятора и архитектуры. Я уже выше доказал что твое утверждение ложно, одного примера достаточно. Согласись, если только по понедельникам указатель ведет себя как число, то что то не так.

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

В твоем примере ты указал два разных адреса, поэтому и указатели не равны.

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

Какой рекомендованный способ сравнения адреса от указателей? Через каст к uintptr_t. Давай попробуем: https://gcc.godbolt.org/z/7qhrssj8f

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

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

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

Ты похоже С не знаешь

Ну уж получше некоторых

#include <stdio.h>
#include <stdint.h>

int main () {
    int x = 42;
    int* ptr = &x;
    uintptr_t addr = (uintptr_t)ptr;
    printf("Указатель: %p\n", (void*)ptr);
    printf("Адреч числа, храняшегося в указателе: %p\n", &x);
    printf("Адрес как число: %p\n", (unsigned long)addr);
    return 0;
}

Ты хотя бы к правильному типу приводил бы

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

Я пользуюсь нормальным компилятором - gcc, а не онлайн гавном. Запусти там и увидишь, что указатели не равны.

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

Не, дело не в этом. Дело в стандарте и оптимизациях.

Я запустил локально. С оптимизациями. И посмотрел ассемблерный листинг. Там сравнение указателей вообще выброшено. Компилятор по стандарту имеет право так сделать. И он это сделал.

hibou ★★★★★
()