[英]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 *
- 它已經是一個指針類型。 每個成員s
和q
的類型都是char *
。 要訪問s
或q
成員,您需要取消引用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.