2014-04-11 121 views
1

我正在處理嵌套結構和聯合的簡單C程序,並且遇到了返回指向char數組的指針的問題。函數返回意外的指針

下面是代碼:

#define BUFSIZE 32 

typedef enum { 
    S1 = 0, 
    S2 
} type_t; 

typedef struct { 
    int x, y; 
    char value[BUFSIZE]; 
} s1_t; 

typedef struct { 
    s1_t s; 
    char value2[BUFSIZE]; 
} s2_t; 

typedef struct { 
    type_t t; 
    union { 
    s1_t s1; 
    s2_t s2; 
    }; 
} s3_t; 

s3_t* new_s1(int x, int y, char* value) { 
    s3_t* s; 
    if ((s = malloc(sizeof(s3_t))) == NULL) 
    return NULL; 
    memset(s, 0, sizeof(s3_t)); 
    s->t = S1; 
    s->s1.x = x; 
    s->s1.y = y; 
    strncpy(s->s1.value, value, BUFSIZE); 
    return s; 
} 

s3_t* new_s2(int x, int y, char* value, char* value2) { 
    s3_t* s; 
    if ((s = malloc(sizeof(s3_t))) == NULL) 
    return NULL; 
    memset(s, 0, sizeof(s3_t)); 
    s->t = S2; 
    s->s2.s.x = x; 
    s->s2.s.y = y; 
    strncpy(s->s2.s.value, value, BUFSIZE); 
    strncpy(s->s2.value2, value2, BUFSIZE); 
    return s; 
} 

// The problem comes from this function ? 
char* get_value(s3_t s) { 
    return (s.t == S1) ? s.s1.value : s.s2.s.value; 
} 

int main(void) { 
    s3_t *a, *b; 
    char *p1, *p2; 
    if ((a = new_s1(1, 2, "A")) == NULL) 
    return 1; 
    if ((b = new_s2(1, 2, "ABCD", "VAL2")) == NULL) 
    return 2; 
    p1 = get_value(*a); 
    printf("a (%p) => P1 : (%p - %s) - (%p - %s)\n", a, p1, p1, a->s1.value, a->s1.value); 
    p2 = get_value(*b); 
    printf("b (%p) => P1 : (%p - %s) - (%p - %s)\n", b, p2, p2, b->s2.s.value, b->s2.s.value); 
    printf("strcmp(p1,p2) = %d\n", strcmp(p1, p2)); 
    free(a); 
    free(b); 
    return 0; 
} 

和輸出:

a (0x1974010) => P1 : (0x7fff085df16c - A) - (0x197401c - A) 
b (0x1974070) => P2 : (0x7fff085df16c - ABCD) - (0x197407c - ABCD) 
strcmp(p1,p2) = 0 

的問題是,由get_value函數返回的指針是相同的,即使PARAMS不是(」 a「,然後是」b「),所以strcmp()返回0. 如您所見,get_value(*a)返回指針0x7fff085df16c,爲什麼不是0x197401c

因爲返回的指針是一樣的,如果我改變主要在:

// ... 
p1 = get_value(*a); 
p2 = get_value(*b); 
printf("a (%p) => P1 : (%p - %s) - (%p - %s)\n", a, p1, p1, a->s1.value, a->s1.value); 
printf("b (%p) => P2 : (%p - %s) - (%p - %s)\n", b, p2, p2, b->s2.s.value, b->s2.s.value); 
printf("strcmp(p1,p2) = %d\n", strcmp(p1, p2)); 
// ... 

P1的字符串值由P2的字符串值覆蓋。 所以輸出的樣子:

a (0x1156010) => P1 : (0x7fffb81d8cbc - ABCD) - (0x115601c - A) 
b (0x1156070) => P2 : (0x7fffb81d8cbc - ABCD) - (0x115607c - ABCD) 
strcmp(p1,p2) = 0 

當然,我可以通過改變功能get_value,使其複製字符串並返回另一個指針解決這個問題。

char* get_value(s3_t s) { 
    char* p; 
    if ((p = malloc(BUFSIZE)) == NULL) 
    return NULL; 
    strncpy(p, ((s.t == S1) ? s.s1.value : s.s2.s.value), BUFSIZE); 
    return p; 
} 

但我不(我想)知道爲什麼由get_value返回的指針是從結構的指針不同。我錯過了什麼 ?

+1

結構中沒有「指針」。你的結構只包含整數和數組。數組和指針是不同的東西。 'get_value'的's'副本中的數組與複製的原始結構中的數組位於不同的存儲位置。 –

+1

另外:與你的主要問題無關,但'strncpy'函數有時不會空終止緩衝區。如果您的意圖是將緩衝區視爲包含字符串,則需要執行其他操作(例如,手動終止它,或者我的首選項「snprintf」)。 –

回答

2

C中的結構/聯合類型(所有類型,真的)都是按值傳遞的。

所以在這個代碼:

char* get_value(s3_t s) { 

} 

編譯器將分配在堆棧上s3_t和主叫方將其內容複製到所分配的堆棧空間。

通過返回s.s1.value的內容,您將返回一個指向堆棧中分配的空間的指針。當get_value()返回到 調用者時,該內存被取消分配。

有些編譯器在發生此錯誤時足以提醒您。例如,我把你的get_value()clang-Wall,得到了以下幾點:

$ /opt/llvm+clang-3.4/bin/clang -Wall -o pbv.o -c pbv.c 
pbv.c:28:24: warning: address of stack memory associated with local variable 's' returned [-Wreturn-stack-address] 
    return (s.t == S1) ? s.s1.value : s.s2.s.value; 
        ^
1 warning generated. 
+2

@OP,解決這個問題的最簡單方法是'char * get_value(s3_t * s)' –

+0

謝謝,我完全忘了這個細節......我用gcc 4.8.1編譯了-Wall和-Wextra標誌,但沒有這樣的警告。 –

+0

是的,我認爲'gcc'也會抓住它。 –

1

此功能將您稱爲:

char* get_value(s3_t s) { 
    return (s.t == S1) ? s.s1.value : s.s2.s.value; 
} 

這個函數值作爲參數(不是指針).. 你應該是這樣的:

char* get_value(s3_t *s) { 
    return (s->t == S1) ? s->s1.value : s->s2.s.value; 
} 

p1 = get_value(a); 

如果你想保留來自調用者的指針..