0

I have the following code :

#include <stdio.h>

int main()
{
    char * string = GetString();

    printf("%c", *(1+string));


    int j;

    for (j = 0; string[j] != '\0'; j++)
    printf("%c", *(j+string));

    int i;

    scanf("%d", &i);

    return 0;
}

and the function GetString

char *  GetString()
{
    char string[100];

    char x; // to get the chars 1 by 1
    int counter = 0;
    scanf("%c", &x); // i dont like do while loops

    while (x != '\n')
    {
        string[counter] = x;
        scanf("%c", &x);
        counter++;
    }
    string[counter] = '\0';

    char * stringg = string;

    return stringg;
}

for some reason, if I print the string inside the function i get normal input. but if i print it in the main i get really wierd output. what is the problem ?

4
  • 3
    Problem is that stringg is a local variable and you can return a pointer to it. It's undefined behaviour. Commented Dec 20, 2014 at 16:14
  • 1
    Although not an answer to your question (others answer that okay) -one observation is the potential buffer overrun and security threat if the number of characters read is more than 99. Probably should be checking counter so that it doesn't exceed the size of your array. Commented Dec 20, 2014 at 16:25
  • Also, you don't need stringg. When an array appears in an expression in C, it is (roughly speaking) transformed into a pointer to its first element, so you could simply return string. Commented Dec 20, 2014 at 16:38
  • Another problem is that your code in main doesn't know what your GetString function is. If you are using a 64 bit system, your compiler most likely will be things wrong. You should compile your code with all warnings enabled. Commented Dec 20, 2014 at 17:51

4 Answers 4

4

When you return stringg, which is a pointer to the local variable string in GetString, you are returning a reference to memory that is just about to be reclaimed. This is because string "lives" in the stack frame of GetString, and the stack frame ceases to exist when the function returns. You have three options:

  1. Move string to main, and pass a pointer to it to GetString.
  2. Dynamically allocate string using malloc inside of GetString. Memory allocated using malloc survives until manually released using free.
  3. Or, as Michael Petch pointed out below, declare string as static, meaning memory will be set aside in the global data segment of the program. However, this means that subsequent calls to GetString will clobber its contents, so if you want to save the result of a previous call to GetString you must manually copy it using strdup or similar.

Before GetString returns, the stack looks something like this:

                       (high address)
+---------------------+
|   (main's frame)    |
|        ...          |
+---------------------+
| (GetString's frame) |
|  char string[100]   |
+---------------------+ <- stack pointer
                       (low address)

As you can see, the stack pointer points to the end of GetString's frame, marking the top of the stack (notice that on most modern platforms, the stack actually grows downwards). But when GetString returns, the stack pointer moves up to the end of main's frame:

                       (high address)
+---------------------+
|   (main's frame)    |
|        ...          |
+---------------------+ <- stack pointer
| Formerly the frame  |
| of GetString, now   |
| (probably) garbage. |
+---------------------+
                       (low address)

And accessing something that is not part of the stack (i.e., in this case below the stack pointer) results in undefined behavior. (Well, not quite - see Barak Manos' comment below) In your case, you get garbage.

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

3 Comments

Another option (albeit ugly with side effects) is to define string as static.
Worth noting that the stack implementation (in particular the low/high address issue) is compiler-dependent and not dictated by the C-language standard, which only states that returning a local array yields undefined behavior.
Thanks for pointing that out, @barakmanos. I have edited my post accordingly.
2

Returning pointer to automatic local variable invokes undefined behavior. You need to use dynamic allocation or use staticspecifier.

char *string = malloc(100);  

or

static char string[100];

2 Comments

Or global variable char string[100]... Or allocating it in main and passing it to GetString.
I would not recommend a static local string either, because both are thread-unsafe. In addition, I would not recommend a constant length string without protecting it from overflowing in the for loop either. But all that is irrelevant to the problem at hand, which is the string's allocation-scope (or allocation segment)... In any case, if we were to discuss the most recommended option then it would probably be to pass the string from main.
0

You can pass the array to the GetString function

void  GetString(char string[], size_t length /* prevent overflow */)
{
    char x; // to get the chars 1 by 1
    int counter = 0;

    if (length == 0)
    {
        string[0] = '\0';
        return;
    }
    scanf("%c", &x); // i dont like do while loops

    while ((x != '\n') && (counter < length - 1))
    {
        string[counter] = x;
        scanf("%c", &x);
        counter++;
    }
    string[counter] = '\0';
}

and in main

int main()
{
    char string[100];

    GetString(string, sizeof(string));
    if (*string != '0')
        printf("%c", *(1+string));

    int j;
    for (j = 0; string[j] != '\0'; j++)
        printf("%c", *(j+string));
    int i;
    scanf("%d", &i);

    return 0;
}

2 Comments

It is a good idea to follow the DRY (en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle. Also GetString() fails for a length of 0.
It still fails for lenght = 0.
0

[too long for a comment]

To follow up on iharob proposal to pass in the buffer, please see this:

int GetString(char * str, size_t len)
{
  int result = 0;

  if ((NULL == str) || (0 == len))
  {
    result = -1;
    errno = EINVAL;
  }
  else 
  {
    while (1 < len)
    {
      result = scanf("%c", str);
      if (EOF == result)
      {
        int errno_save = errno:

        if (ferror(stdin))
        {
          result = -1;
          errno = errno_save;
        }
        else
        {
          result = 0;
        }
      }

      if ((0 != result) || ('\n' == *str))
      {
        break;
      }

      ++str;
      --len;
    }

    *str = '\0';
  }

  return result;
}

Call this like so:

int main()
{
  char string[100];

  int result = GetString(string, sizeof(string));
  if (-1 == result)
  {
    perror("GetString()" failed");
    exit(1);
  }

  ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.