我注意到GCC触发器:
warning: dereferencing ‘void *’ pointer
Run Code Online (Sandbox Code Playgroud)
在获取解除引用的void
表达式的地址时:
int main()
{
void *p = "abcdefgh";
printf("%p\n", p);
printf("%p\n", &*p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,表达式p
相当于&*p
根据C标准:
§6.5.3.2地址和间接运营商
一元&运算符返回其操作数的地址.如果操作数具有类型''type'',则结果具有类型''指向类型''的指针.如果操作数是一元*运算符的结果,则不会对该运算符和&运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值.
知道Clang不会触发此警告也很重要.
免责声明
为了更好的解释,请考虑这个:
int main()
{
int *p = NULL;
printf("%p\n", (void *) p);
printf("%p\n", (void *) &*p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此代码与Clang和GCC完美编译并运行.C标准清楚void
指针,你可以取消引用它们(从而得到一个void
值)但你不能使用表达式的结果.
我正在尝试构建一个 Web 框架(学校项目),该框架必须是面向 API 的,我将使用 PHP 在以 API 为中心的架构上构建 Web 应用程序:
如何向位于同一服务器上的 API 发出请求?我担心在同一台服务器上使用curl发出HTTP请求会太重,其他选择是什么?
谢谢
void
在各种不同的情况下,C中的类型似乎很奇怪.有时它的行为类似于普通的对象类型,例如int
or char
,有时它只是意味着什么(应该如此).
看看我的片段.首先,你可以声明一个void
对象似乎很奇怪,这意味着你什么都不宣布.
然后我创建了一个int
变量并将其结果转换为void
丢弃它:
如果将任何其他类型的表达式计算为void表达式,则会丢弃其值或指示符.(ISO/IEC 9899:201x,6.3.2.2无效)
我试图用void
演员调用我的函数,但是我的编译器给了我(Clang 10.0):
error: too many arguments to function call, expected 0, have 1
所以void
在原型中没有任何意义,而不是类型void
.
但是,然后,我创建了一个指针void
,取消引用它,并将" 结果 " 分配给我的int
变量.我收到了" 不兼容类型 "错误.这意味着void
这里确实存在类型.
extern void a; // Why is this authorised ???
void foo(void); // This function takes no argument. Not the 'void' type.
int main(void)
{
int a …
Run Code Online (Sandbox Code Playgroud) 在研究Linux内部和内存管理时,我偶然发现Linux使用的分段分页模型。
如果我错了,请纠正我,但是Linux(保护模式)确实使用分页将线性虚拟地址空间映射到物理地址空间。由页面组成的线性地址空间,对于进程平面内存模型分为四个部分,即:
__KERNEL_CS
);__KERNEL_DS
);__USER_CS
);__USER_DS
);存在第五个内存段,称为Null段,但未使用。
这些段的CPL(当前特权级别)为0(主管)或3(用户区域)。
为简单起见,我将集中讨论32位内存映射,其中4GiB可寻址空间,3GiB用于用户空间进程空间(以绿色显示),1GiB用于主管内核空间(以红色显示):
因此红色的部分由两个部分__KERNEL_CS
和组成,__KERNEL_DS
绿色的部分由两个部分__USER_CS
和组成__USER_DS
。
这些段彼此重叠。分页将用于用户空间和内核隔离。
但是,从Wikipedia 此处提取:
daccess-ods.un.org daccess-ods.un.org许多32位操作系统都通过将所有段的基数都设置为0来模拟平面存储器模型,以使分段对程序无关。
在这里查看GDT的linux内核代码:
[GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff),
[GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff),
[GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc093, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER32_CS] = GDT_ENTRY_INIT(0xc0fb, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xa0fb, 0, 0xfffff),
Run Code Online (Sandbox Code Playgroud)
正如Peter所指出的,每个段都从0开始,但是那些标志分别是0xc09b
,0xa09b
等等?我倾向于认为它们是段选择器,如果不是,那么如果它们的寻址空间都从0开始,我将如何从内核段访问userland段?
不使用细分。仅使用分页。段的seg_base
地址设置为0,将其空间扩展到0xFFFFF
,从而提供完整的线性地址空间。这意味着逻辑地址与线性地址没有区别。
另外,由于所有段彼此重叠,提供内存保护(即内存分离)的是分页单元吗?
分页提供保护,而不是分段。内核将检查线性地址空间,并根据边界(通常称为 …
我注意到Clang的这个警告:
warning: performing pointer arithmetic on a null pointer
has undefined behavior [-Wnull-pointer-arithmetic]
Run Code Online (Sandbox Code Playgroud)
详细信息,正是此代码触发此警告:
uint8_t *end = ((uint8_t*)0) + sizeof(uint8_t) * count;
Run Code Online (Sandbox Code Playgroud)
为什么在从不同于零的整数获得的非空指针上执行相同操作时禁止对空指针进行算术运算不会触发任何警告?
更重要的是,C标准是否明确禁止空指针算法?