Following yesterday's question, I experimented some more with pointers. Specifically pointers of type int (*) [n]
在昨天的问题之后,我用指针进行了一些实验。特别是int(*)[n]类型的指针
Here's some code I wrote :
这是我写的一些代码:
#include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5};
int (*p) [5] = &a;
int *q = a;
printf("\t\t\t\t\tp\t\tq\n\n");
printf("Original Pointers: \t%20d%20d", p, q);
printf("\n\n");
printf("Incremented Pointers:\t%20d%20d", p+1, q+1);
printf("\n\n");
printf("Pointer values: %20d%20d", *p, *q);
printf("\n\n");
return 0;
}
And here is it's output:
这是它的输出:
p q
Original Pointers: 132021776 132021776
Incremented Pointers: 132021796 132021780
Pointer values: 132021776 1
-
The pointer
p
, jumps by 20 when incremented. Is this because it's a pointer of typeint(*)[5]
, and therefore jumps bysizeof(int) * number of columns
in the array ?指针p在递增时跳过20。这是因为它是int(*)[5]类型的指针,因此跳过sizeof(int)*数组中的列数?
-
Both
p
andq
have the same values (but different types), and yet when using the indirection operator withp
, I don't get the value at the first cell of the array, instead I get the value ofp
itself printed out. Why is this?p和q都有相同的值(但是类型不同),但是当使用带p的间接运算符时,我没有得到数组的第一个单元格的值,而是得到p本身的值打印出来。为什么是这样?
-
When I use
int *p = &a
, it throws up a warning (because of different types of pointers, but later when I use the indirection operator with p, I can get it to print the value of the first cell in the array. Is this because when I assign&a
top
, it CONVERTS the type of&arr
(which isint ( * ) [5]
) to the typeint *
, and then assigns top
?当我使用int * p =&a时,它会抛出一个警告(因为不同类型的指针,但后来当我使用p的间接运算符时,我可以让它打印数组中第一个单元格的值。是这是因为当我分配&a到p时,它将&arr(int(*)[5])的类型转换为int *类型,然后分配给p?
3 个解决方案
#1
4
The pointer p, jumps by 20 when incremented. Is this because it's a pointer of type int(*)[5], and therefore jumps by sizeof(int) * number of columns in the array ?
指针p在递增时跳过20。这是因为它是int(*)[5]类型的指针,因此跳过sizeof(int)*数组中的列数?
Yes.
Both p and q have the same values (but different types), and yet when using the indirection operator with p, I don't get the value at the first cell of the array, instead I get the value OF p printed out. Why is this ?
p和q都具有相同的值(但是类型不同),但是当使用p的间接运算符时,我没有得到数组的第一个单元格的值,而是得到打印出的值OF p。为什么是这样 ?
p
points to an array, so with *p
, you get to this array. Evaluating an array without indexing gets you a pointer to its first element. Your *p
is evaluated as type int *
here.
p指向一个数组,所以使用* p,你可以得到这个数组。在没有索引的情况下评估数组会获得指向其第一个元素的指针。您的* p在此处被评估为int *类型。
On a side note, *
is commonly called the dereference operator. Using different nomenclature can be confusing to others.
另外,*通常称为解除引用运算符。使用不同的命名法可能会让其他人感到困惑。
When I use int *p = &a, it throws up a warning (because of different types of pointers, but later when I use the indirection operator with p, I can get it to print the value of the first cell in the array. Is this because when I assign &a to p, it CONVERTS the type of &arr (which is int ( * ) [5]) to the type int *, and then assigns to p ?
当我使用int * p =&a时,它会抛出一个警告(因为不同类型的指针,但后来当我使用p的间接运算符时,我可以让它打印数组中第一个单元格的值。是这是因为当我分配&a到p时,它将&arr(int(*)[5])的类型转换为int *类型,然后分配给p?
I had an answer here misreading this as something like int *q = (int *)p
which would create an aliasing pointer and likely undefined behavior, so leaving this little hint here just in case someone would get this idea. But what you suggest in this question is just an invalid assignment. Lundin's answer has a complete explanation of that.
我有一个答案在这里误读这个像int * q =(int *)p这样会产生一个别名指针和可能未定义的行为,所以留下这个小提示,以防万一有人会得到这个想法。但是你在这个问题中建议的只是一个无效的任务。伦丁的答案对此有一个完整的解释。
There are more problems in your code, your printing of pointers should look like:
您的代码中存在更多问题,您的指针打印应如下所示:
printf("Original Pointers: \t%20p%20p", (void *)p, (void *)q);
Pointers can be bigger than int
. The need to cast to void *
is because printf()
is a variadic function. If it was declared to take a void *
, the conversion would be implicit, but as it's not, you have to do it yourself.
指针可以大于int。转换为void *的需要是因为printf()是一个可变函数。如果它被声明为取消*,则转换将是隐含的,但事实并非如此,您必须自己完成。
#2
3
In addition to the answer by Felix:
除了Felix的回答:
When I use
int *p = &a
, it throws up a warning当我使用int * p =&a时,它会抛出一个警告
This is because this code is not valid C. It is a so-called constraint violation (roughly means severe language violation). So the compiler is required to give a diagnostic message. A better compiler would give an error, not a warning.
这是因为此代码无效C.这是所谓的约束违规(大致意味着严重的语言违规)。因此编译器需要提供诊断消息。更好的编译器会给出错误,而不是警告。
No pointer conversion takes place. Pointer conversions are not done implicitly in C unless one of the operands is void*
.
没有指针转换发生。除非其中一个操作数为void *,否则指针转换不会在C中隐式完成。
The reason it isn't valid C is because the expression is not a valid form of simple assignment. Valid forms are listed in C11 6.5.16.1:
它无效C的原因是因为表达式不是简单赋值的有效形式。有效表格列于C11 6.5.16.1中:
6.5.16.1 Simple assignment
Constraints6.5.16.1简单赋值约束
One of the following shall hold:
以下其中一项应持有:
— the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
- 左操作数具有原子,限定或非限定算术类型,右边具有算术类型;
Not the case here, both operands are pointers.
不是这里的情况,两个操作数都是指针。
— the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
- 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;
Not the case here.
不是这里的情况。
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左边指向的类型具有右边指出的所有类型的限定词;
The left operand is a (unqualified) pointer type. But the right operand is not a compatible type. So this is condition is not met.
左操作数是(不合格的)指针类型。但右操作数不是兼容类型。所以这是条件不符合。
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)一个操作数是指向对象类型的指针,另一个操作数是指向限定或非限定版本的指针void的类型,左边指向的类型具有右边指向的所有类型的限定符;
No, there is no void
pointers here.
不,这里没有虚拟指针。
— the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
- 左操作数是原子,限定或非限定指针,右边是空指针常量;要么
No null pointer constants either.
没有空指针常量。
— the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.
- 左操作数具有原型,限定或非限定类型的_Bool,右边是指针。
And no bools here either.
这里也没有布尔。
#3
3
Your code causes undefined behavior, so none of the output can be validated.
您的代码导致未定义的行为,因此无法验证任何输出。
First, some generic information :
首先,一些通用信息:
As per the standard, using mismatched type of arguments with format specifiers causes UB. To print a pointer with printf()
, you must use %p
format specifier and cast the corresponding argument to void *
.
根据标准,使用格式说明符的不匹配类型的参数会导致UB。要使用printf()打印指针,必须使用%p格式说明符并将相应的参数转换为void *。
Related, quoting C11
, chapter §7.21.6.1/P8
相关,引用C11,章节§7.21.6.1/ P8
p
The argument shall be a pointer to
void
. [...]参数应该是指向void的指针。 [...]
and P9,
[....] If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
[....]如果任何参数不是相应转换规范的正确类型,则行为未定义。
Since printf()
is a variadic function and no default argument promotion takes place, the cast to void *
is necessary.
由于printf()是一个可变参数函数,并且不会发生默认参数提升,因此需要转换为void *。
Now, coming to the more direct questions.
现在,来看更直接的问题。
§1. The pointer
p
, jumps by 20 when incremented. [...]§1。指针p在递增时跳过20。 [...]
You're right, check the data type. a pointer with type as int (*) [5]
would be incremented / decremented by sizeof(int [5])
, the pointing type, based on your platform. Pointer arithmatic honors the datd type
你是对的,检查数据类型。类型为int(*)[5]的指针将由sizeof(int [5])递增/递减,指向类型,基于您的平台。指针算术尊重datd类型
§2. Both
p
andq
have the same values (but different types), and yet when using the indirection operator with `p [....]§2。 p和q都有相同的值(但是类型不同),但是当使用带有`p [....]的间接运算符时
Please note the type there. p
is of type int (*) [5]
, so *p
is of type int [5]
. That's it. All you should have is an array as a product of the dereference. (But read on.....)
请注意那里的类型。 p的类型为int(*)[5],因此* p的类型为int [5]。而已。您应该拥有的只是一个数组作为解除引用的产物。 (但请继续阅读.....)
Now, while passing an array type as a function argument, it decays to the pointer to the first element to the array, hence is is analogous to int *
, a pointer. So, ultimately, you'll print a pointer value.
现在,在将数组类型作为函数参数传递时,它会衰减到指向数组的第一个元素的指针,因此类似于int *,一个指针。因此,最终,您将打印指针值。
§3. When I use
int *p = &a
, it throws up a warning [....]§3。当我使用int * p =&a时,它会抛出一个警告[....]
Wait. Stop. That is a constraint violation. Strictly speaking, it's invalid C code. The types int *
and int (*) [5]
are not compatible and there exist no cast (implicit or explicit) which makes this a valid expression. Don't do it, use proper types.
等待。停止。这是违反约束的行为。严格来说,它是无效的C代码。 int *和int(*)[5]类型不兼容,并且不存在使其成为有效表达式的强制转换(隐式或显式)。不要这样做,使用适当的类型。
#1
4
The pointer p, jumps by 20 when incremented. Is this because it's a pointer of type int(*)[5], and therefore jumps by sizeof(int) * number of columns in the array ?
指针p在递增时跳过20。这是因为它是int(*)[5]类型的指针,因此跳过sizeof(int)*数组中的列数?
Yes.
Both p and q have the same values (but different types), and yet when using the indirection operator with p, I don't get the value at the first cell of the array, instead I get the value OF p printed out. Why is this ?
p和q都具有相同的值(但是类型不同),但是当使用p的间接运算符时,我没有得到数组的第一个单元格的值,而是得到打印出的值OF p。为什么是这样 ?
p
points to an array, so with *p
, you get to this array. Evaluating an array without indexing gets you a pointer to its first element. Your *p
is evaluated as type int *
here.
p指向一个数组,所以使用* p,你可以得到这个数组。在没有索引的情况下评估数组会获得指向其第一个元素的指针。您的* p在此处被评估为int *类型。
On a side note, *
is commonly called the dereference operator. Using different nomenclature can be confusing to others.
另外,*通常称为解除引用运算符。使用不同的命名法可能会让其他人感到困惑。
When I use int *p = &a, it throws up a warning (because of different types of pointers, but later when I use the indirection operator with p, I can get it to print the value of the first cell in the array. Is this because when I assign &a to p, it CONVERTS the type of &arr (which is int ( * ) [5]) to the type int *, and then assigns to p ?
当我使用int * p =&a时,它会抛出一个警告(因为不同类型的指针,但后来当我使用p的间接运算符时,我可以让它打印数组中第一个单元格的值。是这是因为当我分配&a到p时,它将&arr(int(*)[5])的类型转换为int *类型,然后分配给p?
I had an answer here misreading this as something like int *q = (int *)p
which would create an aliasing pointer and likely undefined behavior, so leaving this little hint here just in case someone would get this idea. But what you suggest in this question is just an invalid assignment. Lundin's answer has a complete explanation of that.
我有一个答案在这里误读这个像int * q =(int *)p这样会产生一个别名指针和可能未定义的行为,所以留下这个小提示,以防万一有人会得到这个想法。但是你在这个问题中建议的只是一个无效的任务。伦丁的答案对此有一个完整的解释。
There are more problems in your code, your printing of pointers should look like:
您的代码中存在更多问题,您的指针打印应如下所示:
printf("Original Pointers: \t%20p%20p", (void *)p, (void *)q);
Pointers can be bigger than int
. The need to cast to void *
is because printf()
is a variadic function. If it was declared to take a void *
, the conversion would be implicit, but as it's not, you have to do it yourself.
指针可以大于int。转换为void *的需要是因为printf()是一个可变函数。如果它被声明为取消*,则转换将是隐含的,但事实并非如此,您必须自己完成。
#2
3
In addition to the answer by Felix:
除了Felix的回答:
When I use
int *p = &a
, it throws up a warning当我使用int * p =&a时,它会抛出一个警告
This is because this code is not valid C. It is a so-called constraint violation (roughly means severe language violation). So the compiler is required to give a diagnostic message. A better compiler would give an error, not a warning.
这是因为此代码无效C.这是所谓的约束违规(大致意味着严重的语言违规)。因此编译器需要提供诊断消息。更好的编译器会给出错误,而不是警告。
No pointer conversion takes place. Pointer conversions are not done implicitly in C unless one of the operands is void*
.
没有指针转换发生。除非其中一个操作数为void *,否则指针转换不会在C中隐式完成。
The reason it isn't valid C is because the expression is not a valid form of simple assignment. Valid forms are listed in C11 6.5.16.1:
它无效C的原因是因为表达式不是简单赋值的有效形式。有效表格列于C11 6.5.16.1中:
6.5.16.1 Simple assignment
Constraints6.5.16.1简单赋值约束
One of the following shall hold:
以下其中一项应持有:
— the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
- 左操作数具有原子,限定或非限定算术类型,右边具有算术类型;
Not the case here, both operands are pointers.
不是这里的情况,两个操作数都是指针。
— the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
- 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;
Not the case here.
不是这里的情况。
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左边指向的类型具有右边指出的所有类型的限定词;
The left operand is a (unqualified) pointer type. But the right operand is not a compatible type. So this is condition is not met.
左操作数是(不合格的)指针类型。但右操作数不是兼容类型。所以这是条件不符合。
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)一个操作数是指向对象类型的指针,另一个操作数是指向限定或非限定版本的指针void的类型,左边指向的类型具有右边指向的所有类型的限定符;
No, there is no void
pointers here.
不,这里没有虚拟指针。
— the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
- 左操作数是原子,限定或非限定指针,右边是空指针常量;要么
No null pointer constants either.
没有空指针常量。
— the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.
- 左操作数具有原型,限定或非限定类型的_Bool,右边是指针。
And no bools here either.
这里也没有布尔。
#3
3
Your code causes undefined behavior, so none of the output can be validated.
您的代码导致未定义的行为,因此无法验证任何输出。
First, some generic information :
首先,一些通用信息:
As per the standard, using mismatched type of arguments with format specifiers causes UB. To print a pointer with printf()
, you must use %p
format specifier and cast the corresponding argument to void *
.
根据标准,使用格式说明符的不匹配类型的参数会导致UB。要使用printf()打印指针,必须使用%p格式说明符并将相应的参数转换为void *。
Related, quoting C11
, chapter §7.21.6.1/P8
相关,引用C11,章节§7.21.6.1/ P8
p
The argument shall be a pointer to
void
. [...]参数应该是指向void的指针。 [...]
and P9,
[....] If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
[....]如果任何参数不是相应转换规范的正确类型,则行为未定义。
Since printf()
is a variadic function and no default argument promotion takes place, the cast to void *
is necessary.
由于printf()是一个可变参数函数,并且不会发生默认参数提升,因此需要转换为void *。
Now, coming to the more direct questions.
现在,来看更直接的问题。
§1. The pointer
p
, jumps by 20 when incremented. [...]§1。指针p在递增时跳过20。 [...]
You're right, check the data type. a pointer with type as int (*) [5]
would be incremented / decremented by sizeof(int [5])
, the pointing type, based on your platform. Pointer arithmatic honors the datd type
你是对的,检查数据类型。类型为int(*)[5]的指针将由sizeof(int [5])递增/递减,指向类型,基于您的平台。指针算术尊重datd类型
§2. Both
p
andq
have the same values (but different types), and yet when using the indirection operator with `p [....]§2。 p和q都有相同的值(但是类型不同),但是当使用带有`p [....]的间接运算符时
Please note the type there. p
is of type int (*) [5]
, so *p
is of type int [5]
. That's it. All you should have is an array as a product of the dereference. (But read on.....)
请注意那里的类型。 p的类型为int(*)[5],因此* p的类型为int [5]。而已。您应该拥有的只是一个数组作为解除引用的产物。 (但请继续阅读.....)
Now, while passing an array type as a function argument, it decays to the pointer to the first element to the array, hence is is analogous to int *
, a pointer. So, ultimately, you'll print a pointer value.
现在,在将数组类型作为函数参数传递时,它会衰减到指向数组的第一个元素的指针,因此类似于int *,一个指针。因此,最终,您将打印指针值。
§3. When I use
int *p = &a
, it throws up a warning [....]§3。当我使用int * p =&a时,它会抛出一个警告[....]
Wait. Stop. That is a constraint violation. Strictly speaking, it's invalid C code. The types int *
and int (*) [5]
are not compatible and there exist no cast (implicit or explicit) which makes this a valid expression. Don't do it, use proper types.
等待。停止。这是违反约束的行为。严格来说,它是无效的C代码。 int *和int(*)[5]类型不兼容,并且不存在使其成为有效表达式的强制转换(隐式或显式)。不要这样做,使用适当的类型。