当前位置: 首页 > 知识库问答 >
问题:

是否可以通过成员地址访问超过结构大小,并分配足够的空间?

荣晨朗
2023-03-14

具体来说,是下面的代码,标记下面的行,好吗?

struct S{
    int a;
};

#include <stdlib.h>

int main(){
    struct S *p;
    p = malloc(sizeof(struct S) + 1000);
    // This line:
    *(&(p->a) + 1) = 0;
}

人们在这里争论,但没有人给出令人信服的解释或参考。

他们的论据基础略有不同,但本质上是一样的。

typedef struct _pack{
    int64_t c;
} pack;

int main(){
    pack *p;
    char str[9] = "aaaaaaaa"; // Input
    size_t len = offsetof(pack, c) + (strlen(str) + 1);
    p = malloc(len);
    // This line, with similar intention:
    strcpy((char*)&(p->c), str);
//                ^^^^^^^

共有3个答案

翟冷勋
2023-03-14

C 标准保证
§6.7.2.1/15:

[…]指向适当转换的结构对象的指针指向其初始成员(如果该成员是位字段,则指向其所在的单元),反之亦然。结构对象中可能有未命名的填充,但不能在其开头。

卓瀚
2023-03-14

这是未定义的行为,因为您正在将非数组(struct S中的int a)作为数组进行访问,并且超出了界限。

实现所需的正确方法是使用不带大小的数组作为最后一个结构成员:

#include <stdlib.h>

typedef struct S {
    int foo;    //avoid flexible array being the only member
    int a[];
} S;

int main(){
    S *p = malloc(sizeof(*p) + 2*sizeof(int));
    p->a[0] = 0;
    p->a[1] = 42;    //Perfectly legal.
}

习斌
2023-03-14

至少自1989年C标准化以来,其目的是允许实现检查数组访问的数组边界。

成员<代码> p-

7出于[加法运算符]的目的,指向不是数组元素的对象的指针的行为与指向长度为1的数组的第一个元素的指针相同,该数组的元素类型为对象的类型。

因此

&(p->a)

是指向< code>int的指针;但它也好像是一个指向长度为1的数组的第一个元素的指针,对象类型为< code>int。

现在6.5.6p8允许计算

将指针加法或减法到数组对象或略高于数组对象的整数类型,会产生一个指向数组对象的略高于数组的结果,并用作计算的一元*运算符的操作数(6.5.6)。

在上面的表达式中,只有一个数组,这个数组(好像)正好包含一个元素。如果<代码>

行为[…],[C11]标准对此没有要求

纸条上写着:

可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式(有或没有发出诊断消息),到终止翻译或执行(发出诊断消息)。

最常见的行为是完全忽略情况,即指针在后面引用了内存位置,但这并不意味着从标准的角度来看,其他类型的行为是不可接受的——标准允许所有可想象和不可想象的结果。

有人声称C11标准文本写得很模糊,委员会的意图应该是这确实是允许的,以前是可以的。事实并非如此。阅读委员会对 [缺陷报告 #017, 日期为 1992 年 12 月 10 日至 C89] 的答复。

问题 16

[...]

回应

对于数组数组,第47页第6.3.6款第12-40行中允许的指针算法可以通过解释object一词的用法来理解,object表示由指针类型和值直接确定的特定对象,而不是与指针相邻性相关的其他对象。因此,如果表达式超出了这些权限,则行为是未定义的。例如,以下代码具有未定义的行为:

 int a[4][5];

 a[1][7] = 0; /* undefined */ 

一些一致性实现可能选择诊断数组边界冲突,而其他实现可能选择用明显的扩展语义成功地解释这种尝试性访问。

(粗体强调我的)

没有理由不将相同的内容传递给结构的标量成员,特别是当6.5.6p7指出指向它们的指针应该被认为与指向长度为1的数组的第一个元素的指针行为相同,并且对象的类型作为其元素类型时。

如果您想要寻址连续的< code>struct,您可以始终将指向第一个成员的指针转换为指向< code>struct的指针,并改为前进:

*(int *)((S *)&(p->a) + 1) = 0;
 类似资料:
  • 在这两个示例中,通过偏移其他成员的指针来访问结构成员是否会导致未定义/未指定/实现定义的行为? C11§6.7.2.1第14段似乎表明,这应该是实施定义: 结构或联合对象的每个非位字段成员都以适合其类型的实现定义方式对齐。 后来又说: 结构对象中可能有未命名的填充,但在其开头没有。 但是,如下所示的代码似乎相当常见: 该标准似乎保证 与 和< code >( 原始应用程序正在考虑从一个结构字段到另

  • 反射值对象(reflect.Value)提供对结构体访问的方法,通过这些方法可以完成对结构体任意值的访问,如下表所示。 反射值对象的成员访问方法 方  法 备  注 Field(i int) Value 根据索引,返回索引对应的结构体成员字段的反射值对象。当值不是结构体或索引超界时发生宕机 NumField() int 返回结构体成员字段数量。当值不是结构体或索引超界时发生宕机 FieldByNa

  • 问题内容: 我有一个带有成员A,B,C字符串的struct v。使用反射,我可以获取字段名称及其值: 因为我有名称,并且可以获取值OUT,所以可以为这些字段分配新值吗?我想做的基本上是: 但这显然行不通。如果仅知道字段名称,是否可以将值分配给结构? 在实践中,我试图将来自a的值分配给结构中的相应字段,其中结构和映射定义可能会随着时间的变化而扩展,并且映射可能包含比该结构更多或更少的值。我已经考虑过

  • 我试图理解在将值存储到结构或联合的成员中时,类型双关是如何工作的。 标准N1570指定 当值存储在结构或联合类型的对象(包括成员对象)中时,与任何填充字节相对应的对象表示的字节采用未指定的值。 所以我把它解释为如果我们有一个对象要存储到一个成员中,这样对象的大小等于,与填充相关的字节将具有未指定的值(即使我们定义了原始对象中的字节)。这是一个例子: 在我的机器。 打印的行为是否为这样的程序定义得很

  • 任何关于直接远程访问(不使用ssh->kubectl代理来避免在Kubernetes节点上进行用户管理)的详细实用指南都将不胜感激。:) 更新: 只是想说清楚点。这是在前提部署中的裸机(没有GCE、AWZ、Azure或任何其他),而且有些环境将完全脱机(这将增加获得安装包的额外问题)。 因此,当我在Kubernetes上设置入口时,我无法创建将443重定向到6443的防火墙规则。似乎唯一的选择是创

  • 问题内容: 我正在尝试跟踪和记录使用PHP 来访问我的网站的用户/访问者。PHP中IP地址跟踪的一种典型方法。 但是,我正在使用CloudFlare进行缓存等,并以CloudFlare的方式接收其IP地址: 108.162.212。-108.162.239。 在仍然使用CloudFlare的同时检索实际用户/访问者IP地址的正确方法是什么? 问题答案: 云耀斑可用的其他服务器变量是: 真正的访客I