0

I finished PSet2 with all the correct results, but I'm trying to figure out why I had to change some of my code in initials.c to make it work properly. The original code is as follows:

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

// Prestage functions
string GetInit(string fName);
string MakeCap(string inits);

int main(void)
{
    // printf("This program will convert your name to initials.\n");
    // printf("Please input your name: ");
    string name = GetString();
    string initials = GetInit(name);
    initials = MakeCap(initials);
    printf("%s\n", initials);
}

string GetInit(string fName)
{
    int spaces = 0;
    // Count spaces in the input for array size
    for (int count = 0; count < strlen(fName); count++)
    {
        if (fName[count] == ' ')
        {
            spaces++;
        }
    }
    // Leave space for first and terminating characters
    char inits[spaces + 2];
    // Set first initial to assumed first character
    inits[0] = fName[0];
    // Increment position counter for next position
    int initPos = 1;
    // Find other letters
    for (int count = 0; count <= strlen(fName); count++)
    {
        // Find space and add next character
        if (fName[count] == ' ')
        {
            inits[initPos] = fName[count + 1];
            initPos++;
        }
    }
    // Add terminating character to the end
    inits[spaces + 1] = '\0';
    // Convert to string
    string initials = inits;
    // Pass to make captial
    return initials;
}

string MakeCap(string inits)
{
    for (int count = 0; count <= strlen(inits); count++)
    {
        // Find lowercase and convert to uppercase
        if (inits[count] > 'Z')
        {
            inits[count] -= 'a' - 'A';
        }
    }
    return inits;
}

I used the gdb debugger to follow along with the code. On line 15 (initials = MakeCap(initials);), the value of initials becomes the capitalized initials as expected. On line 16 (printf), however, initials becomes a garbled mess and the output becomes strange characters.

Now, what's truly baffling is that if I remove the comments from the first two printf's on top, this bug does not occur. Also, if I call GetInit(name) but move the rest of the code into the GetInit function, the bug does not occur.

Why do you think the initials variable would change contents after using printf, and why would it be fine if I use a printf function in the beginning or move the last printf into a different function?

1
  • Without seeing what the declaration of the functions are it will be difficult to be definitive.
    – user6864
    Apr 10, 2015 at 1:18

1 Answer 1

2

It may be hard for you to figure out the cause of the problem assuming that you just finished week 2. The problem deals with concepts like local variables, scope, stack, etc.

You may probably know about local variables and scope and you'll get exposed to the rest as you proceed with the course. I'll try to simplify things as much as I can. If you feel like you still don't get what's in this answer or find it confusing in a way or another, maybe you should give it a look back after you get exposed to concepts above.


What's the cause of the problem?

You're first taking a string from the user via GetString then you're passing it to GetInit. Inside GetInit, you're counting the number of spaces and creating a char array, namely inits, of size spaces + 2. Lastly, you're initializing inits, creating a new string called initials, then returning that string.

Regardless of the fact that there unneeded steps and there are things that could have been done in a better way, inits and initials are local variables to GetInit — as GetInit returns, these variables go out of scope (i.e., no longer accessible). At this point, trying to access the values of these variables results in undefined behaviors — you never know what you're gonna get.

You probably could conclude now that the rest of the operations that you perform on the value returned by GetInit have no defined behavior as well and that's why you see the weird stuff printed.


What does this have to do with the printf statements up there?

local variables are stored in an area of memory called the stack.

+++++++++++++++++
+  /* stack */  +
+++++++++++++++++

Each function that gets called, gets some space reserved for it on the stack. This space is often referred to as the function's stack frame.

The stack frame stores information about the function, its parameters and local variables, the address that the program control is supposed to return to when this function terminates, etc.

Since main is the first function that gets called in a program, think of the following as a visualization to main getting a stack frame of it pushed onto the stack.

++++++++++
+        +
+--------+
+  main  +
++++++++++

When you call GetInit, a stack frame of it gets pushed onto the stack as well, in this case, right above main's stack frame

+++++++++++
+         +
+---------+
+ GetInit +
+---------+
+  main   +
+++++++++++

As you can probably guess, when you declare inits and initials (local variables in GetInit) they get memory allocated for them as part of GetInit's stack frame on the stack, but here's the interesting thing: When a function ends execution, its stack frame gets popped out of the stack — you may think of it as: the system forgets that there was a stack frame for this function here.

So after GetInit ends execution, the stack becomes

+++++++++++++++++++++++++++++++++++++++++++++++
+ /* left-overs from GetInit's stack frame */ +
+---------------------------------------------+
+                     main                    +
+++++++++++++++++++++++++++++++++++++++++++++++

What's destiny of inits when GetInit ends execution? We don't really know. The value the used to be in there could still be there or, if for example another function gets called (e.g., MakeCap on line 15 and printf on line 16), it gets a stack frame of it pushed onto the stack and the value that used to live in inits and initials could get overwritten. That's precisely the reason why trying to access init and/or initials after GetInit terminates has an undefined behavior.

When you call printf a couple of times atop main, stack frames get pushed onto and popped from the stack and out of luck the value of inits and initials (because technically they're the same one value) could survive (i.e., still be there) but of course this doesn't mean that it'll survive forever.


Fix:

I think the easiest fix would be creating the char arrays that you want to manipulate inside other functions in main at least if you want still have access to their values after these functions return. Then you pass and modify these char arrays to/in your functions. Example:

#include <stdio.h>
#include <cs50.h>

// prototypes
void fillWithA(char arr[], int size);

int main(void)
{
    #define SIZE 10

    char arr[SIZE];
    fillWithA(arr, SIZE);

    printf("%s\n", arr); // outputs AAAAAAAAAA
}

/* fills arr with 'A's */
void fillWithA(char arr[], int size)
{
    for (int i = 0; i < size - 1; i++)
        arr[i] = 'A';

    // terminate the string
    arr[size - 1] = '\0';
}

In this fix, we're relying on the fact modifying the contents of an array that is passed to a function affects the original array.

And since main is the last function that gets terminated, arr in this case doesn't go out of scope until main returns, so the result of accessing its value after we declare and initialize it is defined.

2
  • I'm not entirely comfortable with how memory works yet, but I understand enough of your explanation to get what you're saying. Thank you for the detailed explanation! I wasn't aware that you could pass variables to functions like that. In fact, the lectures have specifically said that you only get a copy passed. Is there a specific thing you did that lets the array pass to and from the function?
    – MKrlt
    Apr 13, 2015 at 14:02
  • Nevermind there. PSet3 has the answer to my later question: "As we’ll discuss in Week 4, arrays are not passed "by value" but instead "by reference," which means that sort will not be passed a copy of an array but, rather, the original array itself." Thanks again for your response!
    – MKrlt
    Apr 13, 2015 at 19:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .