Як перетворити байтовий масив у шістнадцятковий рядок в C?


88

У мене є:

uint8 buf[] = {0, 1, 10, 11};

Я хочу перетворити байтовий масив у рядок таким чином, щоб я міг надрукувати рядок за допомогою printf:

printf("%s\n", str);

і отримати (двокрапки не потрібні):

"00:01:0A:0B"

Будь-яка допомога буде вдячна.


buf[i]повинен бути unsigned charbuf[i] > 127buf_ptr += sprintf(buf_ptr, "%02X", (unsigned char)buf[i]);
кастирований, інакше він переллється,

Відповіді:


93
printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);

для більш загального способу:

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) printf(":");
    printf("%02X", buf[i]);
}
printf("\n");

для конкатенації в рядок є кілька способів це зробити ... я б, мабуть, тримав вказівник на кінець рядка і використовував sprintf. Ви також повинні відстежувати розмір масиву, щоб переконатися, що він не стає більшим, ніж виділений простір:

int i;
char* buf2 = stringbuf;
char* endofbuf = stringbuf + sizeof(stringbuf);
for (i = 0; i < x; i++)
{
    /* i use 5 here since we are going to add at most 
       3 chars, need a space for the end '\n' and need
       a null terminator */
    if (buf2 + 5 < endofbuf)
    {
        if (i > 0)
        {
            buf2 += sprintf(buf2, ":");
        }
        buf2 += sprintf(buf2, "%02X", buf[i]);
    }
}
buf2 += sprintf(buf2, "\n");

Дякую, Марк - моя проблема дещо складніша. У мене насправді є буфер довжиною X байт. Я сподівався знайти загальний спосіб зробити це для X байтів і мати в результаті рядок.
Стів Уолш,

Просто оновлено, щоб додати код для обробки будь-якої заданої кількості байтів ... припускаючи, що х - довжина.
Mark Synowiec

Ще раз спасибі, Марк, але найскладнішим у цій проблемі я вважаю те, як надрукувати це у рядок.
Стів Уолш,

5
printf("%02X", (unsigned char)buf[i]);слід використовувати, оскільки оригінал спричинить переповнення для непідписаних символів
easytiger

3
Чому ні printf("%02hhX", buf[i])?
Хінтрон,

32

Для комплектуди ви також можете легко це зробити, не викликаючи жодної важкої функції бібліотеки (ні snprintf, ні strcat, навіть memcpy). Це може бути корисно, скажімо, якщо ви програмуєте якийсь мікроконтролер або ядро ​​ОС, де libc недоступний.

Нічого по-справжньому вигадливого, навколо ви можете знайти подібний код, якщо погуглити. Насправді це не набагато складніше, ніж виклик snprintf, і набагато швидше.

#include <stdio.h>

int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    int i = 0;
    for(; i < sizeof(buf)-1; ++i){
        *pout++ = hex[(*pin>>4)&0xF];
        *pout++ = hex[(*pin++)&0xF];
        *pout++ = ':';
    }
    *pout++ = hex[(*pin>>4)&0xF];
    *pout++ = hex[(*pin)&0xF];
    *pout = 0;

    printf("%s\n", str);
}

Ось ще одна коротша версія. Це просто уникає проміжної змінної індексу i та дублювання останнього коду регістру (але кінцевий символ записується два рази).

#include <stdio.h>
int main(){
    unsigned char buf[] = {0, 1, 10, 11};
    /* target buffer should be large enough */
    char str[12];

    unsigned char * pin = buf;
    const char * hex = "0123456789ABCDEF";
    char * pout = str;
    for(; pin < buf+sizeof(buf); pout+=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
    }
    pout[-1] = 0;

    printf("%s\n", str);
}

Нижче наведено ще одну версію відповіді на коментар, в якій я сказав, що я використав "фокус", щоб дізнатись розмір вхідного буфера. Насправді це не фокус, а необхідні вхідні знання (вам потрібно знати розмір даних, які ви перетворюєте). Я зрозумів це, витягнувши код перетворення в окрему функцію. Я також додав код перевірки межі для цільового буфера, що насправді не потрібно, якщо ми знаємо, що робимо.

#include <stdio.h>

