LINUX.ORG.RU

Вывести char в числовом виде


0

0

У меня есть строка. Нужно вывести ASCII-код n-го символа. Я делаю так: printf("%d\n",(int)(szstr[n])), но это не работает, потому что выводятся неправильные значения. Проверял написанным ранее дампом.

anonymous

Может быть просто printf("%d\n",szstr[n])

Или printf("%d\n",(unsigned char)szstr[n])

Как я понимаю, int это 4 байта (на 32-битной платформе), а размер символа строки - 1 байт. Поэтому приведённый вариант с преобразованием к int будет брать 4 байта, идущих подряд, ещё и перевернёт их и выдаст как одно число. Или я неправ?

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

bash-2.05b$ cat test.c
#include <stdio.h>

int main(int ac, char** av)
{
   char** it_av = av+1;
   char*  it = 0;
   for(;it_av != av+ac; ++it_av)
   {
      for(it = *it_av; *it; ++it)
         printf("%c:%d ",*it,(unsigned int)(*it));
      printf("\n");
   }
   return 0;
}

bash-2.05b$ ./test aaa baba АБЫРВАЛГ
a:97 a:97 a:97 
b:98 a:97 b:98 a:97 
А:-31 Б:-30 Ы:-7 Р:-14 В:-9 А:-31 Л:-20 Г:-25 
bash-2.05b$ 

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

Приведение к типу нужно обязательно потому, что %d ожидает именно
sizeof(int) байт на стеке, и если приведения не сделать, то можно
испортить стек особенно последствия будут печальные в случае scanf...

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

>printf("%hhd\n", szstr[n]);

я делаю так:

printf("%02hhX\n", (unsigned char)szstr[n]);

а в твоём случае приведение к unsigned обязательно ибо избавляет от массы глюков:

printf("%hhu\n", (unsigned char)szstr[n]);

cvv ★★★★★
()

OK. Если написать:

char ch=szstr[n];
unsigned int i=(unsigned int)n;
printf("%d\n",i);

то будет та же ошибка. Здесь в printf все честно: заявлено 4 байта и подано 4 байта. Например, я точно знаю, что определенный символ в этой строке равен 128, а printf выводит -128. В другом случае -125, хотя должно быть 83.
2Flogger_d
В вашем примере тоже есть отрицательные значения:

А:-31 Б:-30 Ы:-7 Р:-14 В:-9 А:-31 Л:-20 Г:-25

Но ASCII-коды не могут быть отрицательными.

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

> Только не очень ясно что означает %hhu

man 3 printf

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

> char** it_av = av+1;
> char* it = 0;

Почему не unsigned char? Естественно, нужно сильно извратиться, чтобы заданное отрицательное значение превратить в неотрицательное из соображений диапазона char.

Как-то мне не очень нравится:
char a;
...(unsigned char)a...

IMHO, с точки зрения здравого смысла(пусть это даже допускается стандартом) это бред. В то же время как
unsigned char a;
..."%lu"...(unsigned long)a...

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

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

Можно char -> unsigned char -> int Так - работает.
...
         printf("%c:%d ",*it,(int)(unsigned char)*it);

bash-2.05b$ ./test АБЫРВАЛГ
А:225 Б:226 Ы:249 Р:242 В:247 А:225 Л:236 Г:231 

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

>а разве в случае когда принтфу передаётся char в стэк не 4 байта кладётся??

в стек кладётся не char расширенный до 4 байт а указатель на первый байт char.

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

>>а разве в случае когда принтфу передаётся 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

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

иногда. точнее если мы имеем дело с самостоятельной переменной а не массивом:

char a; char b[4];

по дефолту sizeof(a)==sizeof(b) хотя компилер об етом никому не говорит.

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

movsbl	-1(%ebp),%eax  /* ну это и есть неявно */
pushl	%eax           /* а потом передаётся */

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

> 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));                                                          
}                                                                                                                                                               

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

bash-2.05b$ cat >test_so.c #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)); } bash-2.05b$ gcc -o test_so test_so.c bash-2.05b$ ./test_so sizeof(a) = 8 sizeof(a.a) = 1 sizeof(a.b) = 4 bash-2.05b$

