Вернуть указатель из функции с указателем в качестве параметра - PullRequest
6 голосов
/ 01 апреля 2019

Я читаю эту книгу: "C von A bis Z" .

Вот этот пример.

/* 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;
}

Я понимаю проблему, о которой говорит автор.

Почему работает решение test4?

Если я правильно понимаю, не так ли

  1. выделить char buffer[10]; в стеке
  2. присвойте адрес первого элемента buffer моему ptr (проживающему в предыдущей области видимости) ptr = buffer;

мои ожидания:

Точка с ptr на buffer должна быть ложной, потому что эта область должна быть разбита / очищена.

Что не так с моим мышлением?

РЕДАКТИРОВАТЬ 1

Я изменил test4(ptr); на ptr = test4(ptr) и он все еще работает ...

До сих пор не знаю, почему test4(char* ptr) работает ...

Ответы [ 2 ]

7 голосов
/ 01 апреля 2019

В вашем мышлении нет ничего плохого - вы абсолютно правы. Хорошая работа, теперь вы более квалифицированы в языке программирования Си, чем автор книги.

Книга бесполезна - 3-е пересмотренное издание, и в ней рассказывается об устаревшей версии C, написанной три десятилетия назад, с ужасно разбитыми примерами. Вы только что оказались счастливчиком с этим test4. Размещение адреса первого элемента массива просто подавляет предупреждение в некоторых компиляторах, и массив оказался в правильной позиции в стеке и не перезаписывается. Но GCC 8.3 не одурачить, используя промежуточную переменную.


В функции

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

использование ptr внутри функции никоим образом не влияет на указатель вне функции. Это работало в исходном примере, потому что ptr был все еще , указывая на значение, возвращаемое из test3, которое было выделено из кучи. Когда вы замените его на ptr = test4(ptr);, вы получите совершенно неопределенное поведение, так как ptr теперь указывает на переменную после ее времени жизни. И когда происходит неопределенное поведение, программа может сделать что-нибудь, в том числе ( C11 3.4.3p1 ):

[...] полное игнорирование ситуации с непредсказуемыми результатами [...]

с «непредсказуемыми результатами», включая возможность того, что он будет работать «как положено».


В предыдущем пункте бюллетеня указан один из вариантов:

  • [Sie verwenden] einen beim Aufruf der Funktion als Аргумент übergebenen Puffer [...]

т.е. [Вы будете использовать] буфер, переданный в качестве аргумента в функцию . Для этой опции test4 должно читаться как

// 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;
}

или даже возможно

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

с документацией о том, что перед вызовом этой функции ptr должен указывать на массив не менее 10 char с.

4 голосов
/ 01 апреля 2019
char *test4(char *ptr) {
    char buffer[10];
    ptr = buffer;
    strcpy(buffer, "teswert");
    return ptr;
}

Этот код не делает ничего, кроме возврата неверного указателя.Ваше понимание верно, возвращенный указатель стека недействителен и не должен читаться.

Причина, по которой это "работает", заключается в том, что этот указатель фактически не используется.

test4(ptr);

Копия указателя передается и возвращаемое значение отбрасывается, поэтому он ничего не делает.Печатный текст от test3.В данном случае вы можете изменить это значение "testwert", и полученный вами отпечаток будет точно таким же, а если вы измените его в test3, он изменит оба отпечатка.Другими словами, книга совершает ошибку и скрывает ее с другой ошибкой, а затем не замечает всех ошибок из-за того, как плохо она тестирует код (если это не будет "testwert" четыре раза, ошибкибыло бы очевидно, и, конечно, любой компилятор, достойный его соли, выдаст предупреждение).

Я рекомендую уничтожить эту книгу.


С отредактированной версией ptr = test4(ptr) это неопределенное поведениетак что все может случиться.Это включает в себя печать ожидаемого вывода, печать мусора, сбой программы или еще хуже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...