У меня есть строка. Нужно вывести ASCII-код n-го символа. Я делаю так: printf("%d\n",(int)(szstr[n])), но это не работает, потому что выводятся неправильные значения. Проверял написанным ранее дампом.
Как я понимаю, int это 4 байта (на 32-битной платформе), а размер символа строки - 1 байт. Поэтому приведённый вариант с преобразованием к int будет брать 4 байта, идущих подряд, ещё и перевернёт их и выдаст как одно число. Или я неправ?
Приведение к типу нужно обязательно потому, что %d ожидает именно
sizeof(int) байт на стеке, и если приведения не сделать, то можно
испортить стек особенно последствия будут печальные в случае scanf...
char ch=szstr[n];
unsigned int i=(unsigned int)n;
printf("%d\n",i);
то будет та же ошибка. Здесь в printf все честно: заявлено 4 байта и подано 4 байта. Например, я точно знаю, что определенный символ в этой строке равен 128, а printf выводит -128. В другом случае -125, хотя должно быть 83.
2Flogger_d
В вашем примере тоже есть отрицательные значения:
Почему не unsigned char? Естественно, нужно сильно извратиться, чтобы заданное отрицательное значение превратить в неотрицательное из соображений диапазона char.
Как-то мне не очень нравится:
char a;
...(unsigned char)a...
IMHO, с точки зрения здравого смысла(пусть это даже допускается стандартом) это бред. В то же время как
unsigned char a;
..."%lu"...(unsigned long)a...
простое преобразование к типу, диапазон которого включает исходный.
>>а разве в случае когда принтфу передаётся char в стэк не 4 байта кладётся??
>в стек кладётся не char расширенный до 4 байт а указатель на первый байт char.
Содержимое:
#include <stdio.h>
void aaa()
{
char x = 'A';
printf("%c\n",x);
}
пропускаем чарез gcc -S:
movb $65, -1(%ebp) /* char x = 'A' */
subl $8, %esp
movsbl -1(%ebp),%eax /* x в EAX с расширением до 4-х байтов */
pushl %eax /* EAX, т.е. x в стек */
pushl $LC0 /* адрес на "%c\n" в стек */
call _printf
> sizeof(a)==sizeof(b)
да как такое может быть?? sizeof(char) всегда ведь единица!
Другое дело как оно в памяти будет лежать...а вот если их в структуру запихнуть, то реально sizeof совсем сходится ))
#define EVAL(x) printf(#x " = %d\n", x)
int main() {
struct A {
char a;
int b;
} a;
EVAL(sizeof(a));
EVAL(sizeof(a.a));
EVAL(sizeof(a.b));
}
>реально он юзает под переменную 4 байта но никому об етом не сообщает
Это называется выравниванием. Под переменную он использует 1 байт, после которого идет заполнение, т.е. пропуск на 3 байта, чтобы по адресу, кратному 4-м находилась следующая переменная.
int main()
{
struct {
char a;
int b __attribute__((aligned(8)));
}a;
printf("%u\n",sizeof(a));
}
int main()
{
struct {
char a;
int b __attribute__((packed));
}a;
printf("%u\n",sizeof(a));
}
А как выравнивание переменных в структуре задать в опциях gcc?
Что-то найти не могу.
>Под переменную он использует 1 байт, после которого идет заполнение, т.е. пропуск на 3 байта, чтобы по адресу, кратному 4-м находилась следующая переменная.
не совсем так. три байта "заполнения" не являются холостыми/неиспользуемыми. при большинстве операций gcc их использует как часть переменной вместе с основным байтом.
Это связано со спецификой интеловских процов которые в другом случае будут тормозить
Много что можно. Но нужно ли?
Что по-твоему происходит при преобразовании char->unsigned char?
Интересует та часть диапазона char, которая не попадает в unsigned char.
Только бреда про всякие 4 байта, стек и прочую ахинею, которой полна эта ветка не надо. Дай чисто формальный ответ.
Для того чтобы получить числовое значение символа достаточно сделать так: char ch = 'a'; unsigned int c = ch-'0'; В c будет храниться числовое значение ch.
>Для того чтобы получить числовое значение символа достаточно сделать так: char ch = 'a'; unsigned int c = ch-'0'; В c будет храниться числовое значение ch.
Будет храниться не числовое значение символа а разность между числовым значением символа и числовым значением символа '0'
Вот некоторые из правильных ответов, которые были здесь представлены.
>Здесь есть все шансы получить sigsegv. в корне не верное решение.
В функциях с элипсами (func(...)) или в необъявленную функцию для целых параметр неявно преобразуется к int, если его sizeof(type)<sizeof(int). sigsegv будет, если имеем баг в компиляторе.
>>printf("%hhu\n", szstr[n]);
>это будет работать но временами будет глючить, например на русских буквах.
С чего ты так решил?
>>printf("%d\n",szstr[n] & 0xff);
>работоспособно только при дефолтных настройках компилера. при добавлении некоторых ключей - sigsegv без проблем
Если эта опция компилера влияет на представление типа целочисленных констант (ты это имел в виду?), то для случая, когда sizeof(0xff)<sizeof(int), см. коментарий к первому примеру. Если sizeof(0xff)>sizeof(int) может быть глюк (но не sigsegv), из-за того, что из стека будет выбрано байт меньше, чем там реально находится. Глюк "лечится" элементарным приемом - printf("%d\n",szstr[n] & (int)0xff). Утешает то, что на практике вместо этих опций принято явно указывать тип константы.
По-моему ты на уровне подсознания не избавился от той мысли, что в стек передается адрес.
Конкретно. Хотя, конечно лучше, чем "где-то видел".
Искать я конечно не буду, ибо даже если там что-то и сказано, то знак
равенства между "почти всегда" и "info gcc" вызывающе сомнителен.
Как-то в практике мне это "почти всегда" ни разу не попадалось.
Наверное я не тем монитором владею.