void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
    unsigned char * pin = in;
    const char * hex = "0123456789ABCDEF";
    char * pout = out;
    for(; pin < in+insz; pout +=3, pin++){
        pout[0] = hex[(*pin>>4) & 0xF];
        pout[1] = hex[ *pin     & 0xF];
        pout[2] = ':';
        if (pout + 3 - out > outsz){
            /* Better to truncate output string than overflow buffer */
            /* it would be still better to either return a status */
            /* or ensure the target buffer is large enough and it never happen */
            break;
        }
    }
    pout[-1] = 0;
}

int main(){
    enum {insz = 4, outsz = 3*insz};
    unsigned char buf[] = {0, 1, 10, 11};
    char str[outsz];
    tohex(buf, insz, str, outsz);
    printf("%s\n", str);
}

1
Це не фокус, просто константа. У контексті питання стає зрозуміло, що довжина джерела, яке ми хочемо перетворити в шістнадцяткове, добре відома (я міг би поставити кілька жорстко закодованих 4 замість sizeof). У загальному випадку функцію слід викликати на якомусь вході відомої довжини, а цільовий буфер має 3 рази + 1 байт. Це повинно забезпечити абонент, що не має жодної причини для функції перетворення виконувати це завдання. Виклик strlen () може бути способом пошуку розміру джерела в деяких випадках, але не завжди. Що робити, якщо число, яке потрібно перетворити в шістнадцяткове, містить нулі?
kriss

Натхненний вашою функцією, я написав версію, яка також повертає кількість байтів, записаних у вихідний буфер, подібно до snprintf
Джейсон Пепас

Я думаю, вам слід автоматично зробити правильним розміром вихідний буфер, використовуючи char str [sizeof (buf) * 3 + 1];
Сесіл Уорд

Також набагато більше змагань захистить вас. Наприклад, "const unsigned char const * p", щоб ви могли переконатися, що вхідні буфери не записані. Один робить адресу (або «вказівник») постійною або змінною, а інший робить пам’ять за цією адресою лише для читання чи ні. Часто перешкоджає змішуванню покажчиків. А також, маючи значущі імена, які документи, які буфери та вказівники призначені для введення та виведення, теж допомогли б.
Сесіл Уорд

@Cecil War: якщо мій код не є фальшивим, використання const не захистить багато, за винятком випадків, коли ви кажете змішування покажчиків або використання того самого вказівника для введення та виведення (добре, все ще можливо). Але це також допоможе компілятору оптимізувати код. Ще краще було б також використовувати ключове слово restrict (дуже погано C99, не C ++, але часто існує як розширення компілятора). Що ви хочете більше означати, щоб викликати вхідний inта вихідний буфер out? Я також міг би обрати використання рядка і повернення копії замість надання буфера виводу, в сучасних оптимізаторах C ++ досить добре, щоб їх не хвилювати.
kriss

15

Ось метод, який набагато швидший:

#include <stdlib.h>
#include <stdio.h>

unsigned char *     bin_to_strhex(const unsigned char *bin, unsigned int binsz,
                                  unsigned char **result)
{
  unsigned char     hex_str[]= "0123456789abcdef";
  unsigned int      i;

  if (!(*result = (unsigned char *)malloc(binsz * 2 + 1)))
    return (NULL);

  (*result)[binsz * 2] = 0;

  if (!binsz)
    return (NULL);

  for (i = 0; i < binsz; i++)
    {
      (*result)[i * 2 + 0] = hex_str[(bin[i] >> 4) & 0x0F];
      (*result)[i * 2 + 1] = hex_str[(bin[i]     ) & 0x0F];
    }
  return (*result);
}

int                 main()
{
  //the calling
  unsigned char     buf[] = {0,1,10,11};
  unsigned char *   result;

  printf("result : %s\n", bin_to_strhex((unsigned char *)buf, sizeof(buf), &result));
  free(result);

  return 0
}

