我正在用Ted Jensen的"关于C〃中的指针和数组的一个定理;手册这是一个非常棘手的争论。
问题1。我已经来到了程序3.1,希望得到澄清。这就是程序:
#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void) {
char *pA;
char *pB;
puts(strA);
pA = strA;
puts(pA);
pB = strB;
putchar('n');
while(*pA != ' ')
{
*pB++ = *pA++;
}
*pB = ' ';
puts(strB);
return 0;
}
关于线pA = strA;
;然后我们将指针pA指向strA。即通过赋值语句将strA[0]的地址复制到我们的变量pA〃中;,这是我不明白的。
要通过赋值声明将strA[0]
的地址复制到我们的变量pA中,我们不应该写pA = & strA
吗?
问题2。表达式c = * ++ p;
增加了p
的值或p
的地址?
间接运算符(*
)是否不指示指向变量的值?
要通过赋值声明将strA[0]的地址复制到我们的变量pA中,我们不应该写
pA = & strA
吗?
&strA
是strA
的地址。CCD_ 11是CCD_ 12的地址。它们是"相同的",因为它们指向内存中的同一位置,但它们有不同的类型,如果我们在pA
的类型是指向strA
元素类型的指针时编写pA = &strA
,编译器会抱怨。
当我们写入pA = strA
时,数组strA
会自动转换为指向其第一个元素的指针,因此pA = strA
等效于pA = &strA[0]
。
问题2:表达式
c = * ++ p;
会增加p的值还是p的地址?
C语法将其组织为c = *(++p);
,而++p
增加了p
的值。如果p
是一个指针,它会增加该指针的值。*
运算符使用增加的值。
谈到指针的地址时要小心。指针的值是一个地址,但不应该说这是指针的地址。指针本身就是内存中的一个对象,因此它在内存中有一个存储其值的地址。存储指针的地址与存储在其中的地址不同。"指针的地址"是存储指针的位置,而不是指针的值。
这只是因为strA是一个指针,它是char数组。一个数组的";值";实际上只不过是数组的第一个元素的地址。
当您分配基元数据类型(int、char等)的地址时,应该按照您描述的方式进行分配。
int x;
int *pA;
pA = &x;
当您分配指针数据类型(数组等)的地址时,您应该在不带&运算符,因为值本身就是地址。
int x[10];
int* pA;
pA = x;
原始代码
#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void)
{
char *pA; /* a pointer to type character */
char *pB; /* another pointer to type character */
puts(strA); /* show string A */
pA = strA; /* point pA at string A */
puts(pA); /* show what pA is pointing to */
pB = strB; /* point pB at string B */
putchar('n'); /* move down one line on the screen */
while(*pA != ' ') /* line A (see text) */
{
*pB++ = *pA++; /* line B (see text) */
}
*pB = ' '; /* line C (see text) */
puts(strB); /* show strB on screen */
return 0;
}
在C中,当您编写:时
char strA[80];
内存已分配给您的表。这是一个例子,你可以尝试将它想象成什么样子。
[0]第一个元素 | <1]2nd元素>[2]3rd元素 | >[….]… | 第n个元素 |
---|---|---|---|
0000 | 0001 | 0002 | 0003 | n
要通过赋值声明将strA[0]的地址复制到我们的变量pA中,我们不应该写pA=&strA?
数组很奇怪,行为与其他类型不同。
表达式CCD_;衰变";从类型";CCD_ 27〃的N元素阵列;至";指向CCD_ 28的指针";,并且表达式的值是第一个元素的地址。这个";衰变;当数组表达式是sizeof
或一元&
运算符的操作数时不会发生,因此表达式&strA
具有类型";指向CCD_ 32的N元素阵列的指针";,或者与char *
不同的char (*)[N]
。地址值相同(数组的地址与其第一个元素的地址相同),但类型不同。
假设声明
char str[N]; // for some size N
char *p;
当你写
p = str; // char * = char *
表达式CCD_ 35具有类型";CCD_ 36〃的N元素阵列;;然而,由于str
不是sizeof
或一元&
运算符的操作数;衰变";以键入CCD_ 40并计算为第一个元素的地址。这相当于写
p = &str[0]; // char * = char *
这是有原因的——C源于早期的一种名为B的语言,在B中,数组类型有一个指向第一个元素的专用指针——当你在B中声明一个类似的数组时
auto a[N];
你记忆中的是
+---+
a: | | -------+
+---+ |
... |
+---+ |
| | a[0] <-+
+---+
| | a[1]
+---+
...
并且数组下标运算a[i]
被定义为*(a + i)
-给定起始地址a
,从该地址偏移i
元素并取消引用结果。
在设计C时,Ritchie希望保留B的数组行为,但他不希望保留该行为所需的显式指针。当你在类似的C中声明一个数组时
int a[N];
你在记忆中得到的是
+---+
a: | | a[0]
+---+
| | a[1]
+---+
...
编译器不是为指向第一个元素的显式指针留出空间,而是用指向a[0]
的指针替换表达式a
的任何出现。所以a[i]
仍然被定义为*(a + i)
。不幸的是,这意味着数组失去了它们的";阵列度";在大多数情况下,大多数时候,你所处理的都是一个指针。
问题2:表达式c=*++p;增加p的值或p的地址?
这有点复杂。要按要求回答,它会增加p
的值——它会将p
设置为指向序列中的下一个对象。这可能可以用一个具体的例子来更好地解释。
假设以下声明:
char str[] = "foo";
char *p = str;
则以下情况成立:
+---+
str: |'f'| str[0] <--- p
+---+
|'o'| str[1] <--- p + 1
+---+
|'o'| str[2] <--- p + 2
+---+
| 0 | str[3] <--- p + 3
+---+
p == &str[0] // char * == char *
*p == str[0] == 'f' // char == char == char
p + 1 == &str[1]
*(p + 1) == str[1] == 'o'
p + 2 == &str[2]
*(p + 2) == str[2] == 'o'
表达式p + 1
的结果是指向str[1]
的指针,表达式p + 2
的结果是针对str[2]
的指针,等等。
c = *++p;
大致相当于写入
tmp = p + 1;
c = *tmp;
p = p + 1;
需要注意的是,对c
和p
的更新可以以任何顺序发生——它们甚至可以同时评估(交错或并行)。
在这种情况下,这意味着我们将*(p + 1)
('o'
)的值分配给c
,并将p
更新为指向str[1]
,剩下的是:
+---+
str: |'f'| str[0]
+---+
|'o'| str[1] <--- p
+---+
|'o'| str[2] <--- p + 1
+---+
| 0 | str[3] <--- p + 2
+---+
p == &str[1]
*p == str[1] == 'o'
p + 1 == &str[2]
*(p + 1) == str[2] == 'o'
p + 2 == &str[3]
*(p + 2) == str[3] == 0