functions: how return char* correctly?

Pages: 12
i always get the same problem for return char* on functions :(
see these function(return the char* in uppercase way):
1
2
3
4
5
6
7
8
9
10
11
12
13
char *convertToUpperCase(const char *sPtr)
{
    char newstr [strlen(sPtr)+1];
    int intsPtr=0;
    while (sPtr[intsPtr]!='\0')
    {
        newstr[intsPtr]=toupper(sPtr[intsPtr]);
        intsPtr++;
    }
    newstr[intsPtr]='\0';
    std::cout <<endl << newstr<<endl;
    return newstr;
}

yes the 'newstr' is printed. but why isn't returned?
what i'm doing wrong?
tested:
1
2
3
4
char *chr1=convertToUpperCase(file.cFileName);
                 char *chr2=convertToUpperCase(FindDirectory);

                std::wcout << chr1 << "hey" << std::endl;

no results :(
make newstr dynamic or static, or pass it into the function to be assigned that way instead of via a return. Regardless, if its dynamically created, you need to delete it someplace. If its static, its not thread-safe (nor is it multiple result safe, eg you can't have 2 pointers to 2 different results from 2 calls to the function active at once!!!!) /shrug. Static is not usually a good solution unless you have very simple problems.

unrelated weirdness:
you do a while loop looking for zero terminal, then assign the zero terminal. why?


Last edited on
i did what i did for try... and no sucess, because 'sPtr' it's changed too :(
1
2
3
4
5
6
7
8
9
10
11
12
char *convertToUpperCase(const char *sPtr)
{
    char *newstr = new char[strlen(sPtr)+1];
    int intsPtr=0;
    while (sPtr[intsPtr]!='\0')
    {
        newstr[intsPtr]=toupper(sPtr[intsPtr]);
        intsPtr++;
    }
    std::cout <<endl << newstr<<endl;
    return newstr;
}


this should work. delete the result when you are done with it, though. Does it or not?
The first problem is that you are using a VLA in C++. Secondly, you are trying to return it from the function, but it is a local array so you can't return it.

If you really need a new string (instead of modifying the input string itself) then:

1
2
3
4
5
6
7
8
char* convertToUpperCase(const char *s) {
    char *newstr = new char[strlen(s) + 1];
    size_t i = 0;
    for ( ; s[i]; i++)
        newstr[i] = toupper(s[i]);
    newstr[i] = '\0';
    return newstr;
}


Remember to delete the array at some point.
both samples use 'new'... why?
using an array isn't the same?
what is 'VLA'?
what is 'VLA'?

Variable Length Array. An array on the stack of size not known at compile time. In C++, it's illegal. Bad C++.

char newstr [strlen(sPtr)+1];
That is a VLA. It's an array on the stack (didn't make it using new) but the size is not known at compile time. It's illegal in C++. Bad. Your compiler shouldn't let you do it. Some compilers let you do it. They shouldn't. It's illegal C++.
If you want to return something that was allocated in a function then it needs to be dynamic (or static, as jonnin said, but as he also mentioned, that tends to be a suboptimal approach).

Dynamic arrays are allocated with new.

VLA: https://en.wikipedia.org/wiki/Variable-length_array

Note that the list of programming languages that support VLAs doesn't include C++. Even C11 relegates them to a conditional feature since they are generally considered to have been a bad idea.

Basically, this is illegal in standard C++:
1
2
3
    int size;
    std::cin >> size;
    int arr[size];    // this is a VLA 


The only VLA that I like: https://en.wikipedia.org/wiki/Very_Large_Array
Last edited on
before finish: speaking in these case, do i realy need delete the
char *chr1=convertToUpperCase(file.cFileName);
'chr1'?
the 'new' is inside the function..
i just need more information.. sorry
Yes. The whole point is that the "new" is inside the function, creating a "dynamic" array which it is therefore allowed to return to the caller. But now the caller is responsible for the allocated memory, which must be deleted at some point (in a properly-written program).
1
2
3
4
char *foo(){
    char ret[] = "Hello";
    return ret;
}

Explanation for why this is unsafe:

On most processors, the above code is compiled to machine code that behaves basically like this:
1
2
3
4
5
6
7
8
9
10
11
foo:
    stack_pointer += 6;
    stack_pointer[-6] = 'H';
    stack_pointer[-5] = 'e';
    stack_pointer[-4] = 'l';
    stack_pointer[-3] = 'l';
    stack_pointer[-2] = 'o';
    stack_pointer[-1] = 0;
    return_register = stack_pointer - 6;
    stack_pointer -= 6;
    return;
Now, what you must know is that nearly every platform out there uses interrupts to handle hardware events. Interrupts can happen at any time and they take control away from any code that happens to be running. Generally what an interrupt does is jump immediately to a special system function (called an interrupt handler) that does something with the interrupt (e.g. move bytes around when data has come in from the hard drive). The interrupt handler is a function too, and needs a call stack to work. It uses your call stack, from the place where you left it, and once it returns it puts the stack pointer back where you had it. But what happens to the data that used to be there? Since the interrupt handler can't waste time or memory saving it for you before doing its job, it will have been replaced by old data from the interrupt handler.
In other words, when you return a pointer to a local, the caller has no guarantee that the data that will be there will be the data that was actually written by the function, and not data that was written by the interrupt handler.
these questions make me thing you are very new to c++.
if that is the case, I am wondering if you can just use string here and stop trying to do C style strings which take a lot of experience, and pointers, which you apparently have never heard of before.

while you should learn about string, let me show you a bypass to your entire issue.

void foo(char * magic)
{
int i = 0;
while(magic[i] = toupper(magic[i++])); //hopefully this upcases, I was feeling creative. (edit: it actually worked, lol)
//return magic; sorry, this was not correct.
}

...
main...

char astring[100]; //how to avoid the pointer foolishness
strcpy(astring,"the quick brown fox");
foo(astring); //upcases astring
cout << astring << endl;

This worked for me. So what we did here is bypass the problem by creating the string in main, as a local variable, and just modifying it in the function, without trying to copy it or allocate more memory or any of that. If you need the original, you can use strcpy to store a copy that can be upper cased. I also made you a somewhat obnoxious version of your uppercase function ... you don't have to do it that way, its not productive to write code like that, its just fun once in a while.


Last edited on
Break down the problem more. Your functions need to do one thing and one thing only. Start with a function that converts a string to upper-case, in-place:

1
2
3
4
5
6
7
8
char *
my_strupr(char *s)
{
  char *start = s;
  for (; *s; ++s)
    *s = std::toupper(*s);
  return start;
}


Since you don't want to convert a string in-place, best write a function to duplicate strings so that you can convert the duplicate:
1
2
3
4
5
6
7
char *
my_strdup(char const *src)
{
  char *dst = new char[ strlen(src) + 1 ];
  std::strcpy(dst, src);
  return dst;
}


Then work on combining the two:
1
2
3
4
5
char *
strupr_copy(char const *s)
{
  return my_strupr(my_strdup(s));
}

Don't forget to delete[] the result. Complete example: https://ideone.com/JxZseZ

What's wrong with std::string?

1
2
3
4
5
6
std::string upcase_string(std::string s) 
{
  for (auto& ch: s) 
    ch = std::toupper(ch);
  return s;
}



Last edited on
i was using char*, but int* or other it's the same... so now i know that i must create a new char* using the 'new char[size]' way.
thank you so much to all.. thank you
well, you don't 'have' to, as I showed. There are many ways to solve most problems :)

If you prefer the new/delete approach, do yourself a favor and understand it before you move on. If you don't you will have ongoing problems when you try to use pointers. Understand both what mbozzi gave you and why your original was not right before you move on, and you will have much better success going forward.
Last edited on
"There are many ways to solve most problems :) "
it's the programming way :)
but why isn't returned?

I've only skimmed the thread, but I don't think anyone has properly explained this, so I'll have a go.

The char* is being returned just fine. The problem is that you don't seem to understand properly what a char* is.

A char* (or an int*, or an Anything*) is a pointer. And a pointer is nothing but a single number. That number is a memory address. When we say that the pointer "points to" something, we mean that it stores the address of the memory where that something is stored.

The tricky thing when using pointers is that you have to make sure that the address stored in the memory has been properly reserved to store the data you're pointing to, for as long as you want to point to it.

In your original code, the memory that newstr points to, is allocated on the stack. That means that, when the function finishes, the memory is freed up; it is no longer reserved for that data. So, your function returns the address of that memory (a pointer), but when the calling code tries to use that address, that memory is no longer storing the data you wanted it to store.

This is why using new is one solution to your problem. By dynamically allocating the memory for the string on the heap, it stays allocated until you decide to use delete to free it up again. That memory stays reserved for storing the data that was put there by the function, even after the function has finished, and will remain reserved until you delete it.

I hope that clears things up?
Last edited on
And, mine allocates it on the stack, as you did, but it is allocated *where you need it*. So its created and existing in main, and the function to uppercase it just modifies the data in-place (if you needed a copy you have to make one). So main has not finished yet when the function is called, so that memory is still valid. Then it uppercases it and you use it and the memory is still valid. The memory exists until main exits (or any other function that uses it will work this way too).

The issue, to reiterate it once more, is that you get memory in the function, change it, and then exit the function which releases the memory.
thank you so much to all.. thank you
You're welcome. Hope the explanation made it clearer how pointers work, and what things you need to think about when using them.
Pages: 12