3
Цей код містить помилку, яка виявляється лише на дивних недрукованих входах (не встигли розібратися, що саме відбувається математично). Спробуйте закодувати двійковий шістнадцятковий код, ca9e3c972f1c5db40c0b4a66ab5bc1a20ca4457bdbe5e0f8925896d5ed37d726і ви ÌaÌe3cÌ72f1c5dÌ40c0b4a66Ìb5bÌ1Ì20cÌ4457bÌbÌ5Ì0Ì8Ì258Ì6Ì5Ìd37Ì726вийдете. Щоб це виправити, біт усередині hex_strпершого рядка циклу for потрібно змінити на такий, (input[i] >> 4) & 0x0Fяк у відповіді @ kriss. Тоді це працює нормально.
niemiro

Помилка - не перевіряє помилку malloc ().
Сесіл Уорд,

Завжди краще використовувати беззнакові символи абсолютно скрізь, оскільки ніхто не хоче ризику підписаних символів (божевільна апаратна функція DEC PDP11), і таким чином ви не ризикуєте помилкових порівнянь помилитися або підписані правильні зрушення зіпсують значення. У цьому випадку, справедливості заради, код захисно робить & 0x0F скрізь, що захищає вас тут.
Сесіл Уорд

Вхідним параметром bin повинен бути const unsigned char const * bin, щоб оголосити пам'ять лише для читання для цілей цієї процедури.
Сесіл Уорд

1
Я інтегрував пропозиції Сесіла Уорда, дякую за відгук
Янут,

14

Подібні відповіді вже існують вище, я додав цю, щоб пояснити, як саме працює наступний рядок коду:

ptr += sprintf(ptr, "%02X", buf[i])

Це тихо хитро і непросто зрозуміти, я пояснив пояснення в коментарях нижче:

uint8 buf[] = {0, 1, 10, 11};

/* Allocate twice the number of bytes in the "buf" array because each byte would
 * be converted to two hex characters, also add an extra space for the terminating
 * null byte.
 * [size] is the size of the buf array */
char output[(size * 2) + 1];

/* pointer to the first item (0 index) of the output array */
char *ptr = &output[0];

int i;

for (i = 0; i < size; i++) {
    /* "sprintf" converts each byte in the "buf" array into a 2 hex string
     * characters appended with a null byte, for example 10 => "0A\0".
     *
     * This string would then be added to the output array starting from the
     * position pointed at by "ptr". For example if "ptr" is pointing at the 0
     * index then "0A\0" would be written as output[0] = '0', output[1] = 'A' and
     * output[2] = '\0'.
     *
     * "sprintf" returns the number of chars in its output excluding the null
     * byte, in our case this would be 2. So we move the "ptr" location two
     * steps ahead so that the next hex string would be written at the new
     * location, overriding the null byte from the previous hex string.
     *
     * We don't need to add a terminating null byte because it's been already 
     * added for us from the last hex string. */  
    ptr += sprintf(ptr, "%02X", buf[i]);
}

printf("%s\n", output);

Блискуча логіка. Шукав годину для елегантної відповіді на цей виклик без C ++!
Марк

6

Я просто хотів додати наступне, навіть якщо це трохи не тематично (не стандартно C), але я часто шукаю його і натрапляю на це питання серед перших пошукових запитів. Функція друку ядра Linux printk, також має специфікатори формату для виведення вмісту масиву / пам'яті "безпосередньо" через специфічний специфікатор формату:

https://www.kernel.org/doc/Documentation/printk-formats.txt

Raw buffer as a hex string:
    %*ph    00 01 02  ...  3f
    %*phC   00:01:02: ... :3f
    %*phD   00-01-02- ... -3f
    %*phN   000102 ... 3f

    For printing a small buffers (up to 64 bytes long) as a hex string with
    certain separator. For the larger buffers consider to use
    print_hex_dump(). 

... однак, здається, ці специфікатори формату не існують для стандартного простору користувача (s)printf.


4

Рішення

Функція btoxперетворює довільні дані *bbдо неузгодженої рядку *xpз nшістнадцяткових цифр:

void btox(char *xp, const char *bb, int n) 
{
    const char xx[]= "0123456789ABCDEF";
    while (--n >= 0) xp[n] = xx[(bb[n>>1] >> ((1 - (n&1)) << 2)) & 0xF];
}

Приклад

#include <stdio.h>

typedef unsigned char uint8;

