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
char buffer[10];
on the stackbuffer
to my ptr
(living in previous scope) ptr = buffer;
my expectation:
Point with ptr
on buffer
should 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...
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.
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.
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.
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 char
s.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With