Два указателя, расположенные по одному адресу, но указывающие на разные вещи - PullRequest
0 голосов
/ 24 октября 2018

Вот код:

#define _GNU_SOURCE
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }


// memory-safe asprintf, implemented as a macro
#define Sasprintf(write_to, ...)            \
    {                                       \
        char* tmp = (write_to);             \
        printf("==== before asprintf\n");   \
        DebugPointer(tmp);                  \
        DebugPointer(write_to);             \
        asprintf(&(write_to), __VA_ARGS__); \
        printf("==== after  asprintf\n");   \
        DebugPointer(tmp);                  \
        DebugPointer(write_to);             \
        free(tmp);                          \
    }

int main() {
    char* s = NULL;
    int n = 0;
    Sasprintf(s, "%s", "aa\n");
    Sasprintf(s, "%s%s", s, "bb\n");
    Sasprintf(s, "%s%s", s, "cc\n");
    printf("string: %s\n", s);
    free(s);
}

Макрос Sasprintf взят из книги 21-го века.Я пытаюсь понять, как это работает.Результаты выглядят немного странно:

==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: (nil)
       s              address: 0x7ffe8b767cd8
       s            points to: (nil)
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: (nil)
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae6e0
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae6e0
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae700
==== before asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae700
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae700
==== after  asprintf
     tmp              address: 0x7ffe8b767cd8
     tmp            points to: 0x560969cae700
       s              address: 0x7ffe8b767cd8
       s            points to: 0x560969cae6e0
string: aa
bb
cc

Похоже, что два указателя tmp и s находятся по одному и тому же адресу, но указывают на разные вещи.Почему?

Ответы [ 3 ]

0 голосов
/ 24 октября 2018
#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }

Итак, вы печатаете адрес p1, который является локальным для каждого вызова DebugPointer, поэтому он может быть одинаковым при каждом вызове.

похоже, цель p1 состоит в том, чтобы избежать многократных вычислений макропараметра p.Чтобы достичь цели печати, вы можете изменить макрос, чтобы получить адрес заранее.

#define DebugPointer(p)                                         \
    do {                                                        \
        __typeof__(&(p)) p1 = &(p);                             \
        void *p2 = *p1;                                         \
        printf("%8s %20s: %p\n", #p, "address", (void*)p1);     \
        printf("%8s %20s: %p\n", #p, "points to", p2);          \
    } while(0)

Вот определение Sasprintf, которое я нашел онлайн :

#define Sasprintf(write_to,  ...) {           \
    char *tmp_string_for_extend = (write_to); \
    asprintf(&(write_to), __VA_ARGS__);       \
    free(tmp_string_for_extend);              \
}

Он использует временную функцию для хранения исходного указателя, который должен был быть выделен предыдущим вызовом для Sasprintf (или инициализирован для NULL).Вызов asprintf может измениться, куда он указывает.Теперь макрос может вызывать free для исходного указателя.

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

#define Sasprintf(write_to,  ...) do { \
    char **dest = &(write_to);         \
    free(*dest);                       \
    asprintf(dest, __VA_ARGS__);       \
} while(0)
0 голосов
/ 24 октября 2018

Два указателя, tmp и s, находятся в одном месте;однако они делают это в разное время.

Причиной вышеизложенного является это объявление:

__typeof__(p) p1 = p;

Копирует p в p1, который является переменнойлокально для вмещающего блока кода.Макрос DebugPointer раскрывается дважды, каждый раз создавая новую локальную переменную с именем p1 во временной области.

Компилятор замечает, что две переменные p1 никогда не существуют вместе в одно и то же время, поэтому он повторно используетпространство первого экземпляра p1 для хранения значения второго экземпляра p1.

Примечание: Теперь вы, вероятно, задаетесь вопросом, зачем вам нужен p1 впервое место.Это связано с тем, что вы применяете к нему оператор адреса &.Вместо этого можно написать &(p), но такой макрос будет хрупким: он будет работать в вашем примере ( demo ), но вызов, скажем, DebugPointer(tmp+1) вызовет ошибку компиляции.

0 голосов
/ 24 октября 2018

Проблема в вашей процедуре печати:

#define DebugPointer(p)                                         \
    {                                                           \
        __typeof__(p) p1 = p;                                   \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p1));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p1)); \
    }

Вы объявляете новую переменную с именем p1 и печатаете адрес этой переменной .В этом случае бывает так, что эта переменная имеет один и тот же адрес каждый раз, когда попадает в область действия.

Избавьтесь от дополнительной переменной, чтобы напечатать адрес фактической переменной, которая вас интересует.

#define DebugPointer(p)                                        \
    {                                                          \
        printf("%8s %20s: %p\n", #p, "address", (void*)&(p));  \
        printf("%8s %20s: %p\n", #p, "points to", (void*)(p)); \
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...