0

First I would like to thank everyone for everything they have ever done for me whether they knew it or not. I am a first time poster long time lurker.

I am starting a new class that is based around C and several of its variants. The problem is it assumes you have experience with at least C++, and I unfortunately have only taken a couple semesters of Java and Mips. I am struggling to learn C right now with no book or lesson plan going over it. I have survived this first month using Google. But my question today I just cannot seem to wrap my head around, and while I know it is elementary I would like some help understanding the working of the code in my assignment.

The professor has supplied us with the following code:

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

void encrypt(int offset, char *str) {

    int i,l;

    l=strlen(str);

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

    for(i=0;i<l;i++)
        if (str[i]!=32)
            str[i] = str[i]+ offset;

    printf("\nEncrypted str = \n%s \nlength = %d\n", str, l);
}   

void decrypt(int offset, char *str) {


}   

void main(void) {

    char str[1024];

    printf ("Please enter a line of text, max %d characters\n", sizeof(str));

    if (fgets(str, sizeof(str), stdin) != NULL)
    {
        encrypt(5, str);    // What is the value of str after calling "encrypt"?

        // add your method call here:
    }
}   

So the questions for the homework are listed in the code, but to make it clear that is not what I am after. I want to understand how this program is working so far.

Specifically:

  1. Why write char str[1024]

  2. What exactly is if (fgets(str, sizeof(str), stdin) != NULL) doing? I have a decent idea but I don't know the reasoning behind it.

3.And lastly (I hope) in

if (str[i]!=32)
    str[i] = str[i]+ offset;

why are we worried about str[i] not equaling 32?

I am sorry if this is a lot to be asking but I really truly want to understand this.

Also if you know of any fantastic reads for C please let me know because I am rather worried about the rest of this semester at this point.

EDIT:

Just wanted to say thank you very much to everyone who answered me. I am unfortunately not one of those people who immediately likes to continue asking more questions to acknowledge that you helped me. So for those of you who I didn't directly thank or comment back, Thank you very much. I now have a much firmer grasp on some of the very elementary concepts I was nervous about only 30 mins ago.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Slater
  • 21
  • 1
  • 7
  • 1
    I don't think you'll get a good answer to 3. The code appears so bizarre and illogical, I don't think anyone will be able to give you a good explanation for why it does what it does. – David Schwartz Sep 17 '12 at 02:00
  • @DavidSchwartz for ASCII 32..? – mvds Sep 17 '12 at 02:03
  • 1
    @DavidSchwartz: ... 32 is the ASCII code for a space. – Chris Dargis Sep 17 '12 at 02:03
  • 1
    32 is the space character...presumably it only encrypts non-spaces. Note that this makes the function non-invertible if control characters are input (e.g. tab, newline, vtab, etc.) since the offset might make them into spaces. – nneonneo Sep 17 '12 at 02:04
  • @nneonneo: That's just one awful thing about the code. The other is that it assumes an array of X bytes can hold a C-style string X characters long which, of course, it can't. – David Schwartz Sep 17 '12 at 02:07
  • @DavidSchwartz: Where does it make that assumption? `fgets` reads `size-1` bytes before storing a null, which means it will only ever store a 1023-byte string. – nneonneo Sep 17 '12 at 02:12
  • @nneonneo: Here: `printf ("Please enter a line of text, max %d characters\n", sizeof(str));` – David Schwartz Sep 17 '12 at 02:14
  • Eh. It's a minor cosmetic bug. Not the worst thing that could happen, by far. – nneonneo Sep 17 '12 at 02:17
  • Who is gonna push the limit to the last character? This is homework example code and this is an easily overlooked blemish to simplify the concept for students. Thought it might be something to note after the code has been fully grasped by the class. – recursion.ninja Sep 17 '12 at 02:22
  • A few points. 1. Using `32` to denote the space character is horribly bad style. It should be written as `' '`, which (a) is clearer, and (b) is portable to systems that use different character sets (in EBCDIC, `' '` has the value 64). 2. `void main(void)` is wrong; it should be `int main(void)`. 3. Just so you know, the C standard defines a "string" as "a contiguous sequence of characters terminated by and including the first null character"; it's a data layout, not a data type. In particular, a `char*` is not a string, though it can point to (the first character of) a string. – Keith Thompson Sep 17 '12 at 03:12
  • Welcome to SO. Try to sum up your questions more in the title. – Jordan Richards Sep 17 '12 at 07:54
  • @awashburn: It's extremely important to get those things right in example code because the purpose of example code is to teach people how to do things right. Forgetting about the newline at the end of `fgets` and forgetting about the terminator on C-style strings are very common real-world bugs. – David Schwartz Sep 17 '12 at 08:04

5 Answers5

