簡體   English   中英

如何通過指針訪問結構的第二個成員?

[英]How to access second member of struct via pointer?

我已經看到結構的首地址同時是該結構的第一個成員的首地址。 現在我想了解的是,為什么我總是需要雙指針在結構中移動:

#include <stdio.h>
#include <stdlib.h>

struct foo
{
    char *s;
    char *q;
};

int main()
{
    struct foo *p = malloc(sizeof(struct foo));
    char ar[] = "abcd\n";
    char ar2[] = "efgh\n";
    *(char**)p = ar;
    *(char**)((char**)p+1) = ar2; //here pointer arithmetic (char**)p+1

    printf("%s\n",p->q);
}

問題是,為什么我需要char**而不是簡單的char* 我在匯編程序中看到的是在簡單char*的情況下,算術的行為與普通char一樣。 也就是說 -> (char*)p+1的表達式會將地址p移動一個字節(而不是8 ,因為地址是 8 個字節長)。 但是char*類型地址,所以我不明白為什么算術表現得像取消引用類型(普通 char -> 一個字節)。

所以對我來說唯一的解決方案是添加另一個間接char** ,其中指針算術神奇地采用8作為大小。 那么為什么在結構中需要這種奇怪的轉換呢?

你在做有趣的事情。 你應該這樣做:

struct foo *p = malloc(sizeof(struct foo));
char ar[] = "abcd\n";
char ar2[] = "efgh\n";
p->s = ar;
p->q = ar2; 

首先,你在做什么有點奇怪。 這也是不安全的,因為結構成員之間可能存在填充,並且您的地址計算可能會關閉(在這種特殊情況下這可能不是真的,但要記住這一點)。

至於為什么你需要多個指針......

p的類型是struct foo * - 它已經是一個指針類型。 每個成員sq的類型都是char * 要訪問sq成員,您需要取消引用p

(*p).s = ar;    // char * == char *
(*p).q = ar2;   // char * == char *

因此,如果您嘗試通過p訪問s指向的第一個字符,那么您就是在嘗試通過一個指針 ( s ) 和另一個指針 ( p ) 訪問一個字符。 p不存儲 s 的第一個字符的地址,它存儲存儲s的第一個字符的地址的東西s地址。 因此需要將p轉換為char **而不是char *

在這一點上,我必須強調不要這樣做。 您無法使用指針安全地遍歷結構成員。

引入->運算符是為了讓通過指針訪問結構成員變得不那么刺眼:

p->s = ar;  // equivalent to (*p).s = ar
p->q = ar2; // equivalent to (*p).q = ar2

由於結構類型的 object 的地址等於其第一個成員的地址,因此您可以編寫例如

( void * )&p->s == ( void * )p

這是一個演示程序

#include <stdio.h>
#include <stdlib.h>

struct foo
{
    char *s;
    char *q;
};

int main(void) 
{
    struct foo *p = malloc(sizeof(struct foo));
    
    printf( "( void * )p == ( void * )&p->s is %s\n",
           ( void * )p == ( void * )&p->s ? "true" : "false" );
           
    return 0;
}

它的 output 是

true

所以指針p的值等於數據成員s的地址。

換句話說,指向數據成員s的指針等於指針p

由於數據成員s的類型是char * ,因此指向s的指針的類型是char **

要分配指向的 object,您需要將struct foo *類型的指針 p 轉換為char **類型。 要訪問作為數據成員的指針 object,您必須取消引用char **類型的指針。

結果你有

*(char**)p = ar;

現在,數據成員s (即char *類型的指針)被分配了數組ar的第一個元素的地址。

在第二個表達式中,最左邊的轉換是多余的

*(char**)((char**)p+1) = ar2;
 ^^^^^^^^

因為表達式(char**)p+1已經具有類型char ** 所以你可以寫

*((char**)p+1) = ar2;

為什么我需要char**而不是簡單的char*

使用指針,賦值的左側,代碼需要 object 的地址。

*address_of_the_object = object

由於 object 是一個char * ,左側的類型, address of the object ,需要是char **類型。


如何通過指針訪問結構的第二個成員?

最好改為使用明智的:

p->q = ar2;

...然后是令人費解的:

//          |-- address of p->q as a char * ----|
*((char **) ((char *)p + offsetof(struct foo, q))) = ar2;
//|------------ address of p->q as a char ** ---|

OP 的*(char**)((char**)p+1) = ar2; 是不對的,因為它做了錯誤的指針數學運算並假定沒有填充。


復雜的方法細節。

要在struct中查找偏移量,請使用offsetof(struct foo, q) 它返回字節偏移量並將考慮潛在的填充。 將其添加到struct地址的char *版本以進行適當的指針添加以形成p->q的地址。 該總和是一個char * ,轉換為 object 的地址類型。最后在 LHS 上取消引用它作為分配的一部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM