LINUX.ORG.RU

char = char * char как?!


1

1

Доброго времени суток! Есть задание на С, надо просто вывести на экран чар, как произведение двух чаров. Пример:

char a,b,c;

int main (){

printf(«%c», a);

printf(«%c», b);

c=a*b;

printf(«%c», c);

}

После первого printf консоль вылетает, что-то не так, но getchar работает, хмм. Поскольку конечное значение char, то после перемножения, он выдает не число интегером, а символы. Как присвоить чару, модуль произведия двух чаров, не изменяя его тип данных, то есть сохранить формат char?

Спасибо!



Последнее исправление: maxylopes (всего исправлений: 1)

Ответ на: комментарий от hexdump01010101

У меня для вас плохие новости. ;-) Типы продвигаются до наибольшего типа в выражении, а потом, при необходимости, приводятся к lvalue, за исключением типов меньших int — они всегда приводятся к int перед арифметическими операциями над ними.

тип char уже стал больше int? Вообще говоря, как именно будет вычислять char + char, это ЕМНИП в стандарте было отдано на усмотрение компилятора. Как ему удобнее - так оно и будет. В x86-32 int * int компилятору было удобнее считать как произведение 2х 32х битных int и результат в 64х битный long long. А вот что там на счёт произведения char - не знаю. В любом случае, твои примеры не играют никакой роли. Абсолютно не важно, как происходит преобразование, выражение должно правильно работать в любом случае. Очевидно, что если ты желаешь перемножить два char, то тебе их необходимо для надёжности сначала перевести в int. Что однако не гарантирует отсутствия переполнения в любом случае и на любой архитектуре.

Надеяться на то, что компилятор преобразует char в int ИМХО - плохая идея.

И да, в твоём примере, в printf, у тебя «lvalue» имеет тип int, насколько я знаю, в стек невозможно загрузить только 1 char, во всяком случае в x86. Вот оно и приводится к 32х битам.

Как работает sizeof(выражение) я не знаю. Не задумывался. Возможно в этом случае и работает перевод в int. Что совсем не доказывает, что a=b*c тоже такое происходит, если все char a,b,c;, и примера недостаточно, давай цитату из стандарта.

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

Знаю только паскаль и C++

странно... А где ты видел в паскале или C++ «переменные без типа»? Не помню как оно в паскале, но в C/C++ у переменных тип есть ВСЕГДА. И он всегда жёстко задан на этапе компиляции (небольшая поблажка имеется лишь для производных классов в C++, там тип можно немного менять прямо в рантайме)

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

и да, я нашёл в стандарте только то, что константы вроде 'Z' имеют тип int. Причём тут char - мне непонятно, особенно учитывая, что 'Ф' никак в char не влезет.

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

Абсолютно не важно, как происходит преобразование,

Конечно же важно.

char a,b;

#define FOO (a*b)

x = malloc(sizeof(FOO));
y = malloc(sizeof(a));

memcpy(y, x, sizeof(FOO));

По вашему изначальному заявлению, когда вы еще не знали про integer promotion, и говорили, что типы вообще не меняются, вы бы предполагали, что в коде нету ошибки. Но очевидно, что эта программа упадет. ;-)

давай цитату из стандарта

Ну вот, пример к 5.1.2.3:

EXAMPLE 2
In executing the fragment
char c1, c2;
/* ... */
c1 = c1 + c2;
the ‘‘integer promotions’’ require that the abstract machine promote the value of each variable to int size
and then add the two ints and truncate the sum. Provided the addition of two chars can be done without
overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only
produce the same result, possibly omitting the promotions

Тут говорится, «require ... promote .. to int *size*». Далее идет объяснение, что машина не обязана внутри считать это в больших типах, какой тип у результата тут не говорится, а говорится он в самом определении:

6.3.1.1.2

The following may be used in an expression wherever an int or unsigned int may be used:

-- An object or expression with an integer type whose integer conversion rank is less
than or equal to the rank of int and unsigned int.

-- A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type, the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the integer
promotions.

Переведу с языка стандартов: «an object or expression with an integer type whose integer conversion rank is less than or equal to the rank of int» (то есть например char), может быть использован в «an expression wherever an int or unsigned int may be used», а далее, говорится уже открыто, и без всяких «но»: «If an int can represent all values of the original type, the value is converted to an int;». И это называется «the integer promotions».

А с учетом того, что они это говорят, когда определяют операторы арифметики, то я могу предположить, что для типов меньше int они вообще формально не определены. ;-) (Что имеет смысл. Ведь (char)1 << 1, обязан дать тип int, так же как и (char)1 << (char)x — результат int).

Можно об этом думать так: каждый арифметический оператор содержит единицу (а у нее, как мы знаем, тип int). То есть умножение это 1*, деление 1/. Отсюда, какой тип будет у (char)1 * (char)1 * 1? Правильно, int, даже без integer promotion, а по arithmetic conversions (6.3.1.8).

В x86-32 int * int компилятору было удобнее считать как произведение 2х 32х битных int и результат в 64х битный long long.

У вас какой-то чудесный компилятор. :-)

/tmp$ cat c.c
#include <stdio.h>
#include <limits.h>

int main()
{
        int i = INT_MAX;
        long long j = i * 2;
        long long k = (long long)i * 2;

        printf("%zd %d %lld %lld\n", sizeof(INT_MAX * 2), i, j, k);
        return 0;
}
/tmp$ gcc -m32 -Wall c.c && ./a.out 
4 2147483647 -2 4294967294

По вашему, j и k должны были бы быть равны... да и размер результата как-то не сходится — на long long не тянет.

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

тип char уже стал больше int?

внимательней читай

за исключением типов меньших int — они всегда приводятся к int перед арифметическими операциями над ними.

т.е в примере

char a = 200;
char b = a + a;
в правой части операнды СНАЧАЛА продвинутся до int, тип rvalue будет int со значением 400, а потом произойдет неявное приведение к lvalue с потерей результата.

Вообще говоря, как именно будет вычислять char + char, это ЕМНИП в стандарте было отдано на усмотрение компилятора

наркоман штоле? судя твоему заявлению следующий код (с++), скомпилированный на разных компиляторах будет работать по-разному.

void foo(int i) {}
void foo(char c) {}

char a = 10;
foo(a + a);
foo(a * a);

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

Что совсем не доказывает, что a=b*c тоже такое происходит, если все char a,b,c;

а счего бы a=b*c должно вычисляться по другому? в правой части выражение, неявно приводится к левой части.

Объект типа перечисление, символ, короткое целое, целое в битовом поле - все они со знаком или без могут использоваться в выражении там, где возможно применение целого. Если тип int позволяет «охватить» все значения исходного типа операнда, то операнд приводится к int, в противном случае он приводится к unsigned int.

k&r.

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

и да, я нашёл в стандарте только то, что константы вроде 'Z' имеют тип int.

в сях sizeof('Z') == sizeof(int), в крестах == sizeof(char).

особенно учитывая, что 'Ф' никак в char не влезет.

про однобайтовые кодировки слышал?

vvviperrr ★★★★★
()
Ответ на: комментарий от ms-dos128

Неявное приведение происходит само собой... тогда нельзя присваивать переменным a,b,c значения больше 255 (128) и происзведение также должно быть меньше этого числа

ms-dos128
()
Ответ на: комментарий от hexdump01010101

По вашему изначальному заявлению, когда вы еще не знали про integer promotion, и говорили, что типы вообще не меняются

говорил, и буду говорить - типы жёстко задаются на этапе компиляции. Если вы написали int x;, то это так и будет int x. И в char/long оно само по себе не превратится. Преобразуется только если второй операнд имеет большую размерность, к пример long y = x; - тут int будет расширено до long, если конечно в данной реализации long длиннее int.

Provided the addition of two chars can be done without

overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only produce the same result, possibly omitting the promotions

а с чем ты споришь-то? c1 = (char)((char)c1 + (char)c2)); даст тот же результат, что и c1 = (char)(int)(c1 + c2); и c1 = (char)((int)c1 + (int)c2);

Не? Как именно твой компилятор это будет считать - вопрос другой, и в стандарте об этом не рассказано (во всяком случае, ни я ни ты не нашёл пока). Умножение также можно обрезать с тем же результатом: 100*100 == 10000 == 16(mod256) == 10000%256 == (int)100*(int)100 == (char)100*(char)100;

А с учетом того, что они это говорят, когда определяют операторы арифметики, то я могу предположить, что для типов меньше int они вообще формально не определены. ;-)