void main(void) 
{
    uint8 buf[] = {0, 1, 10, 11};
    int n = sizeof buf << 1;
    char hexstr[n + 1];

    btox(hexstr, buf, n);
    hexstr[n] = 0; /* Terminate! */
    printf("%s\n", hexstr);
}

Результат: 00010A0B.

Прямий ефір: Tio.run .


1

Це один із способів перетворення:

#include<stdio.h>
#include<stdlib.h>

#define l_word 15
#define u_word 240

char *hex_str[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};

main(int argc,char *argv[]) {


     char *str = malloc(50);
     char *tmp;
     char *tmp2;

     int i=0;


     while( i < (argc-1)) {
          tmp = hex_str[*(argv[i]) & l_word];
          tmp2 = hex_str[*(argv[i]) & u_word];

          if(i == 0) { memcpy(str,tmp2,1); strcat(str,tmp);}
          else { strcat(str,tmp2); strcat(str,tmp);}
          i++;
    }

    printf("\n*********  %s  *************** \n", str);

}

1

Трохи модифікована версія Yannith. Просто мені подобається мати це як повернене значення

typedef struct {
   size_t len;
   uint8_t *bytes;
} vdata;

char* vdata_get_hex(const vdata data)
{
   char hex_str[]= "0123456789abcdef";

   char* out;
   out = (char *)malloc(data.len * 2 + 1);
   (out)[data.len * 2] = 0;
   
   if (!data.len) return NULL;
   
   for (size_t i = 0; i < data.len; i++) {
      (out)[i * 2 + 0] = hex_str[(data.bytes[i] >> 4) & 0x0F];
      (out)[i * 2 + 1] = hex_str[(data.bytes[i]     ) & 0x0F];
   }
   return out;
}


1

Ця функція підходить там, де користувач / абонент хоче, щоб шістнадцятковий рядок був поміщений в масив / буфер символів. Маючи шістнадцятковий рядок у буфері символів, користувач / абонент може використовувати свій власний макрос / функцію для відображення або реєстрації в будь-якому бажаному місці (наприклад, у файлі). Ця функція також дозволяє абоненту керувати кількістю (шістнадцяткових) байтів, що вводяться в кожен рядок.

/**
 * @fn 
 * get_hex
 *
 * @brief 
 * Converts a char into bunary string 
 *
 * @param[in]   
 *     buf Value to be converted to hex string
 * @param[in]   
 *     buf_len Length of the buffer
 * @param[in]   
 *     hex_ Pointer to space to put Hex string into
 * @param[in]   
 *     hex_len Length of the hex string space
 * @param[in]   
 *     num_col Number of columns in display hex string
 * @param[out]   
 *     hex_ Contains the hex string
 * @return  void
 */
static inline void
get_hex(char *buf, int buf_len, char* hex_, int hex_len, int num_col)
{
    int i;
#define ONE_BYTE_HEX_STRING_SIZE   3
  unsigned int byte_no = 0;

  if (buf_len <= 0) {
      if (hex_len > 0) {
        hex_[0] = '\0';
      }
      return;
  }

  if(hex_len < ONE_BYTE_HEX_STRING_SIZE + 1)
  {
      return;
  }

  do {
         for (i = 0; ((i < num_col) && (buf_len > 0) && (hex_len > 0)); ++i )
         {
            snprintf(hex_, hex_len, "%02X ", buf[byte_no++] & 0xff);
            hex_ += ONE_BYTE_HEX_STRING_SIZE;
            hex_len -=ONE_BYTE_HEX_STRING_SIZE;
            buf_len--;
         }
         if (buf_len > 1)
         {
             snprintf(hex_, hex_len, "\n");
             hex_ += 1;
         }
  } while ((buf_len) > 0 && (hex_len > 0));

}

Приклад: Код

#define DATA_HEX_STR_LEN 5000
    char      data_hex_str[DATA_HEX_STR_LEN];

    get_hex(pkt, pkt_len, data_hex_str, DATA_HEX_STR_LEN, 16);
    //      ^^^^^^^^^^^^                                  ^^
    //      Input byte array                              Number of (hex) byte
    //      to be converted to hex string                 columns in hex string

    printf("pkt:\n%s",data_hex_str) 

