Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return Pointer from function with pointer as param

I'm reading this book: "C von A bis Z".

There is this example.

/* ptr14.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Fehler: Funktion gibt die Adresse
 * einer lokalen Variablen zurück. */
/* [ Error: Function returns the address of a
     a local variable. ] */
// ...

/* Möglichkeit2: Speicher vom Heap verwenden */
/* [ Option2: Use memory from the heap ] */
char *test3(void){
   char *buffer = (char *) malloc(10);
   strcpy(buffer, "testwert");
   return buffer;
}

/* Möglichkeit3: Einen Zeiger als Argument übergeben */
/* [ Option3: Pass a pointer as argument ] */
char *test4(char *ptr){
   char buffer[10];
   ptr = buffer;
   strcpy(buffer, "testwert");
   return ptr;
}
int main(void) {
   char *ptr;

   /* ... */

   ptr = test3();
   printf("test3: %s\n", ptr);
   test4(ptr);
   printf("test4: %s\n", ptr);
   return EXIT_SUCCESS;
}

I'm understanding the problem the author is talking about.

Why the test4 solution is working?

If I am understanding correctly, doesn't it

  1. allocate char buffer[10]; on the stack
  2. assign the address of the first element of buffer to my ptr (living in previous scope) ptr = buffer;

my expectation:

Point with ptr on buffershould be false, because this scope should be broken/cleaned up.

What is wrong with my thinking?

EDIT 1

I changed test4(ptr); to ptr = test4(ptr) and it still works...

Still don't know why test4(char* ptr) is working...

like image 348
Wulf Avatar asked Apr 01 '19 07:04

Wulf


People also ask

How do you pass a pointer to a function as a parameter?

Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.

How do you return a pointer from a function?

Return Function Pointer From Function: To return a function pointer from a function, the return type of function should be a pointer to another function. But the compiler doesn't accept such a return type for a function, so we need to define a type that represents that particular function pointer.

Can you return a pointer in C++?

C++ allows you to return a pointer to this array, but it is undefined behavior to use the memory pointed to by this pointer outside of its local scope.


2 Answers

Nothing is wrong with your thinking - you're absolutely correct. Good work, you're now more qualified in the C programming language than the author of the book.

The book is worthless - 3rd revised edition, and it teaches the antiquated version of C from 3 decades ago with horribly broken examples. You just happened to be lucky with that test4. Putting the address of the first element of array just suppresses the warning in some compilers, and the array happened to be in the correct position on the stack and not being overwritten. But GCC 8.3 isn't fooled by using an intermediate variable.


In the function

char *test4(char *ptr){
    char buffer[10];
    ptr = buffer;
    strcpy(buffer, "testwert");
    return ptr;
}

using the ptr within the function does in no way affect the pointer outside the function. It worked in the original example because the ptr was still pointing to the value returned from test3, which was allocated from heap. When you replace it with ptr = test4(ptr); you'll get wholly undefined behaviour, as ptr now points to a variable past its lifetime. And when undefined behaviour happens, then the program might do anything, including (C11 3.4.3p1):

[...] ignoring the situation completely with unpredictable results [...]

with "unpredictable results" including the possibility of it working "as intended".


The previous bulletin point lists one of the options as

  • [Sie verwenden] einen beim Aufruf der Funktion als Argument übergebenen Puffer [...]

i.e. [You'll use] a buffer passed as an argument into the function. For this option, test4 should read

// use the **array** starting from *ptr
char *test4(char *ptr){
    // use a **different** string here so that you can verify
    // that it actually *works* (max 9 characters!)
    strcpy(ptr, "testval 4");
    return ptr;
}

or even perhaps

void test4(char *ptr){
    strcpy(ptr, "testval 4");
}

with documentation telling that prior to calling this function ptr should point to an array of at least 10 chars.

like image 106

char *test4(char *ptr) {
    char buffer[10];
    ptr = buffer;
    strcpy(buffer, "teswert");
    return ptr;
}

This code doesn't do anything else than returning an invalid pointer. Your understanding is correct, the stack pointer returned is invalid and shouldn't be read.

The reason why this "works" is because that pointer isn't actually used.

test4(ptr);

A copy of the pointer is passed and the return value is discarded, so it does nothing. The printed text is from test3. Case in point, you can change that one "testwert" and the print you get is the exact same, and if you change the one in test3 it changes both prints. So in other words, the book makes a mistake and hides it with another mistake, and then it doesn't notice all the mistakes because of how poorly it tests the code (if it wouldn't be "testwert" four times, the errors would be apparent, and of course any compiler worth its salt will issue a warning).

I recommend trashing that book.


With the edited version of ptr = test4(ptr) it's undefined behavior, so anything might happen. This includes printing the expected output, printing rubbish, crashing the program or worse.

like image 4
Blaze Avatar answered Oct 21 '22 02:10

Blaze