2
  1. You use char str[1024] because C does not have any concept of a string, only arrays of char terminated by NUL aka 0. It doesn't have any concept of "stretchy" containers that you don't explicitly implement yourself. So if you want to read a string of input without writing a lot of your own input handling you guess at some maximum length (in this case, 1024) and make space for that in advance.
  2. fgets reads a string (and when you say "string" in C that means "array of char terminated by 0, as above). You pass in str which is the pointer to the start of the storage and sizeof(str) which is evaluated at compile time to be the number of bytes that str takes up. It means 1024 in this case, with the advantage that it will track the change if you change the char str line. That argument is preventing fgets from writing beyond the allocated space of str. Remember, it's not a stretchy string.
  3. ASCII 32 is space. It would have been clearer to write str[i] != ' '
Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
  • "You use char str[1024] because C does not have any concept of a string, only arrays of char terminated by NUL aka 0. It doesn't have any concept of "stretchy" containers that you don't explicitly implement yourself. So if you want to read a string of input without writing a lot of your own input handling you guess at some maximum length (in this case, 1024) and make space for that in advance." With that statement you have taught me infinitely more than my professor so far! Thank you! – Slater Sep 17 '12 at 02:31
  • I will ask you one question that pertains directly to one of the questions asked of me in my homework, if thats alright? I cannot seem to figure out what it wants when it asks me what the value of `str` is after `encrypt` runs? does it mean like that combined value of the ASCII characters? – Slater Sep 17 '12 at 03:14
  • @Slater: If they're being very literal, the value of `str` is unchanged. In C when you use the name of an array without subscripting it [decays](http://stackoverflow.com/questions/1461432/what-is-array-decaying) to a pointer to the start of the array storage. That location does not change. The *contents* change as you describe (the string is encrypted). That's because the pointer is passed to the function by *value* but the function writes through the pointer onto the original data. – Ben Jackson Sep 17 '12 at 03:23
2

A great start would be Kernighan + Ritchie's book,The C Programming Language.
Amazon: here

Also, see The Definitive C Book Guide and List.

Community
  • 1
  • 1
Andrew W Carson
  • 163
  • 1
  • 7
0
  1. this says that str is an array of 1024 chars
  2. "fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer."
  3. apparently characters with value 32 are not to be "encrypted". Find an ASCII table and see what special character 32 is...

ps. "gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read."

mvds
  • 45,755
  • 8
  • 102
  • 111
0
char str[1024];

declares str as a 1024-element array of char.

fgets(str, sizeof (str), stdin)

reads up to sizeof (str) (1024) characters from stdin (standard input, basically your console) and stores those characters to str. fgets will return NULL if there's an error during the read, so

if (fgets(std, sizeof(str), stdin) != NULL)
{
  encrypt(...);
  ...
}

will execute the body of the if statement only if we successfully read an input string.

32 is the ASCII code for a space (' ') character; for whatever reason, the encryption algorithm leaves space characters in the string as is.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

First question:

In Java, you have to new all your arrays, e.g. char[] arr = new char[1024]. In C (and C++), you can opt to either dynamically allocate the array like Java (using malloc in C and new [] in C++), or statically allocate the array using a fixed size (the char str[1024] you see there).

In C, because all memory needs to be manually managed with malloc and free, statically allocated buffers save a bit of hassle since they are automatically freed when the function exits. In Java, you rarely deal with memory allocation because the runtime automatically garbage collects unused objects.

In Java, you have String, which wraps an array of char. In C, you only have arrays of char, typically represented as either an array of char or a pointer to the first character (char *). Note too that the Java char is 16 bits (2 bytes) wide as it handles Unicode; the C char is only 8 bits (1 byte) in size.

Finally, note that in C, all strings are terminated by a null byte '\0'. Strings thus have to be big enough to hold both the content and the terminating null byte -- failing to count that null byte might result in a buffer overflow.

Second question:

In Java, you can use readLine to read a line of text from a file, or from the user's input. In C, you use fgets. However, in Java, the runtime will dynamically allocate a string big enough to hold the input line (however long that line might be); in C, since buffers are typically allocated by the programmer, the function can only read as many characters as the buffer will permit (reading characters past the end of a buffer is called a buffer overflow, a common cause of security vulnerabilities).

fgets therefore takes three parameters: the buffer to read into, the size of that buffer, and the file to read from (here stdin, the console).

Third question:

Strings in C are simply sequences of ASCII characters. So, the code there is encrypting characters (by adding this "offset") only if the character isn't 32 -- a space character. The result is that all non-spaces will be transformed to gibberish, leaving the spaces intact.

Community
  • 1
  • 1
nneonneo
  • 171,345
  • 36
  • 312
  • 383