ВИХІД

pkt:
BB 31 32 00 00 00 00 00 FF FF FF FF FF FF DE E5 
A8 E2 8E C1 08 06 00 01 08 00 06 04 00 01 DE E5 
A8 E2 8E C1 67 1E 5A 02 00 00 00 00 00 00 67 1E 
5A 01 

0

Для цього в C. немає примітиву. Я, мабуть, malloc (або, можливо, aloca) досить довгий буфер і цикл над входом. Я також бачив, як це робиться з динамічною бібліотекою рядків із семантикою (але не синтаксисом!), Подібною до C ++ ostringstream, що є, імовірно, більш загальним рішенням, але це може не коштувати додаткової складності лише для одного випадку.


0

Якщо ви хочете зберегти шістнадцяткові значення у char *рядку, ви можете використовувати snprintf. Вам потрібно виділити місце для всіх надрукованих символів, включаючи нулі та двокрапку.

Розширюючи відповідь Марка:

char str_buf* = malloc(3*X + 1);   // X is the number of bytes to be converted

int i;
for (i = 0; i < x; i++)
{
    if (i > 0) snprintf(str_buf, 1, ":");
    snprintf(str_buf, 2, "%02X", num_buf[i]);  // need 2 characters for a single hex value
}
snprintf(str_buf, 2, "\n\0"); // dont forget the NULL byte

Тож тепер str_bufбуде містити шістнадцятковий рядок.


це перезаписує перші 2 символи знову і знову .. так?
xordon

0

Рішення ZincX, пристосоване для розмежувачів товстої кишки:

char buf[] = {0,1,10,11};
int i, size = sizeof(buf) / sizeof(char);
char *buf_str = (char*) malloc(3 * size), *buf_ptr = buf_str;
if (buf_str) {
  for (i = 0; i < size; i++)
    buf_ptr += sprintf(buf_ptr, i < size - 1 ? "%02X:" : "%02X\0", buf[i]);
  printf("%s\n", buf_str);
  free(buf_str);
}

0

Тут я додаю версію С ++ для всіх, хто цікавиться.

#include <iostream>
#include <iomanip>
inline void print_bytes(char const * buffer, std::size_t count, std::size_t bytes_per_line, std::ostream & out) {
    std::ios::fmtflags flags(out.flags()); // Save flags before manipulation.
    out << std::hex << std::setfill('0');
    out.setf(std::ios::uppercase);
    for (std::size_t i = 0; i != count; ++i) {
        auto current_byte_number = static_cast<unsigned int>(static_cast<unsigned char>(buffer[i]));
        out << std::setw(2) << current_byte_number;
        bool is_end_of_line = (bytes_per_line != 0) && ((i + 1 == count) || ((i + 1) % bytes_per_line == 0));
        out << (is_end_of_line ? '\n' : ' ');
    }
    out.flush();
    out.flags(flags); // Restore original flags.
}

Він надрукує шістнадцятковий формат bufferдовжини countдо std::ostream out(ви можете зробити це за замовчуванням std::cout). Кожен рядок міститиме bytes_per_lineбайти, кожен байт представляється з використанням великих двозначних шістнадцяткових символів. Між байтами буде пробіл. І в кінці рядка або в кінці буфера він надрукує новий рядок. Якщо bytes_per_lineвстановлено значення 0, він не буде друкувати new_line. Спробуйте самі.


0

Для простого використання я створив функцію, яка кодує вхідний рядок (двійкові дані):

/* Encodes string to hexadecimal string reprsentation
    Allocates a new memory for supplied lpszOut that needs to be deleted after use
    Fills the supplied lpszOut with hexadecimal representation of the input
    */
void StringToHex(unsigned char *szInput, size_t size_szInput, char **lpszOut)
{
    unsigned char *pin = szInput;
    const char *hex = "0123456789ABCDEF";
    size_t outSize = size_szInput * 2 + 2;
    *lpszOut = new char[outSize];
    char *pout = *lpszOut;
    for (; pin < szInput + size_szInput; pout += 2, pin++)
    {
        pout[0] = hex[(*pin >> 4) & 0xF];
        pout[1] = hex[*pin & 0xF];
    }
    pout[0] = 0;
}

