小编Dro*_* K.的帖子

使用%p打印空指针是未定义的行为?

使用%p转换说明符打印空指针是不确定的行为?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个问题适用于C标准,而不适用于C实现.

c c99 undefined-behavior language-lawyer c11

92
推荐指数
2
解决办法
4424
查看次数

灵活的数组成员会导致不确定的行为吗?

  1. 通过在结构类型中使用灵活的数组成员(FAM),我们是否将程序暴露给未定义行为的可能性?

  2. 程序是否可以使用FAM并且仍然是严格符合的程序?

  3. 灵活数组成员的偏移量是否需要位于结构的末尾?

这些问题适用于C99 (TC3)C11 (TC1).

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

int main(void) {
    struct s {
        size_t len;
        char pad;
        int array[];
    };

    struct s *s = malloc(sizeof *s + sizeof *s->array);

    printf("sizeof *s: %zu\n", sizeof *s);
    printf("offsetof(struct s, array): %zu\n", offsetof(struct s, array));

    s->array[0] = 0;
    s->len = 1;

    printf("%d\n", s->array[0]);

    free(s);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

sizeof *s: 16
offsetof(struct s, array): 12
0
Run Code Online (Sandbox Code Playgroud)

c undefined-behavior language-lawyer flexible-array-member

34
推荐指数
3
解决办法
1361
查看次数

所有指针都是从指向结构类型的指针派生的吗?

问题

是否所有指针都是从指向结构类型的指针派生的问题都不容易回答.我发现这是一个重要问题,主要有以下两个原因.

A.缺少指向"任何"不完整或对象类型的指针,对方便的函数接口施加了限制,例如:

int allocate(ANY_TYPE  **p,
             size_t    s);

int main(void)
{
    int *p;
    int r = allocate(&p, sizeof *p);
}
Run Code Online (Sandbox Code Playgroud)

[ 完整代码示例 ]

指向"任何"不完整或对象类型的现有指针明确描述为:

C99/ C11 §6.3.2.3 p1:

指向void的指针可以转换为指向任何不完整或对象类型的指针.[...]

从指向"任何"不完整或对象类型的现有指针派生的指针,指向void的指针,严格地是指向void的指针,并且不需要使用从指向"任何"不完整的指针派生的指针进行转换或对象类型.


B.程序员根据他们对特定实现的经验,使用基于不需要的假设的约定,有意识地或不知不觉地与指针的泛化相关,这种情况并不少见.假设,例如可转换,可表示为整数或共享公共属性:对象大小,表示或对齐.


标准的话

根据C99 §6.2.5 p27/ C11 §6.2.5 p28:

[...]所有指向结构类型的指针应具有相同的表示和对齐要求.[...]

其次是C99 TC3 Footnote 39/ C11 Footnote 48:

相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员.

虽然标准没有说:"指向结构类型的指针"并且选择了以下单词:"所有指向结构类型的指针",但它没有明确指定它是否适用于这种指针的递归推导.在标准中提到指针的特殊属性的其他情况下,它没有明确指定或提及递归指针派生,这意味着"类型派生"适用,或者它没有 - 但它没有明确提到.

虽然在引用类型时使用"所有指针"的措辞使用两次,(对于结构和联合类型),而不是更明确的措辞:在整个标准中使用的"指针",我们不能总结它是否适用于这种指针的递归推导.

c struct pointers c99 strict-aliasing

28
推荐指数
1
解决办法
812
查看次数

如何在符合C标准的情况下一般地转换指针的地址

通常使用隐式函数返回void*转换为分配指定指针,就像malloc()一样:

void *malloc(size_t size);
int *pi = malloc(sizeof *pi);
Run Code Online (Sandbox Code Playgroud)

我希望在传递目标指针的地址时执行相同的赋值,而不从函数内部(不在其体内,也不在参数中)显式地转换其类型.

以下代码似乎实现了这一点.

  1. 我想知道代码是否完全符合(任何)C标准.
  2. 如果它不符合,我想知道是否有可能在符合(任何)C标准的情况下达到我的要求.

.

#include <stdio.h>
#include <stdlib.h>

int allocate_memory(void *p, size_t s) {
    void *pv;
    if ( ( pv = malloc(s) ) == NULL ) {
        fprintf(stderr, "Error: malloc();");
        return -1;
    }
    printf("pv: %p;\n", pv);
    *((void **) p) = pv;
    return 0;
}

int main(void) {
    int *pi = NULL;
    allocate_memory(&pi, sizeof *pi);
    printf("pi: %p;\n", (void *) pi);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果:

pv: 0x800103a8;
pi: 0x800103a8;
Run Code Online (Sandbox Code Playgroud)

c pointers strict-aliasing

11
推荐指数
2
解决办法
554
查看次数

指向结构的指针是否暗示了可互换性?

根据两者C99 §6.2.5p27C11 §6.2.5p28:

所有指向结构类型的指针都应具有相同的表示和对齐要求.

用脚注(#39#48分别):

相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员.

(注意,C89 §3.1.2.5没有指定关于结构的指针)

-

众所周知,指向void的指针,例如C11 §6.3.2.3p1:

A pointer to void may be converted to or from a pointer to any object type.
Run Code Online (Sandbox Code Playgroud)

并不意味着指向void的指针与指向void的指针相同,并不意味着与指向数据对象的指针的其他指针相同.

(如果我过于宽泛地使用"隐含相同"一词,我会道歉,它只能在指定的上下文中使用)

以下是演示指向struct的指针的通用指针的代码示例:

#include <stdio.h>
#include <stdlib.h>

int allocate_struct(void *p, size_t s) {
    struct generic { char placeholder; };    

    if ( ( *(struct generic **) p = malloc(s) ) == NULL ) {
        fprintf(stderr, "Error: malloc();");
        return -1;
    }

    printf("p:  %p;\n", …
Run Code Online (Sandbox Code Playgroud)

c struct pointers strict-aliasing language-lawyer

11
推荐指数
1
解决办法
273
查看次数