ну наконец-то вам оно стало понятно. На самом деле, если вам так будет угодно, вы можете _представлять_, что ваши char превращаются в int, умножаются, а потом опять кастрируются до char. В силу того, что результат идентичен - вы имеете на это право. Однако, IRL я наблюдал, что эти ваши char(типы меньшие, чем int) просто так берут, и умножаются. Без всяких преобразований.

Ведь (char)1 << 1, обязан дать тип int, так же как и (char)1 << (char)x — результат int).

слово «обязан» мне тут непонятно.

Можно об этом думать так: каждый арифметический оператор содержит единицу (а у нее, как мы знаем, тип int). То есть умножение это 1*, деление 1/. Отсюда, какой тип будет у (char)1 * (char)1 * 1? Правильно, int, даже без integer promotion, а по arithmetic conversions (6.3.1.8).

Кто вам сказал, что у (char)1 тип int? Нет, у (char)1 тип char. С тем же успехом можно доказать, что 1* имеет тип double или любой другой.

По вашему, j и k должны были бы быть равны... да и размер результата как-то не сходится — на long long не тянет.

по моему младшие типы автоматически преобразуются в старшие, если есть старший операнд. По стандарту - тоже. Потому тут j != k. Не вижу в этом ничего странного и противоречивого. i*INT_MAX - тут всё имеет тип int, и результат тоже. Берётся только 32 бита 64х битного произведения. Что в этом странного? Но если i это 64и бита, то INT_MAX тоже расширяется, и берётся(или вычисляется) только 64 младших бита. Что мы и наблюдаем.

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

в правой части операнды СНАЧАЛА продвинутся до int, тип rvalue будет int со значением 400, а потом произойдет неявное приведение к lvalue с потерей результата.

включи голову, и обдумай, что будет, если НЕ продвинуться до int, а так и останутся char. Это как-то повлияет на результат? Нет.

наркоман штоле? судя твоему заявлению следующий код (с++), скомпилированный на разных компиляторах будет работать по-разному.

ну... Хоть что-то новое ты узнал... Открой для себя gcc -S, и узнай, что _работать_ будет действительно по разному, а результат будет одинаковый. Правда не в этом случае, ибо на x86 ты просто не сможешь отдать функции тип меньший чем int, потому в данном случае компилятор конечно переведёт всё в int, ибо ему нужен результат int.

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

что будет, если НЕ продвинуться до int, а так и останутся char. Это как-то повлияет на результат? Нет.

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

/tmp$ cat c.cxx
#include <stdio.h>

void foo(int c) { printf("int\n"); }
void foo(char c) { printf("char\n"); }

int main()
{
        char a=1,b=1;

        foo(a);
        foo(a * b);
        return 0;
}
/tmp$ g++ -Wall c.cxx && ./a.out 
char
int

«Давай, до свидания». ;-)

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

И каким хреном сюда замешаны плюсы?

Писал бы на сях:

#include <stdio.h>

main(){
	char a, b = 5, c = 9;
	a = b * c;
	printf("%hhd * %hhd = %hhd\n", b,c,a);
	// и жопа:
	b = 12, c = 17;
	a = b * c;
	printf("%hhd * %hhd = %hhd\n", b,c,a);
}

И вот:

gcc 1.c && ./a.out 
5 * 9 = 45
12 * 17 = -52
Кстати, о чем вы там спорите?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от hexdump01010101

что будет, если НЕ продвинуться до int, а так и останутся char. Это как-то повлияет на результат? Нет.

Да. Изменится тип, размер результата в байтах.

в нашем случае тип результата - char.

char a=1,b=1;

foo(a);
foo(a * b);

«Давай, до свидания». ;-)

я всё не пойму, ЧТО вы мне пытаетесь доказать? Что компилятор для вычисления

char x = 1, y = 1;
char z = x + y;

просто таки ОБЯЗАН переводить x, y в int?

Ну и где такое записано?

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

Ну, вообще-то вот:

…
printf("sz: %d, %d, %d\n", sizeof(b), sizeof(c), sizeof(b*c));
…
печатает:
sz: 1, 1, 4
Похоже, такой уж глюк у gcc

// int будет и при любой другой арифметической операции с b и c

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

Ну и где такое записано?

В стандарте, вам уже приводили текст.

Мне кажется, вы путаете язык с компилятором и его выходом. То, что если вы не используете информацию о типе и компилятор выплевывает ассемблерный код, который будет оперировать хоть чёртом лысым, никак не говорит о том, что типы изменились на «чёрта лысого». Просто в данном случае тип был не важен, и компилятор сделал то, что было выгоднее. Если ему будет выгоднее посчитать char * char с помощью FPU, он это сделает, но это никак не изменит типы внутри исходника.

