在另一个问题的热门评论主题之后,我开始讨论C99标准中有关C数组的内容和内容.
基本上,当我定义一个2D数组时int a[5][5],标准C99是否保证它将是一个连续的整数块,我可以将它投射到(int *)a并确保我将拥有一个25个整数的有效一维数组.
正如我理解标准的那样,上面的属性隐含在sizeof定义和指针算术中,但其他人似乎不同意并且说铸造到(int*)上面的结构给出了一个未定义的行为(即使他们同意所有现有的实现实际分配连续的价值观).
更具体地说,如果我们认为一种实现可以检测数组检查所有维度的数组边界并在访问1D数组时返回某种错误,或者不能正确访问第1行以上的元素.这样的实施可以是标准的编制吗?在这种情况下,C99标准的哪些部分是相关的.
我有一个关于指针差异和结果类型的问题ptrdiff_t.
C99§6.5.6(9)说:
当减去两个指针时,两个指针都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素; 结果是两个数组元素的下标的差异.结果的大小是实现定义的,其类型(有符号整数类型)
ptrdiff_t在头文件中定义.如果结果在该类型的对象中无法表示,则行为未定义.换句话说,如果表达式P和Q分别指向数组对象的第i和第j个元素,则表达式(P) - (Q)具有值i-j,条件是该值适合于对象类型ptrdiff_t.
§7.18.3(2)要求ptrdiff_t的范围至少为[-65535,+ 65535]
我感兴趣的是如果结果太大则未定义的行为.我无法在标准版本中找到任何与签名版本size_t或类似内容相同的范围.所以,现在我的问题是:符合标准的实现是否可以生成ptrdiff_t带符号的16位类型但size_t64位?[编辑:正如Guntram Blohm指出的那样,16位签名最多为32767,所以我的例子显然不符合]据我所知,我不能在严格符合代码甚至超过65535个元素的数组上做任何指针减法如果实现支持比这大得多的对象.此外,该程序甚至可能崩溃.
理由(V5.10)§6.5.6说:
重要的是要对这个类型[
ptrdiff_t]进行签名,以便在处理同一数组中的指针时获得适当的代数排序.但是,指针差异的大小可以与可以声明的最大对象的大小一样大; 由于这是一个无符号类型,两个指针之间的差异可能会导致某些实现溢出.
这可以解释为什么不需要指定每个指针差异(对同一个数组的元素),但它不能解释为什么PTRDIFF_MAX至少没有限制SIZE_MAX/2(使用整数除法).
为了说明,假设T是任何对象类型和编译时未知n的对象size_t.我想为n对象分配空间T,我想用指定范围内的地址进行指针减法.
size_t half = sizeof(T)>1 ? 1 : 2; // (*)
if( SIZE_MAX/half/sizeof(T)<n ) /* do some error handling */;
size_t size = n * sizeof(T);
T *foo = malloc(size);
if(!foo) /* ... */; …Run Code Online (Sandbox Code Playgroud) linux内核(和其他地方)中常用的宏container_of是(基本上)定义如下:
#define container_of(ptr, type, member) (((type) *)((char *)(ptr) - offsetof((type), (member))))
Run Code Online (Sandbox Code Playgroud)
在给定指向其中一个成员的指针的情况下,它基本上允许恢复"父"结构:
struct foo {
char ch;
int bar;
};
...
struct foo f = ...
int *ptr = &f.bar; // 'ptr' points to the 'bar' member of 'struct foo' inside 'f'
struct foo *g = container_of(ptr, struct foo, bar);
// now, 'g' should point to 'f', i.e. 'g == &f'
Run Code Online (Sandbox Code Playgroud)
但是,并不完全清楚其中包含的减法是否container_of被视为未定义的行为.
一方面,因为barinside struct foo只是一个整数,所以只*ptr应该是有效的(以及ptr + 1).因此,container_of有效地产生一个表达式 …