[英]Use of char* over void* in pointer arithmetic
return (char*)desc + sizeof *desc;
為什么desc
轉換為char*
? 我試圖用我自己的代碼來模仿它:
#include <stdio.h>
#include <stdlib.h>
struct Test {
int value;
};
int main() {
struct Test* test = malloc(sizeof test);
struct Test* test1 = (void*)test + sizeof *test;
test1->value = 1;
printf("%d\n", test1->value);
struct Test* test2 = (void*)test1 + sizeof *test;
test2->value = 10;
printf("%d\n", test2->value);
}
這也有效。 但是,有什么區別呢? 為什么使用char*
?
注意:我使用void*
只是為了看看它是否有效。 由於char*
與所討論的struct
無關,我只是想,“如果我在那里使用void*
怎么辦?”。 一個更具體的問題可能是,為什么不int*
或float*
以及為什么使用char*
?
為什么 desc 轉換為 char*? ...因為
char*
與所討論的struct
無關...
在 C 中,除位域外的每個 object 都由字節序列組成。 1將 object 的地址轉換為char *
會生成指向 object 的第一個字節的指針,您可以使用該指針訪問 object 的各個字節。
在標准 C 中,指針運算使用指向類型的單位。 對於struct Test
類型的指針p
, p+1
指向 p 之后的下一個結構, p
p+2
指向之后的結構,依此類推。 對於char *
類型的指針q
, q+1
指向 q 之后的下一個char
, q+2
指向之后的char
,依此類推。
因此,要訪問 object 的各個字節,您可以將其地址轉換為char *
並使用它。
一個更具體的問題可能是,為什么不
int*
或float*
以及為什么使用char*
?
使用char *
是因為 C 中的所有對象(位域除外)都被定義為表示為字節序列。 它們不一定是int
或float
的序列。 unsigned char *
和signed char *
也可以使用,由於符號問題的復雜性, unsigned char *
可能更可取。
C 標准有關於使用字符指針訪問對象的特殊規則,因此它保證以這種方式訪問 object 的字節是可行的。 相反,使用int *
或float *
訪問對象可能不起作用。 允許編譯器期望指向int
的指針不會用於訪問float
object,並且當它為程序生成機器指令時,它可能會根據該期望編寫這些指令。 使用字符指針可防止編譯器假定char *
與另一種指針指向的位置不同。
注意:我使用
void*
只是為了看看它是否有效。
為了使指針算法起作用,編譯器需要知道指向的 object 的大小。 將 1 添加到指向struct Test
的指針時,編譯器需要知道調整內部地址的字節數。
void
是不完整的類型。 沒有void
對象,並且void
沒有大小。 (大小不為零。沒有大小。)因此,當p
是void *
時,C 標准沒有定義p+1
的任何含義。
但是, GCC 將void *
上的算術定義為擴展。 它的工作方式就好像void
的大小為 1 個字節。 Clang 也支持這個。
由於這個擴展,使用void *
指針進行算術運算與使用char *
指針進行算術運算本質上是相同的。
這個擴展是不必要的; 任何對void *
進行算術運算的代碼都可以改寫為使用char *
。 有時這需要額外的強制轉換來轉換指針類型,這可能就是將擴展添加到 GCC 的原因(以減少所需的代碼量並使其看起來更好)。
您可以使用開關-Werror -Wpointer-arith
禁用此擴展,或者您通常可以使用-Werror -std=c18 -pedantic
。 我盡可能使用-Werror -std=c18 -pedantic
,我推薦它。
1位域是保存在一些較大的字節容器中的位序列,可能恰好與字節重合。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.