在指针运算中使用 char* 而不是 void*
Use of char* over void* in pointer arithmetic
我正在经历这个 code for learning purposes and have a question about this line:
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*
?
为什么使用指向字符的指针
Why is desc cast to char*? … As char*
has nothing to do with the struct
in question…
在 C 语言中,除位域外的每个对象都由字节序列组成。1将对象的地址转换为 char *
会产生指向对象的第一个字节,您可以使用该指针访问对象的各个字节。
在标准 C 中,指针算法使用指向类型的单元。对于struct Test
类型的指针p
,p+1
指向p
之后的下一个结构,p+2
指向下一个结构,以此类推。对于char *
类型的指针q
,q+1
指向q之后的下一个char
,q+2
指向q之后的char
,依此类推上。
因此,要访问对象的各个字节,您可以将其地址转换为 char *
并使用它。
为什么不使用其他类型的指针
A more specific question could be, why not int*
or float*
& why char*
is used?
使用 char *
是因为 C 中的所有对象(位域除外)都被定义为表示为字节序列。它们不一定是 int
或 float
的序列。也可以使用 unsigned char *
和 signed char *
,由于符号问题的复杂性,unsigned char *
可能更可取。
C 标准有关于使用字符指针访问对象的特殊规则,因此它保证以这种方式访问对象的字节是有效的。相反,使用 int *
或 float *
访问对象可能不起作用。允许编译器期望指向 int
的指针不会用于访问 float
对象,并且当它为程序生成机器指令时,它可能会根据该指令编写这些指令期待。使用字符指针可防止编译器假定 char *
与另一种指针不指向同一位置。
为什么指向 Void 的指针起作用
Note: I used void*
just to see if that works.
要使指针算法起作用,编译器需要知道所指向对象的大小。当指向a的指针加1时struct Test
,编译器需要知道将内部地址调整多少字节。
void
是一个不完整的类型。没有 void
个对象,void
没有大小。 (大小不为零。没有大小。)因此,当 p
是 void *
.
时,C 标准没有为 p+1
定义任何含义
然而,GCC defines arithmetic on void *
as an extension。它的工作原理就好像 void
的大小为 1 个字节。 Clang 也支持这个。
由于这个扩展,使用 void *
指针进行算术运算与使用 char *
指针进行算术运算本质上是相同的。
这个扩展是不必要的;任何在 void *
上进行算术运算的代码都可以改写为使用 char *
。有时这需要额外的转换来转换指针类型,这可能是将扩展添加到 GCC 的原因(以减少所需的代码量并使其看起来更好)。
您可以使用开关 -Werror -Wpointer-arith
禁用此扩展,或者您通常可以使用 -Werror -std=c18 -pedantic
请求更接近标准 C。我尽可能使用-Werror -std=c18 -pedantic
,我推荐它。
脚注
1 位域是保存在一些更大的字节容器中的位序列,并且可能恰好与字节重合。
我正在经历这个 code for learning purposes and have a question about this line:
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*
?
为什么使用指向字符的指针
Why is desc cast to char*? … As
char*
has nothing to do with thestruct
in question…
在 C 语言中,除位域外的每个对象都由字节序列组成。1将对象的地址转换为 char *
会产生指向对象的第一个字节,您可以使用该指针访问对象的各个字节。
在标准 C 中,指针算法使用指向类型的单元。对于struct Test
类型的指针p
,p+1
指向p
之后的下一个结构,p+2
指向下一个结构,以此类推。对于char *
类型的指针q
,q+1
指向q之后的下一个char
,q+2
指向q之后的char
,依此类推上。
因此,要访问对象的各个字节,您可以将其地址转换为 char *
并使用它。
为什么不使用其他类型的指针
使用A more specific question could be, why not
int*
orfloat*
& whychar*
is used?
char *
是因为 C 中的所有对象(位域除外)都被定义为表示为字节序列。它们不一定是 int
或 float
的序列。也可以使用 unsigned char *
和 signed char *
,由于符号问题的复杂性,unsigned char *
可能更可取。
C 标准有关于使用字符指针访问对象的特殊规则,因此它保证以这种方式访问对象的字节是有效的。相反,使用 int *
或 float *
访问对象可能不起作用。允许编译器期望指向 int
的指针不会用于访问 float
对象,并且当它为程序生成机器指令时,它可能会根据该指令编写这些指令期待。使用字符指针可防止编译器假定 char *
与另一种指针不指向同一位置。
为什么指向 Void 的指针起作用
Note: I used
void*
just to see if that works.
要使指针算法起作用,编译器需要知道所指向对象的大小。当指向a的指针加1时struct Test
,编译器需要知道将内部地址调整多少字节。
void
是一个不完整的类型。没有 void
个对象,void
没有大小。 (大小不为零。没有大小。)因此,当 p
是 void *
.
p+1
定义任何含义
然而,GCC defines arithmetic on void *
as an extension。它的工作原理就好像 void
的大小为 1 个字节。 Clang 也支持这个。
由于这个扩展,使用 void *
指针进行算术运算与使用 char *
指针进行算术运算本质上是相同的。
这个扩展是不必要的;任何在 void *
上进行算术运算的代码都可以改写为使用 char *
。有时这需要额外的转换来转换指针类型,这可能是将扩展添加到 GCC 的原因(以减少所需的代码量并使其看起来更好)。
您可以使用开关 -Werror -Wpointer-arith
禁用此扩展,或者您通常可以使用 -Werror -std=c18 -pedantic
请求更接近标准 C。我尽可能使用-Werror -std=c18 -pedantic
,我推荐它。
脚注
1 位域是保存在一些更大的字节容器中的位序列,并且可能恰好与字节重合。