1

I have a function (called encrypt) that will return a string (called ciphertext). Inside this function, I created an array char[] (called cptxt_arr) and after running a loop and creating the ciphertext from the plaintext (got it from the user), I stored each letter inside the char[], then I assigned the char[] to the string and returned the string.

Here's my question: In the main(void) function, how come, when I print the encrypt function as a string (%s) I don't get anything from the CLI, but if I print the same function as char (%c) I do get the letters?

PS: From what I've read, I know I can't return arrays in C, which is why I'm assigning the char[] to a string variable and returning that.

I just want to know why is this happening because If I print the string ciphertext inside the encrypt function it works meaning that the code works fine.

NOTE: For the sake of the question, use positive integers for the "key" variable. I cleaned a bit the code, so the important part could be seen more easily.

Here's the code:

#include <cs50.h> #include <stdio.h> #include <string.h> string encrypt(string plaintext, int key); int main(void) { int key; string plaintext; // Get the key. key = get_int("Key: "); // Get plaintext from the user. plaintext = get_string("plaintext: "); // Print encrypted text. printf("ciphertext: %s\n", encrypt(plaintext, key)); // Same but with the formatted char (%c). // printf("ciphertext: %c\n", encrypt(plaintext, key)[0]); } // Encrypt text. string encrypt(string plaintext, int key) { // Get plaintext length. int length = strlen(plaintext); char cptxt_arr[length]; string ciphertext; for (int i = 0; i < length; i++) { if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { cptxt_arr[i] = ((plaintext[i]) + key); } else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { cptxt_arr[i] = ((plaintext[i]) + key); } } // Assign cptxt_arr[] to string and returning it. ciphertext = cptxt_arr; return ciphertext; } 

I have (I don't if it could matter) created a variable in the main(void) function and assigned the encrypt function to it, which doesn't work either.

14
  • 3
    You're returning the address of a local variable. When the function ends that variable no longer exists and you have a dangling pointer. Using it causes undefined behavior. Commented Jul 7, 2023 at 22:24
  • 2
    Q: I can't return arrays in C. A: Of COURSE you can return arrays in C! You just can't allocate the array inside a function (as a local variable). In C, local variables are NO LONGER VALID when the function exits. SOLUTIONS: 1) malloc() the array inside the function, or (better!) 2) allocate a char[] buffer OUTSIDE, and pass it as an argument INTO the function. Commented Jul 7, 2023 at 22:24
  • 1
    @paulsm4 ... no? Commented Jul 7, 2023 at 22:25
  • 1
    For clarity: I questioned the statement when it was: "Q: I can't return arrays in C. A: Of COURSE you can return arrays" - which is ... no, not in C. Commented Jul 7, 2023 at 22:28
  • 2
    ... which is why it's utterly dangerous to use string and char* interchangeably in cs50. Commented Jul 7, 2023 at 23:11

2 Answers 2

3

The CS50 data type string is nothing more than a reference to an array which contains a sequence of characters. It is not an array itself.

These references are called "pointers" in C and you will learn all about them in week 4 of CS50.

In the line

ciphertext = cptxt_arr; 

you are not making a copy of the characters contained in the array cptxt_arr. Instead, you are making ciphertext refer to the array cptext_arr.

In the line

return ciphertext; 

the function encrypt will pass this reference to cpttxt_arr to the function main. However, as soon as the function encrypt returns, all of its local variables will cease to exist. This includes the array cptxt_arr. This means that the function main cannot do anything with the returned reference, because the reference is invalid, as it refers to an array that no longer exists.

In week 2 of CS50, you are supposed to solve the exercises by overwriting the array that was created and returned by get_string.

In week 4 of CS50, you will learn how to use the functions strcpy and malloc, with which you can copy character arrays and allocate memory for these copies in such a way that the copies do not cease to exist when the function returns.

Sign up to request clarification or add additional context in comments.

6 Comments

"The CS50 data type string is nothing more than a reference to an array which contains a sequence of characters." - But it is. That's what leads cs50 students astray. It's the atexit registration that makes it special.
@TedLyngmo: The atexit registration that you are referring to only applies to the function get_string, but not to the data type string in general. For example, when writing int main( int argc, string argv[] ), the elements of argv do not have the atexit properties that you describe, despite them having the data type string.
Exactly! They should not be confused! char* != string (typedefined aside)
@TedLyngmo: I don't understand what point you are trying to make. Are you claiming that the data type string should only be used with get_string and not with argv? The CS50 course also uses the data type string with argv. Is it your intent to criticize the CS50 course in this respect?
In so many words, yes. I want to separate the two types char* and string since, in the world of cs50, they are just as different as std::string and char* in C++. It's not nice to lead people learning the crazy mid-language "cs50" anything else. A string in cs50 is autmatically destroyed. That clue could be enougfh.
|
2

Why my returned value is not printed as a string but it is printed as a char?

Code attempts to return the address of a local object which leads to undefined behavior (UB).

char cptxt_arr[length]; string ciphertext; ... ciphertext = cptxt_arr; return ciphertext; // Bad 

Certainly a dupe someplace.

9 Comments

"attempts to return the address" - Not only does it attempt to do it, it does.
@TedLyngmo Perhaps. In either case a fine point. As I understand, even the attempt to return the value is UB, even if the calling code does not use it.
It's not a problem if not dereferenced - if I got it right.
@TedLyngmo " not a problem if not dereferenced" --> I don't think so. Simply passing around a defunct address is problematic. We could spend time looking it up, yet it is still poor programming. Yet even if UB, I have never seen a system that had much trouble with simply copying such addresses.
I'm not gonna look at the standard either, but it seems like you could return an uninitialized pointer with an indeterminate value and it only becomes a problem if you try and use it. A pointer to an array that used to exist seems like the same thing to me. I bet we can all agree it's just a bad idea. :)
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.