собственно о чём я и речь веду.

>sizeof(a.a) = 1

здесь компилер врёт. реально он юзает под переменную 4 байта но никому об етом не сообщает ибо результаты одинаковы

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

>cvv имел в виду sizeof(a)==sizeof(b[0]) :)

нет

то что почти всегда sizeof(a)!=sizeof(b[0]) если sizeof(a)!=4 есть известный факт

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

такая вот щтука ))

void func(const char *dummy, ...)                                               
{                                                                               
    int *ptr = (int *) &dummy;                                                  
    while (*++ptr != -1) {                                                      
        printf("%08x (%c)\n", *ptr, *ptr);                                      
    }                                                                           
}                                                                               
                                                                                
int main()                                                                      
{                                                                               
    func("foo", 'b', 'a', 'r', -1);                                             
}

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

>реально он юзает под переменную 4 байта но никому об етом не сообщает

Это называется выравниванием. Под переменную он использует 1 байт, после которого идет заполнение, т.е. пропуск на 3 байта, чтобы по адресу, кратному 4-м находилась следующая переменная.

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

                                                                                
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?
Что-то найти не могу.

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

> А как выравнивание переменных в структуре задать в опциях gcc?

-fpack-struct, ещё есть прагма, но атрубут всё-таки красивей))

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

>Это называется выравниванием.

>Под переменную он использует 1 байт, после которого идет заполнение, т.е. пропуск на 3 байта, чтобы по адресу, кратному 4-м находилась следующая переменная.

не совсем так. три байта "заполнения" не являются холостыми/неиспользуемыми. при большинстве операций gcc их использует как часть переменной вместе с основным байтом.

Это связано со спецификой интеловских процов которые в другом случае будут тормозить

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

>Можно char -> unsigned char

Много что можно. Но нужно ли?
Что по-твоему происходит при преобразовании char->unsigned char?
Интересует та часть диапазона char, которая не попадает в unsigned char.

Только бреда про всякие 4 байта, стек и прочую ахинею, которой полна эта ветка не надо. Дай чисто формальный ответ.

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

Для того чтобы получить числовое значение символа достаточно сделать так: char ch = 'a'; unsigned int c = ch-'0'; В c будет храниться числовое значение ch.

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

>Для того чтобы получить числовое значение символа достаточно сделать так: char ch = 'a'; unsigned int c = ch-'0'; В c будет храниться числовое значение ch.

Будет храниться не числовое значение символа а разность между числовым значением символа и числовым значением символа '0'

Вот некоторые из правильных ответов, которые были здесь представлены.

printf("%d\n",(unsigned char)szstr[n]);

printf("%hhu\n", szstr[n]);

printf("%d\n",szstr[n] & 0xff);

Вы перед тем как давать ответ, проверяйте.

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

>printf("%d\n",(unsigned char)szstr[n]);

Здесь есть все шансы получить sigsegv. в корне не верное решение.

>printf("%hhu\n", szstr[n]);

это будет работать но временами будет глючить, например на русских буквах.

>printf("%d\n",szstr[n] & 0xff);

работоспособно только при дефолтных настройках компилера. при добавлении некоторых ключей - sigsegv без проблем

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

>>printf("%d\n",(unsigned char)szstr[n]);

>Здесь есть все шансы получить 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). Утешает то, что на практике вместо этих опций принято явно указывать тип константы.

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

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

> то что почти всегда sizeof(a)!=sizeof(b[0]) если sizeof(a)!=4 есть известный факт


Был в ахуе от известности факта.
Можно пару примерчиков, где бы этот известный факт давал о себе знать?

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

Или имелся в виду тот самый случай компиляции на сильнофрагментированном сказёвом винте, когда кориолисова сила периодически становится отрицательной?

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

>Можно пару примерчиков, где бы этот известный факт давал о себе знать?

если мне не изменяет память info gcc

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

Конкретно. Хотя, конечно лучше, чем "где-то видел".
Искать я конечно не буду, ибо даже если там что-то и сказано, то знак
равенства между "почти всегда" и "info gcc" вызывающе сомнителен.
Как-то в практике мне это "почти всегда" ни разу не попадалось.
Наверное я не тем монитором владею.

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