【问题标题】:Pointer arithmetic of unspecified type with known size已知大小的未指定类型的指针算法
【发布时间】:2020-04-07 09:00:19
【问题描述】:
void * ptr = NULL; // array of unspecified 13-byte type

for (int i = 0; i < 10; i++) {
    printf("%i ", ((char (*) [13]) ptr) + i);
}

putchar('\n');

输出:

0 13 26 39 52 65 78 91 104 117

所以这行得通(至少在 GCC 上),但是我如何声明一个 (char (*) [13] 类型的变量,这样我就不必在每次我想对该指针执行指针算术时都进行这种转换?

这种行为是否可移植?

【问题讨论】:

  • 首先,您的程序有一个未定义的行为,因为无效指针上的指针算术是 UB。其次,你“对象”的大小是13,是一个大小为13的char数组,即char x[13];
  • "%i" 用于int,而不是指针。最好使用printf("%p ", (void*) some_object_pointer_expression);
  • 我知道,这只是为了提高可读性,%p 的输出将是这样的:(nil) 0xd 0x1a 0x27 0x34 0x41 0x4e 0x5b 0x68 0x75 当然,在我的实际代码中我永远不会这样做。

标签: c void-pointers pointer-arithmetic


【解决方案1】:

已知大小的未指定类型的指针算法

这种行为是否可移植?

严格来说:也许:

需要深入了解C17 6.3.2.3 Pointers

下面的内容依赖于&amp;fred[0] 的值能够毫无问题地转换为ptr。许多实现将毫无问题地处理这个问题,但ptr 是指向数组的指针,而不是指向char 的指针。如果ptr 是指向char 的指针,没问题。

我认为因为ptr 和fred 都有相同的对齐要求 - 代码没问题。

typedef something unspecified_13_byte_type;
assert(sizeof(unspecified_13_byte_type) == 13);

#define N
unspecified_13_byte_type fred[N];

char (*ptr)[13] = (void *) fred;
for (int i = 0; i < N; i++) {
  // printf("%i ", ((char (*) [13]) ptr) + i);
  printf("%p ", (void *)  (((char (*) [13]) ptr) + i));
}
puts("");

更深一点:如果something 是一些struct,那么ptr 和&amp;fred[0] 不需要是相同的编码,甚至不需要相同的大小——即使它们可以很好地相互转换并且相互等同. struct 指针和 char * 的差异可能比通常预期的要大 - 即使这种差异在今天非常罕见。

【讨论】:

    【解决方案2】:

    你的意思好像是这样的

    char s[10][13];
    //…
    void * ptr = s;
    

    用作初始化器的数组s被隐式转换为指向其第一个char ( * )[13]类型元素的指针。

    或者你可以写

    char s[10][13];
    //…
    char ( *tmp )[13] = s;
    void * ptr = tmp;
    

    这是一个显示指针算法的演示程序

    #include <stdio.h>
    
    int main(void) 
    {
        enum { N = 13 };
        char s[][N] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
        const size_t M = sizeof( s ) / sizeof( *s );
    
        void * ptr = s; 
    
        for ( size_t i = 0; i < M; i++ )
        {
            printf( "%s ", ( ( char ( * )[N] )ptr )[i] );
            // or
            // printf( "%s ", *( ( char ( * )[N] )ptr + i ) );
        }
    
        putchar( '\n' );
    
        return 0;
    }
    

    它的输出是

    A B C D E F G H I J 
    

    【讨论】:

    • 这绝对不是我想要做的。我无意取消对指针的引用。我只需要返回正确的地址 (ptr + i * size_of_type) 我只想知道是否可以将其转换为 char (*) [size_of_type] 以利用指针算术语法糖。
    • @Salt 有什么问题?演示程序显示了指针算法。如果您不想取消引用指针,那么问题出在哪里?
    • @Salt 在我的回答中显示了如何声明相应的数组。
    • 我不想声明一个新数组。我正在尝试声明一个指针,当我执行ptr++ 之类的操作时,该指针将递增 13
    • 那我一定是瞎了眼,你在哪一行显示的?
    【解决方案3】:

    什么意思? ptr 甚至没有分配。强制转换会导致未定义的行为。

    你可以这样做

    void * ptr = malloc(10 * 13); // on heap
    

    或者这个

    void * ptr = (char [10][13]){}; // on stack (C99)
    

    【讨论】:

    • 铸造 NULL 未定义?
    • @Salt 您代码中的强制转换不是 UB,因此“强制转换将导致未定义的行为。”不符合标准。您代码中的指针添加是 UB。 Jared Pyzynski 后来的建议很好。
    猜你喜欢
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多