1

I am new to programming. And I have elementary doubt in regard of strings.
I can successfully print a string given by a user. But things get weird when I run the code for Caesar Cipher.
Following codes will explain where actually the problem is arising.

In the following code I am able to successfully print the string as enetered by me:

#include <stdio.h>
int main()
{
    char str[20];
    printf("Enter a string\n");
    scanf("%[^\n]s",&str);
    printf("The entered string is %s",str);
}

Output:enter image description here

OR(Using for loop as follows)

#include <stdio.h>
int main()
{
    char str[20],i;
    printf("Enter a string\n");
    scanf("%[^\n]s",&str);
    printf("The entered string is:");
    for(i=0;i<=20;i++)
    {
        if(str[i]=='\0')
        break;
        printf("%c",str[i]);
    }
}

OUTPUT:enter image description here

I understand that at the end of every string \0 is present to mark the end of a character string.
But things go out of the way if I try to print Caesar Cipher in the above said methods.(Here I am declaring a character array of length 20,as I was told to do so in my Lab session. I have attached my actual lab question at the end)
1st method

#include <stdio.h>
int main()
{
  char str[20],shift_str[20];
    int n;
    printf("Enter a string\n");
     scanf("%[^\n]s",&str);
     printf("Enter a number to shift the string:");
     scanf("%d",&n);
    int i,s,p;
    for(i=0;i<=20;i++)
    {
        if(str[i]=='\0')
        break;
        s=(int)str[i];//type casting str to get the ASCII Value
        p=n+s;
        shift_str[i]=(char)p;// type casting the shifted ASCII Value to char
        
    }
    printf("Caesar Cipher is:%s",shift_str);
    
}

OUTPUT:enter image description here

2nd Method(Using for loop):

#include <stdio.h>
int main()
{
  char str[20],shift_str[20];
    int n;
    printf("Enter a string\n");
     scanf("%[^\n]s",&str);
     printf("Enter a number to shift the string:");
     scanf("%d",&n);
    int i,s,p;
    for(i=0;i<=20;i++)
    {
        if(str[i]=='\0')
        break;
        s=(int)str[i];//type casting str to get the ASCII Value
        p=n+s;
        shift_str[i]=(char)p;// type casting the shifted ASCII Value to char
        
    }
    for(i=0;i<=20;i++)
    {
      if(shift_str[i]=='\0')
      break;
      printf("%c",shift_str[i]);
    }
}

enter image description here

As you can see the coding is running fine expect for the fact that it is printing some junk value stored in the array after the desired output.
As far as I have learned, shift_str should have '\0' after "Ifmmp!Xpsme" so the string should terminate after it, but rather it goes on to print the junk values in the rest of the array as well!!

So at first I would like to know why is it working in that way, and second what is the correct code to run my Caesar Cipher.
I would be glad to receive any help.
(I have originally posted this question in "code review stack", that later found that was not the correct place to ask this type of question as "code review" is meant for improving already successful coed.My apologies)

My original lab question: enter image description here

sameed hussain
  • 205
  • 2
  • 7
  • 1
    There is no `'s'` after `"%[^\n]s"`, the `%[...]` format specifier is complete in an of itself. There is no `&` before `&str`. Why? On access, an arrays is converted to a pointer to its first element. `str` is already a pointer. You need to compile with **warnings** enabled that would have identified each of these issues. So `scanf("%[^\n]",str);` is correct. You should always check the return, e.g. `if (scanf("%19[^\n]",str) != 1) { /* user canceled input */ }`. NOTE: you must always protect your array bounds by providing the *field-width* modifier (e.g. `19`) to avoid storing beyond the end. – David C. Rankin Jan 01 '22 at 07:53
  • All of which is why all user input should be taken with `fgets()` (don't skimp on buffer size). Then to remove the `'\n'` from the end, use `buffer[strcspn(buffer, "\n")] = 0;`. E.g. `if (fgets (str, 20, stdin) == NULL) { /* user canceled */ } str[strcspn(str, "\n")] = 0;`, Now print (or use) `str`. Also,, if not on an embedded system `char str[1024];` is a reasonable size for input use. If on embedded, consider `32` or `64` chars. When you print, don't forget to add a `'\n'` at the end, e.g. `"The entered string is %s\n"` (of course windows is non-standard in this regard...) – David C. Rankin Jan 01 '22 at 07:59
  • As to why you think `scanf()` with `&str` "works", see [How come an array's address is equal to its value in C?](https://stackoverflow.com/q/2528318/3422102) – David C. Rankin Jan 01 '22 at 08:05
  • @sameedhussain Who or what text suggested `scanf("%[^\n]s",&str);`? – chux - Reinstate Monica Jan 01 '22 at 08:14
  • 1
    @chux-ReinstateMonica a website called geeks for geeks suggested me. Here is the link https://www.geeksforgeeks.org/taking-string-input-space-c-3-different-methods/ – sameed hussain Jan 01 '22 at 08:18
  • 1
    @sameedhussain Thank-you. [Method 4](https://www.geeksforgeeks.org/taking-string-input-space-c-3-different-methods/) is junk. Same for Method 1: junk `gets()` not part of C library. Method 3 is weak. Only method 2: `fgets()` is sound. Consider that only 1 in 4 ideas was good and that tells you something about the site. – chux - Reinstate Monica Jan 01 '22 at 08:21

1 Answers1

1

I understand that at the end of every string \0 is present to mark the end of a character string.

It doesn't seem like you understand that because you didn't put such a marker at the end of shift_str but then used it as if it was a string. In your code, shift_str is not a string because there is no \0 at the end of it and therefore it is an error to pass it to printf through %s.

Perhaps change:

        if(str[i]=='\0')
        break;

to

        if(str[i]=='\0')
        {
            shift_str[i]='\0';
            break;
        }
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Thanks for your answer. But I have some query. Is it something like, if you input string from user then C will automatically assign the marker at the end of string. But on doing so with for loop withing the program we have to explicitly do it? – sameed hussain Jan 01 '22 at 08:15
  • @sameedhussain Both `fgets()` and `scanf()` (provided you provide valid storage and use a valid *field-width* with `scanf()`) will ensure the input is nul-terminated. When you loop reading a character-at-a-time (e.g. with `getchar()`, etc.), storing the characters in a buffer (character array). you are the only person that can ensure the nul-terminating character is added at the end. – David C. Rankin Jan 01 '22 at 08:35