Використання:

unsigned char input[] = "This is a very long string that I want to encode";
char *szHexEncoded = NULL;
StringToHex(input, strlen((const char *)input), &szHexEncoded);

printf(szHexEncoded);

// The allocated memory needs to be deleted after usage
delete[] szHexEncoded;

0

На підставі Yannuth в відповідь , але спрощена.

Тут передбачається, що довжина номера dest[]вдвічі більша len, і її розподілом керує абонент.

void create_hex_string_implied(const unsigned char *src, size_t len, unsigned char *dest)
{
    static const unsigned char table[] = "0123456789abcdef";

    for (; len > 0; --len)
    {
        unsigned char c = *src++;
        *dest++ = table[c >> 4];
        *dest++ = table[c & 0x0f];
    }
}

0

Я знаю, що на це питання вже є відповідь, але я думаю, що моє рішення могло б комусь допомогти.

Отже, у моєму випадку у мене був байтовий масив, що представляє ключ, і мені потрібно було перетворити цей байтовий масив у символьний масив шістнадцяткових значень, щоб роздрукувати його в одному рядку. Я витягнув свій код до такої функції:

char const * keyToStr(uint8_t const *key)
{
    uint8_t offset = 0;
    static char keyStr[2 * KEY_SIZE + 1];

    for (size_t i = 0; i < KEY_SIZE; i++)
    {
        offset += sprintf(keyStr + offset, "%02X", key[i]);
    }
    sprintf(keyStr + offset, "%c", '\0');

    return keyStr;
}

Тепер я можу використовувати свою функцію так:

Serial.print("Public key: ");
Serial.println(keyToStr(m_publicKey));

Serialobject є частиною бібліотеки Arduino і m_publicKeyє членом мого класу з наступним оголошенням uint8_t m_publicKey[32].


0

Ви можете вирішити за допомогою snprintf та malloc.

char c_buff[50];

u8_number_val[] = { 0xbb, 0xcc, 0xdd, 0x0f, 0xef, 0x0f, 0x0e, 0x0d, 0x0c };

char *s_temp = malloc(u8_size * 2 + 1);

for (uint8_t i = 0; i < u8_size; i++)
{
    snprintf(s_temp  + i * 2, 3, "%02x", u8_number_val[i]);
}

snprintf(c_buff, strlen(s_temp)+1, "%s", s_temp );

printf("%s\n",c_buff);

free(s);

ВИХІД: bbccdd0fef0f0e0d0c


-2

Які складні рішення!
Маллок та спринти та касти (Цитата OZ)
і жодного рема ніде. Боже

Як щодо чогось такого?

main()
{
    // the value
    int value = 16;

    // create a string array with a '\0' ending ie. 0,0,0
    char hex[]= {0,0,'\0'}; 
    char *hex_p=hex;

    //a working variable
    int TEMP_int=0;

    // get me how many 16s are in this code
    TEMP_int=value/16;

    // load the first character up with 
    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // move that pointer to the next (less significant byte)<BR>
    hex_p++;

    // get me the remainder after I have divied by 16
    TEMP_int=value%16;

    // 48+0 gives you ascii 0, 55+10 gives you ascii A
    if (TEMP_int<10) {*hex_p=48+TEMP_int;}
        else {*hex_p=55+TEMP_int;}

    // print the result
    printf("%i , 0x%s",value,hex);

}

Добре, тепер у вас є дві шістнадцяткові цифри. Залишилося додати роздільники та подбати про інші байти для перетворень. Може, з петлею? Зробіть це функцією, і у вас буде щось подібне до мого (але досить багатослівне і важке для читання). Може, вам слід хоча б закінчити роботу перед тим, як називати імена на інших плакатах?
kriss

1
І слово про коментарі у вихідному коді (не REM, це базове ключове слово для коментарів, будь ласка, уникайте цього): коментарі, що говорять англійською, що робить код, дуже погана практика! Так, програмісти повинні знати, що означають оператори за модулем (надати залишки), і цей підрозділ підраховує кількість випадків, коли число з'являється в іншому ... і що printf друкує результат. О мій!
kriss
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.