4

Here is an example of what I am trying to do:

#include <stdio.h>

FILE* f;
const char* getstring()
{
    f = fopen ("hello.txt", "r");
    char x[200];
    for (int i = 0; i < 200; i++) x[i] = 0;
    for (int c = getc(f), i = 0; (c != EOF) && (i < 200); c = getc(f), i++)
        x[i] = c;
    fclose(f);
    printf ("getstring(): x = %s", x);
    const char* y = x;
    printf ("getstring(): y = %s", y);
    return y;
}

void printstring (const char* string)
{
    printf ("%s", string);
}

int main()
{
    printstring(getstring());
    printf ("\nprintf: %s", getstring());
    return 0;
}

and the output is:

getstring(): x = Hello World
getstring(): y = Hello World
���getstring(): x = Hello World
getstring(): y = Hello World

printf: ��������

I don't know why the printstring() function is outputting nothing and printf is outputting random data or why there is a bit of random data at the end of the string when I use the printstring() function.

Is there any way to fix this and what am I doing wrong?

Fract
  • 113
  • 6
  • 1
    Welcome to stackoverflow! We highly suggest that you leave plain old C `char*` strings and use `std::string`. It's C++ standard library and has lots of benefits. Unless you're using `C` and not `C++` of course – Vinícius Jan 12 '20 at 19:34
  • Which book are you using to learn C++? I'm surprised that it teaches C-style strings. – Lightness Races in Orbit Jan 12 '20 at 19:38
  • @snoopy Yeah. I automatically default to using c instead of c++ because I'm not very familiar with the functions in the c++ standard libraries. – Fract Jan 12 '20 at 20:40
  • 1
    https://stackoverflow.com/questions/7769998/how-to-return-local-array-in-c – M.M Jan 12 '20 at 22:04

4 Answers4

6

The problem

The problem is that getstring() returns a pointer to a local array. This array gets destructed when the function returns, so you have an dangling pointer. Using this pointer is then undefined behavior. Anything can happen: for example you can get garbage random value, you can get the old unchanged value, or the system could crash.

The solution

Since this question is labelled c++, just use std::string instead of char*, and this kind of nightmare will vanish for good.

Note that Using std::string in prinf() would require you get a pointer with .c_str().

If for an obscure reason, you are required to use char* you'd have to to use strdup() or llocate some memory for the c string and return a pointner to that memory. But the caller must then delete this pointer if you don't want memory to leak.

Christophe
  • 68,716
  • 7
  • 72
  • 138
3

The C string is stored in a function local char array. This array is destroyed when the function is left. Since the question is tagged as C++ use std::string

#include <iostream>
#include <string>
#include <fstream>

std::string getstring()
{
    std::ifstream f("hello.txt");
    std::string x;
    x.resize(200);
    for (int i = 0; i < 200; i++) x[i] = 0;
    for (int c = f.get(), i = 0; (c != EOF) && (i < 200); c = f.get(), i++)
        x[i] = c;
    std::cout << "getstring(): x = " << x;
    const std::string& y = x;
    std::cout << "getstring(): y = " << y;
    return x;
}

void printstring (const std::string& string)
{
    std::cout << string;
}

int main()
{
    printstring(getstring());
    std::cout << "\nprintf: " << getstring();
    return 0;
}
Vinícius
  • 15,498
  • 3
  • 29
  • 53
Thomas Sablik
  • 16,127
  • 7
  • 34
  • 62
0

Since others have explained how to go about resolving the issue, I'll expand the other answers a bit and explain why you had an issue in the first place.

To put it simply, whenever a function is called, it is given a stack frame (also called activation record) of its own. That is to say: it is given an area of memory where it can place its local variables. When the function returns, the stack frame is destroyed. If you then call another function, the stack frame of that function overwrites the stack frame of the previous function.

In this specific case, when getstring returns and printstring and subsequently printf are called, the stack frames of the latter two along with that of main overwrite the data that was previously located within the stack frame of getstring. The most likely result is that printf will output complete junk. In the worst case, it can make the entire program crash if the null terminator of the string was overwritten.

It is also interesting to note that in your case it seems that a binary value corresponding with that of the null terminator \0 was inserted somewhere, because only a bit of junk is printed before printf returns. This would indicate that it did not stop at the original null terminator of the character array x but rather encountered a value it interpreted as the null terminator and returned.

If you wish, you can read more about the call stack on Wikipedia.

0x4d45
  • 704
  • 1
  • 7
  • 18
0

This is the answer for C code (not C++). In the code in NPE's answer on the similar question that I found at this link, which I didn't find before I posted this question, I found the malloc(bytes) function does the trick but if you compile with gcc, you have to have to convert the malloc function as a char* manually by using (char*) malloc (bytes). I also found that because it's using pointers you will have to treat the allocated string as a global variable when you free() it. Here is an example of the working code based on the code in my question:

#include <stdio.h>
#include <stdlib.h>

FILE* f;
char* getstring()
    {
    f = fopen ("hello.txt", "r");
    char* x = (char*) malloc (200);
    int length = 0;
    for (int i = 0; i < 200; i++) x[i] = 0;
    for (int c = getc(f), i = 0; (c != EOF) && (i < 200); c = getc(f), i++)
        { x[i] = c; length++; }
    fclose(f);
    return x;
    }

void printstring (char* string)
    {
    printf ("%s", string);
    free (string);
    }

int main()
    {
    printstring(getstring());
    return 0;
    }

And this outputs the exact first 200 bytes of "hello.txt".

Fract
  • 113
  • 6