Как только типы будут важны (а вам уже и эти примеры приводили), то все ваши непонимания типов сразу же выльются в ошибки. И снова вам приводили примеры.

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

Похоже, такой уж глюк у gcc
// int будет и при любой другой арифметической операции с b и c

http://images3.wikia.nocookie.net/__cb20120510225719/sonic/images/3/31/Faceho...

(Признайтесь, вы просто хотите еще больше поней, поэтому и не читаете тред? :-) Тут речь идет именно о том, что это не глюк, а стандарт.

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

Похоже, такой уж глюк у gcc

sizeof - сущность, известная и вычисляемая на этапе компиляции. Она вообще-то не имеет никакого отношения к тому, как будет вычисляться b*c. А вычисляться оно будет совершенно как угодно компилятору, он может его вычислить вообще по 16 штук сразу, загоняя их в SSE2, а это возможно только для байтов с результатом в байт. За то это намного быстрее, чем считать произведение 16и 32х битных чисел. Именно потому, в стандарте не определён порядок приведения чисел, лишь сказано о том, что результат должен получаться правильный.

И вообще, нет никакого способа определить тип a*b, во всяком случае мне он не известен. sizeof тут явно не поможет, и вообще не причём.

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

Просто в данном случае тип был не важен, и компилятор сделал то, что было выгоднее.

забирайте лошадку обратно. Я именно об этом с самого начала. IRL будет считаться хрен знает как, как удобнее, как быстрее, как проще, но никак НЕ путём преобразования аргументов в int и результата из int куда надо. И это несмотря на то, что по стандарту 100*100 должно получится как (char)10000 == 16. Всё дело в том, что 100*100 это и есть 16 в типе char, и никакой ошибки тут НЕТ.

drBatty ★★
()
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	char a = 'a';
	char b = 'c';

	char c = a * b;

	printf("%c * %c = '%c'\n",a, b, c);

	return EXIT_SUCCESS;
}

В чем прикол? Что трудного то?

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

забирайте лошадку обратно. Я именно об этом с самого начала.

Да вы же наркоман! Вы даже сейчас говорите какой-то кромешный ужас. Лошадку не заберу. :-)

это несмотря на то, что по стандарту 100*100 должно получится как (char)10000 == 16. Всё дело в том, что 100*100 это и есть 16 в типе char, и никакой ошибки тут НЕТ.

Вы до сих пор не отделяете, то, что происходит на уровне языка, и то, что происходит на уровне инструкций. Это грустно. Никто не спорил по поводу результата, спорили по поводу типов.

Тип бывает важен, и то, что происходит на уровне языка вам уже показали примерами, sizeof и с перегруженной функцией foo: foo(char) и foo(int). По *стандарту* *тип* у «char * char», будет *int*. Всё. Новую лошадку искать лень. nuff said.

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

И вообще, нет никакого способа определить тип a*b

typeof, но это расширение. В Си сам тип не столько важен, сколько важен размер. Вы думаете, что sizeof(char * char) не определен по стандарту, а он определен. Это ваше главное заблуждение, которое легко трансформируется в реальные ошибки, как было показано.

А в С++ перегрузкой определяется легко. И вам тоже пример показывали.

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

Вы до сих пор не отделяете, то, что происходит на уровне языка, и то, что происходит на уровне инструкций. Это грустно. Никто не спорил по поводу результата, спорили по поводу типов.

что касается языка, то как не считай, но 100*100 == 16, если результат в char. Это если подойти «как должно быть по стандарту». Если задаться вопросом «как работает?», то уровня языка вам не хватит, ибо как не считай, получается 16.

Ну не интересно «как работает?», всё, заканчиваем спор, остаёмся каждый со своим мнением.

Тип бывает важен, и то, что происходит на уровне языка вам уже показали примерами, sizeof и с перегруженной функцией foo: foo(char) и foo(int). По *стандарту* *тип* у «char * char», будет *int*.

для функций параметры передаются через стек, и если стек у вас имеет гранулярность 4, то и любой аргумент функции будет 4 или кратен 4. Потому, любой тип вроде char, который короче 4, придётся преобразовать в int размером 4. И что? Эти особенности 32х битной реализации x86 разве что-то доказывают?

Про sizeof() - весьма неожиданно конечно, тут не знаю, что за глюк, и что он тоже показывает 4. Какие-то sizeofпроблемы.

Всё. Новую лошадку искать лень. nuff said.

жаль... Ну ладно. Хотя вы таки и не доказали того «факта», что арифметическое выражение ОБЯЗАНО иметь тип int или старше.

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

typeof, но это расширение.

угу. не пруф. да и никто не гарантирует, что сегодня у вас int, а завтра у меня ВНЕЗАПНО будет таки char

Вы думаете, что sizeof(char * char) не определен по стандарту, а он определен. Это ваше главное заблуждение

вообще-то я вообще не понял, при чём тут sizeof?

А в С++ перегрузкой определяется легко. И вам тоже пример показывали.

это архитектурные детали реализации. В 32х битный стек нельзя пихать 8и битные char'ы. Вот и всё. Именно потому компилятор всё и фигачит в int, кроме случая, когда тип _точно_ совпадает с тем, что в прототипе. Уверен, unsigned char оно тоже переведёт именно в int, если не будет прототипа именно для uchar, и если архитектура с char'ами как с параметрами работать не умеет.

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

жаль... Ну ладно. Хотя вы таки и не доказали того «факта», что арифметическое выражение ОБЯЗАНО иметь тип int или старше.

The rules, then (which you can also find on page 44 of K&R2, or in section 6.2.1 of the newer ANSI/ISO C Standard) are approximately as follows:

First, in most circumstances, values of type char and short int are converted to int right off the bat.

http://www.eskimo.com/~scs/cclass/int/sx4cb.html

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

мы говорим про типы результатов выражений в языке, а не о том, как компилятор «может» их посчитать. для определения этих типов существуют правила, называемые правилами продвижения типов в выражениях, благодаря которым можно определить тип ЛЮБОГО выражения вида «a op b», вне зависимости от типов операндов. и в этих правилах ни lvalue, ни конкретная архитектура никакой роли не играют.

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

First, in most circumstances, values of type char and short int are converted to int right off the bat.

in most circumstances

и?

да, я знаю эти случаи - часто результат должен быть int, или это символьная константа, или это число-константа... Да мало-ли что? Но это НЕ пруф.

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

тип ЛЮБОГО выражения вида «a op b», вне зависимости от типов операндов. и в этих правилах ни lvalue, ни конкретная архитектура никакой роли не играют.

ладно... что воду в ступе толочь?..

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

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

т.е результат char + double + float очевидно типа double, но можно сэкономить на первом пункте и не продвигать char до int (по ссылке список пунктов смотри).

а не к тому, что продвижение вообще происходит в большинстве случаев. я так считаю.

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

т.е результат char + double + float очевидно типа double, но можно сэкономить на первом пункте и не продвигать char до int (по ссылке список пунктов смотри).

т.е. ты допускаешь, что в результате char = char + char можно таки сэкономить на всех ЧЕТЫРЁХ пунктах, если архитектура умеет складывать char напрямую? (а x86 умеет. Нативно. А с SSE2 аж по 16 штук сразу)

а не к тому, что продвижение вообще происходит в большинстве случаев. я так считаю.

ну считай. Я не против. Я тебе больше скажу - в ваших примерах, в рантайме, НИЧЕГО не вычисляется, ибо константный char * константный char есть константный char, по стандарту являющийся константным int. Т.ч. sizeof таки прав, ибо (char)2 + (char)2 вообще-то будет 4. А у константы 4 тип таки int. Я тут не спорю.

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

Не тупи, понятно переменных без типа нет. Не так ты меня понял.

maxylopes
() автор топика
Ответ на: комментарий от drBatty

Надеяться на то, что компилятор преобразует char в int ИМХО - плохая идея.

Вообще то и произойдёт приведение типа char к int , так как не происходит потери данных тип char можно свободно использовать в любых выражениях с любыми целыми толь конечно нужно точно знать char unsigned или signed вот тут то и кроется неопределённость.

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

«в большинстве случаев» указано к тому,

не к тому. вспомни систему команд тогдашних ЭВМ, и подумай, каким будет первый шаг, при обработке любых вычислений? правильно - перевод к целым int, ибо _считать_ что-то другое компы тогда попросту не умели. Но это было очень давно. Наша x86 настолько костыльная, что умеет и по 8, и по 16, и по 32 и по 64 бита считать.

drBatty ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.