? 4" /> ? 4" /> ? 4"/>

Why printf doesn't print first character of char* string?

198 Views Asked by At

I want print table of ascii symbols like this:

  0 1 2 3 4 5 6 7 8 9 a b c d e f
0 
1            
2   ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~ 
8 � � � � � � � � � � � � � � � �
9 � � � � � � � � � � � � � � � �
a � � � � � � � � � � � � � � � �
b � � � � � � � � � � � � � � � �
c � � � � � � � � � � � � � � � �
d � � � � � � � � � � � � � � � �
e � � � � � � � � � � � � � � � �
f � � � � � � � � � � � � � � � �

I wrote this c code:

#include <stdio.h>

int main() {
    char s[34] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n";
    printf("%s", s);
    for (size_t i = 0; i < 16; ++i) {
        char str[34];
        char hex[2];
        snprintf(hex, sizeof(hex), "%x", (int)i);
        str[0] = hex[0];
        for (size_t j = 0; j < 16; ++j) {
            str[2 * j + 1] = ' ';
            str[2 * j + 2] = i * 16 + j;
        }
        str[33] = '\0';
        printf("%s\n", str);
    }
}

It prints this:

  0 1 2 3 4 5 6 7 8 9 a b c d e f
0 
1            
   ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~ 
8 � � � � � � � � � � � � � � � �
9 � � � � � � � � � � � � � � � �
a � � � � � � � � � � � � � � � �
b � � � � � � � � � � � � � � � �
c � � � � � � � � � � � � � � � �
d � � � � � � � � � � � � � � � �
e � � � � � � � � � � � � � � � �
f � � � � � � � � � � � � � � � �

My problem is that 4 line of symbols, that begins with symbol '2', prints without first symbol '2'. I dont understant why.

I tried debug my code, but I didnt notice anything, that may help me.

5

There are 5 best solutions below

0
gulpr On

Your code invokes undefined bahaviour:

char s[34] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n";

s is too short to accommodate the whole string and the terminating null character. As it does not contain null terminating character you can't print it as a string with printf (It is UB)

Computers are much better at counting characters than humans:

char s[] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n";

You do not need strings for this task. Also, you should print only characters which can be printed. Some chars have special meanings like new line, line feed, backspace, bell etc.

int main() {
    puts("  0 1 2 3 4 5 6 7 8 9 a b c d e f");
    for(size_t row = 0; row < 16; row++)
    {
        printf("%zx ", row);
        for(size_t col = 0; col < 16; col++)
        {
            int ch = row * 16 + col;
            printf("%c ", isprint(ch) ? ch : ' ');
        }
    printf("\n");
    }
}

https://godbolt.org/z/5hzbcnPeG

Also use the correct format for size_t which is %zu or %zx

0
Ted Lyngmo On
  • The string " 0 1 2 3 4 5 6 7 8 9 a b c d e f\n" has 35 characters so your program has undefined behavior when printing it since the null terminator isn't included in s.
  • Your program prints control characters, such a backspace, tab, newline etc. Most notably, it prints ESC (ascii value 27) which probably tells your console that an escape sequence starts. It will then consume the following character(s) to form a valid escape sequence, which will most likely fail, but the characters are consumed and therefore lost in your visible output.

Check that the character is printable before printing. If you expect output for characters in the higher range, you need to set a locale (and possibly adjust your terminal accordingly). Note that locales are platform dependent so the one I use may not work for you.

Example:

#include <ctype.h>
#include <locale.h>
#include <stdio.h>

int main() {
    setlocale(LC_ALL, "sv_SE.iso-8859-15");
    char str[] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n"; // char[35]
    fputs(str, stdout);
    for (unsigned i = 0; i < 16; ++i) {
        char hex[2];
        snprintf(hex, sizeof hex, "%x", (unsigned)i);   // unsigned
        str[0] = hex[0];
        for (unsigned j = 0; j < 16; ++j) {
            unsigned char ch = i * 16 + j;
            if (isprint(ch)) str[2 * j + 2] = (char)ch; // isprint
            else str[2 * j + 2] = ' ';
        }
        fputs(str, stdout);
    }
}

Possible output:

  0 1 2 3 4 5 6 7 8 9 a b c d e f
0
1
2   ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~
8
9
a   ¡ ¢ £ € ¥ Š § š © ª « ¬ ­ ® ¯
b ° ± ² ³ Ž µ ¶ · ž ¹ º » Œ œ Ÿ ¿
c À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï
d Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
e à á â ã ä å æ ç è é ê ë ì í î ï
f ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ
0
WStanzl On

