两个指针位于同一地址但指向不同的事物

时间:2018-10-24 20:01:18

标签: c pointers

代码如下:

#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世纪的c书。我试图了解它是如何工作的。结果看起来有些奇怪:

==== 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

看起来两个指针tmps位于相同的地址,但指向不同的事物。为什么?

3 个答案:

答案 0 :(得分:2)

#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)

这是我发现的onlineSasprintf定义:

#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)

答案 1 :(得分:1)

问题出在您的打印例程中

VueJs

您要声明一个名为<script> export default { data() { return { ticketInvoices: {}, form: new Form({ id: "", vendor_id: "", ticket_invoice_no: "", ticket_invoice_date: "", ticket_invoice_fares_total: "", ticket_invoice_taxes_grand_total: "", ticket_invoice_grand_total: "", ticketInvoiceItems: [{ id: "", ticket_invoice_id: "", passenger_name: "", tax_SB: 0, tax_SRP: 0, tax_YQ: 0, tax_RG: 0, tax_PK: 0, tax_YR: 0, tax_SF: 0, tax_PTT: 0, tax_OAS: 0, tax_PSF: 0, tax_PB: 0, tax_OAD: 0, fares: "", total_tax_breakup: 0, sub_total: "" }] }) }; }, computed: { totalTax() { let calTaxTotal = parseInt(this.form.ticketInvoiceItems[0].tax_SB) + parseInt(this.form.ticketInvoiceItems[0].tax_SRP) + parseInt(this.form.ticketInvoiceItems[0].tax_YQ) + parseInt(this.form.ticketInvoiceItems[0].tax_RG) + parseInt(this.form.ticketInvoiceItems[0].tax_PK) + parseInt(this.form.ticketInvoiceItems[0].tax_YR) + parseInt(this.form.ticketInvoiceItems[0].tax_SF) + parseInt(this.form.ticketInvoiceItems[0].tax_PTT) + parseInt(this.form.ticketInvoiceItems[0].tax_OAS) + parseInt(this.form.ticketInvoiceItems[0].tax_PSF) + parseInt(this.form.ticketInvoiceItems[0].tax_PB) + parseInt(this.form.ticketInvoiceItems[0].tax_OAD); this.form.ticketInvoiceItems[0].total_tax_breakup = calTaxTotal; return calTaxTotal; } }, } </script> 的新变量,并打印那个变量的地址。在这种情况下,碰巧发现该变量每次进入范围都具有相同的地址。

摆脱多余的变量,以显示您感兴趣的实际变量的地址。

#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)); \
    }

答案 2 :(得分:1)

两个指针tmps共享同一位置;但是,他们在不同的时间这样做。

发生上述情况的原因是此声明:

__typeof__(p) p1 = p;

它将p的副本复制到p1中,这是封闭代码块局部的变量。 DebugPointer宏被扩展了两次,每次在临时作用域内创建一个名为p1的新局部变量。

编译器会注意到两个变量p1永远不会同时存在,因此它会重用p1的第一个实例的空间来存储p1的第二个实例的值

注意:现在您可能想知道为什么首先需要p1。这样做的原因是您要向其应用&运算符的地址。您可以改写&(p),但是这样的宏会很脆弱:它可以在您的示例(demo)中工作,但是调用DebugPointer(tmp+1)会导致编译错误。