Yes, the control characters mess up your table. I would do it as shown below.

  • My personal preference is to declare strings with [size + 1] as opposed to buffer character arrays. A slight semantic hint.

  • I exclude the \n from the header line, I'd say it always belongs into the printf().

  • Always the same 1-character portion of str will be modified, so we can initialise str with spaces first (and also the trailing null) and leave them there. For clarity, I use hex values instead of decimal values for the values in the exclusion test for non-printable characters.

  • I use no isprint() function call because we always know which ASCII code we have. The non-printable characters in ASCII are well-documented; We exclude them as we go. https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png.

The code is (deliberately) not platform neutral. An EBCDIC computer would produce incorrect results.

#include <stdio.h>
#include <string.h>

int main(int argc, char** argv) 
{
    char s[] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f";
    char str[33 + 1];
    unsigned char c;
  
    memset(str, ' ', 33);
    str[33] = '\0';
  
    printf("%s\n", s);    

    for (int i = 0; i < 16; i++) 
    {
        sprintf(str, "%x ", i);

        for (int j = 0; j < 16; j++) 
        {
            c = i * 16 + j;
            if (c > 0x1f && c != 0xff)
               str[2 + j * 2] = c;
            else
               str[2 + j * 2] = ' ';
        }

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

Output:

  0 1 2 3 4 5 6 7 8 9 a b c d e f
0                                
1                                
2   ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~ 
8 €  ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ  Ž 
9  ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ
a   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯
b ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿
c À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï
d Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
e à á â ã ä å æ ç è é ê ë ì í î ï
f ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ  

Greetings, happy programming!

0
Muhammad Nasir On

Code:

#include <stdio.h>

int main() {
    printf("  0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
    
    for (int i = 0; i < 16; ++i) {
        printf("%x ", i); // Print the hexadecimal digit for the row
        
        for (int j = 0; j < 16; ++j) {
            int ascii_code = i * 16 + j;
            if (ascii_code < 32 || ascii_code == 127) { // Check if the ASCII code is non-printable
                printf("  "); // Print two spaces for non-printable characters
            } else {
                printf("%c ", (char)ascii_code); // Print the ASCII character
            }
        }
        
        printf("\n"); // Move to the next line after printing each row
    }
    
    return 0; // 
}

Output [paste image for the above code output]

enter image description here

0
Luis Colorado On

Dimensioning 34 for char arrays is producing undefined behaviour.

First of all, don't initialize an array with a string literal that is longer than the array size (string literal is 35 characters wide, if you take into account the final \0 that the compiler puts on it) if you do, the extra characters will be ignored (producing a string that is not null terminated, leading to Undefined Behaviour, but you should receive at least a warning for this)

    char s[34] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n";

for exactly the same reason, the rest of arrays have to be increased, at least by one in size, to allow for the '\0' to be put at the end of the strings that will be printed on them. It's a good idea to give a short marging or not to initialize the array size.

    char s[] = "  0 1 2 3 4 5 6 7 8 9 a b c d e f\n";

This is perfectly valid and will initialize the array with a size adequate to fit the complete string literal (and the trailing nul byte)

On other side, here's a quite simpler approach, that doesn't involve all the complexity you have used (you don't need to build the strings to be printed, this can be done on the fly):

#include <stdio.h>
#include <ctype.h>

int main()
{
    printf("  0 1 2 3 4 5 6 7 8 9 a b c d e f\n");
    printf("0\n"); /* these are control characters that don't print well */
    printf("1\n");
    unsigned char c = 0x20;
    for (int row = 2; row < 16; ++row) {
        printf("%x", row);
        for (int col = 0; col < 16; col++, c++) {
             if (iscntrl(c)) {
                 printf("  ");
             } else {
                 printf(" %c", c);
             }
        }
        printf("\n");
    }
}

that will produce:

$ pru
  0 1 2 3 4 5 6 7 8 9 a b c d e f
0
1
2   ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~  
8 � � � � � � � � � � � � � � � �
9 � � � � � � � � � � � � � � � �
a � � � � � � � � � � � � � � � �
b � � � � � � � � � � � � � � � �
c � � � � � � � � � � � � � � � �
d � � � � � � � � � � � � � � � �
e � � � � � � � � � � � � � � � �
f � � � � � � � � � � � � � � � �

by the way, I don't recommend you to print an ASCII table for the full set of 8bit chars. The problem is that for 0x80 and above, those characters are not ASCII (ASCII is a 7-bit character set, so it ranges from 0x00 to 0x7f), and, as me, you should have an utf-8 terminal that considers characters 0x80-0xff as invalid utf-8 sequences (this is the reason they are all printed as �) if you want them printed correctly, you need to use an iso-latin-something character set in your terminal