打印

搞到今天都没弄懂的C语言问题...

[复制链接]
楼主: zq1987731
手机看帖
扫描二维码
随时随地手机跟帖
41
xlsbz| | 2010-2-27 11:46 | 只看该作者 回帖奖励 |倒序浏览
36# sinanjj

对地址进行取地址 等于地址本身  keil下。

设地址为ADDRESS

那么ADDRESS = &&&&&&&&&&&&&&&&&&&&ADDRESS。

这就是编译器编写的人随便弄的。

使用特权

评论回复
42
sinanjj| | 2010-2-27 13:35 | 只看该作者
gcc 测试结果如下:
程序1:

int main(void)
{
        int A[10];
        int (*p)[10];
        p = A;
        (*p)[1] = 1;
        (*p)[2] = 2;
        return 0;
}
$ gcc -o test test.c -Wall
test.c: In function ‘main’:
test.c:5: warning: assignment from incompatible pointer type
能编译成功, 正常运行,但有W。

程序2,

int main(void)
{
        int A[10];
        int (*p)[10];
        p = &A;
        (*p)[1] = 1;
        (*p)[2] = 2;
        return 0;
}
$ gcc -o test test.c -Wall
无任何警告成功。

使用特权

评论回复
43
sinanjj| | 2010-2-27 13:38 | 只看该作者
那么经我测试, 这个

p==&p
&p==&&p
&&p==&&&p

确实成立,


这个不知道是什么原因。

恐怕不是 “这就是编译器编写的人随便弄的“这么简单吧。

简单的现象总有背后的解释。哪位大侠能解释下。我google了下没有查到端倪。

使用特权

评论回复
44
yewuyi| | 2010-2-27 13:52 | 只看该作者
数组名在C中,有的时候可以认为它就是代表着数组首地址。

这还要做什么解释?

使用特权

评论回复
45
ShakaLeo| | 2010-2-27 14:55 | 只看该作者
to 43楼:
定义一个数组int A[10],A是一个常量,类型是int *,一个常量前加“&”并不是对该常量取地址。&A也是一个常量,和并且“A”在数值上是相同的,但"&A"的类型是int (*)[10],所以在42楼的第一种写**有警告。
同理,如果定义一个二维数组int B[1][10],那么B[0]是常量,类型是int *,&B[0]也是常量并且在数值上与B[0]相同,但类型是int (*)[10]。

使用特权

评论回复
46
sinanjj| | 2010-2-27 23:41 | 只看该作者
to 43楼:
定义一个数组int A[10],A是一个常量,类型是int *,一个常量前加“&”并不是对该常量取地址。&A也是一个常量,和并且“A”在数值上是相同的,但"&A"的类型是int (*)[10],所以在42楼的第一种写**有警告。
...
ShakaLeo 发表于 2010-2-27 14:55

A确实是个常量, 但这个常量我认为在内存也是有空间存它的, 就是它也要被放在一个内存地址里, 那么, 为什么&A不是存放A的地址呢? 非要和A相等干嘛? &不是取地址符号么.

比如: A的值为0x0003 地址为0x456366那么, &A应该是0x456366啊.

恩, 那&&A又是怎么回事呢? 这个难道是: A的值没有加载到data区, 不可能啊, A的值是系统现分配的应该................





哪位大侠能详细的说下这个问题

使用特权

评论回复
47
lelee007| | 2010-2-28 00:30 | 只看该作者
[]的优先级比*高

讨论的挺多,没细看,LZ的意思应该是纠缠于此吧

使用特权

评论回复
48
ShakaLeo| | 2010-2-28 08:42 | 只看该作者
本帖最后由 ShakaLeo 于 2010-2-28 08:53 编辑
A确实是个常量, 但这个常量我认为在内存也是有空间存它的, 就是它也要被放在一个内存地址里, 那么, 为什么&A不是存放A的地址呢? 非要和A相等干嘛? &不是取地址符号么.

比如: A的值为0x0003 地址为0x456366那么, &A ...
sinanjj 发表于 2010-2-27 23:41

A的值既不在data区,而且和“常变量(即const型变量)”不同,也不在const区,A的值可能在code区,也可能根本没有一个固定的位置来存储A的值。所以&符号不是取地址。下边是IAR EWARM环境下"p=&A"的汇编指令:
  LDR  R0, [PC, #+808]     ;把变量p的地址加载到R0
  LDR  R1, [PC, #+800]     ;把A的值加载到R1
   STR  R1, [R0, #0]            ;把A的值存储到变量p的地址上

PC+808和PC+800这些位置都是在code区的。所以“存储变量p的地址”的位置和“存储A的值”的位置都是在code区的。

ARM没有直接寻址的指令,所以只能把这些值存储在code区的某个位置来间接寻址。对于有直接寻址的MCU来说,甚至没有存储A的值的位置,比如AVR,同样是p=&A, ICCAVR的汇编指令如下:
LDI   R24,  0x04
LDI   R25,  0x01         ;数组A的首地址是0x0104,把A的值加载到R24和R25
STS  0x0103,  R24
STS  0x0102,  R25    ;指针p的地址是0x0102,把数组A的首地址直接存储到变量p的地址上

可以说A的值是在代码中,没有一个固定的存储位置,所以这时候对A取地址就更没有意义了,所以&A不会是对常量A取地址。
对于&&A的写法,我在上面的两个编译器里都试了一下,都会报错,估计不是标准用法。

使用特权

评论回复
49
sinanjj| | 2010-2-28 13:05 | 只看该作者
A的值既不在data区,而且和“常变量(即const型变量)”不同,也不在const区,A的值可能在code区,也可能根本没有一个固定的位置来存储A的值。所以&符号不是取地址。下边是IAR EWARM环境下"p=&A"的汇编指令:
  LDR ...
ShakaLeo 发表于 2010-2-28 08:42


有幸得到骨灰级大侠指点, 实乃万幸。

那么, 从汇编代码上看, 也就是说:A指代的地址值在编译的时候已经确定,并写入code区,不在程序运行中改变了。所以对A取地址无意义。

从这个解释上看, 争论中, HWM前辈实际对C的理解更胜一筹啊。呵呵。

p=&A; 符合语法解释但不符合实际模型。
p=A; 符合实际模型但语法上说不过去。



再次感谢您的解释。

使用特权

评论回复
50
反质子| | 2010-3-1 11:40 | 只看该作者
指针基础啊
int (*p)[10]  指向一个匿名数组呀

使用特权

评论回复
51
highgear| | 2010-3-1 21:46 | 只看该作者
又见申明问题。
p1 = &A; p2 = A; 中 A 是数组名, 可以被编译器当作常数对待, 但数组名就是数组名, 不是概念的常数. p1 = &A; p2 = A 或许最终在汇编层面都被编译器作为常数处理,但在编译器层面有类型的不同。 &A 不是对常数取地址,而是对数组名取地址,即得到一个指向数组的指针。 而 p2 = A, p2 只是指向数组第一个元素的指针, 不是指向整个数组. 这种区别体现在指针操作上:
p1++; p2++; 此时, p1, p2 指向了不同的地址。

int a1= ((int) (p1+1)) - ((int) p1);  
int a2= ((int) (p2+1)) - ((int) p2); 注意 a1, a2 的不同。

另外: 这种申明问题,用我以前讲过的一个中心,两个基本点来解题, 就很简单。 int (*p)[10] 的中心是 *p (因为括号的优先级), 说明p是一个指针。 于是整个申明就是 p 是一个什么什么的 指针。

使用特权

评论回复
52
love_5206| | 2010-3-4 15:45 | 只看该作者
10# 冷漠

char   *P[10];——>char   P[ ][10];  // 二维数组就是个指针数组。字符串数组中很常用的写法。

兄弟这个...char *p[10] ,是不是声明了一个10个元素的指针数组呢?如果是二维数组表示的话 是不是char   P[10][]呢?

使用特权

评论回复
53
lenglx| | 2010-3-4 18:30 | 只看该作者
呵呵,凑个热闹.
单从C语言的角度的讲,涉及到某个具体的编译器时,那要看它对C标准支持到什么程度.
有那种比较古老的C编译器,对指针的处理是宽松的.
如果是符合C99标准的现代编译器,对指针的处理是"强类型"的,不允许不同类型的指针直接赋值(void* 不在讨论范围).
当然你可以强行关闭"强类型"检查.
这里我只讨论那种"强类型"的C.
这种C语言,强烈区分"数组"和"指针",虽然,数组的名字可以做为它第一个值的地址来用,
但这仍然是2个不同的类型.这个因为在C中使用过于频繁,特别豁免的("ARM"一书,提起过这个,好像)
  int * p1 [10];
  int (* p2) [10];
  
  这是2个不同的类型.
  从C语言的运算符优先级看.
  int * p1 [10];
        -------   p1 是一个数组
      ---------   数组的内容由指针组成
  -------------   指针所指的对象类型是 int
    p1是一个有10个int *组成的数组, 即p1的类型是 int *
   
  int (* p2) [10];
      ------        p2 是一个指针
      -----------   指针所指对象是一个10个对象所组成数组
  ---------------   数组对象的类型是int  
    p2 是一个指向10个int所组成的对象的指针. 即 p2 的类型是 (int[10])*
   
   
    int i[10];   // i 是一个10个int所组成的数组
                 // i 也可以表示 i[0] 的地址, 即 &i[0]
    p1 = i;     //  错: p1 是数组
    p1[0] = i;  //  OK: p1[0]的类型是int*, i是i[0]的地址,也是int*
   
    p2 = i;   // 错: p2 的类型 (int[10])*, 而i是int[10],即使看作int*,也同p2的类型不同
    p2=&i;   // OK: p2 的类型 (int[10])*, 而i是int[10], &i(取址),类型当然是(int[10])*

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
zq1987731 + 1
54
lenglx| | 2010-3-4 18:47 | 只看该作者
to 43楼:
定义一个数组int A[10],A是一个常量,类型是int *,一个常量前加“&”并不是对该常量取地址。&A也是一个常量,和并且“A”在数值上是相同的,但"&A"的类型是int (*)[10],所以在42楼的第一种写**有警告。
...
ShakaLeo 发表于 2010-2-27 14:55

再饶舌下:

int A[10];
A除了可以表示数组的首地址外(此时它的类型是是int *),
同时它还是个对象,类型是int[10];
所以是可以对它取址的,即&A是可以的.
同时,因为A的地址同A[0]的地址是相同的(因为它是数组嘛),所以下面的程序片段是正确的:
void * p1= A;   // p1 = &A[0];
vod * p2 = &A; // p2 = &A;
ASSERT(p1==p2);// 此断言是成立的

使用特权

评论回复
55
ShakaLeo| | 2010-3-4 19:22 | 只看该作者
本帖最后由 ShakaLeo 于 2010-3-4 19:32 编辑
再饶舌下:

int A[10];
A除了可以表示数组的首地址外(此时它的类型是是int *),
同时它还是个对象,类型是int[10];
所以是可以对它取址的,即&A是可以的.
同时,因为A的地址同A[0]的地址是相同的(因为它是数组嘛),所以 ...
lenglx 发表于 2010-3-4 18:47

我并不是说&A这个表达式有问题,而是说&号在这里不是对A这个常量取地址的意思,你可以看看sinanjj在36楼的发言,再看看我在48楼的分析,A是一个常量,很有可能只出现在指令的操作数中,这样就没有一个固定的存储地址,何来取地址?

使用特权

评论回复
56
thanksgiving| | 2010-3-4 21:50 | 只看该作者
mark

使用特权

评论回复
57
冷漠| | 2010-3-5 09:37 | 只看该作者
哈!再饶舌一下。A不能叫常量,——A叫做“指针常量。”
指针是一个地址。所以一个指针,只能作为“左值”使用。例如:
A=&A[0];   //正确写法

所以下面写法有语法问题:
P=A;     //地址A 成了右值。
但实际中为什么可以这么写?HWM 讲过了。编译器认为没问题。它有它的道理。

P=A;和  P=&A;编译结果一样。
把警告级别降低就不会出现警告提示了。

使用特权

评论回复
58
machunshui| | 2010-3-5 11:15 | 只看该作者
P=&A;
所有编译器都可以无警告通过编译。

P=A;
少部分编译器可以通过,但有告警。
大部分编译器不可以编译通过。

使用特权

评论回复
59
highgear| | 2010-3-5 22:10 | 只看该作者
顶 53 楼 lenglx “强类型“。
57楼冷漠概念不清.

数组就是数组, 不可以与常量混淆. 某些 compiler(往往无os内存管理器) 编译连接时静态分配内存,因而数组地址在编译时可以作为常量嵌入, 连接时确定, 此时,可以认为数组名 A 是常数。另外一些compiler, 无法在编译时确定地址, 只能在运行时由 os 的内存管理器分配后,根据 offset 确定。

"P=A;和  P=&A;编译结果一样。", 但概念不一样。 就如同:
int** p1 = (int**) 1234;
int* p2=(int*) 1234;

int (*p1)[10]; 的定义本身说明了 p1 是一个“强“类型, 那么还是严格遵照类型去做。

使用特权

评论回复
60
guanjc7| | 2010-3-7 12:28 | 只看该作者
1# zq1987731
我大概看了一下Int  p[10], int *p[10], int (*p)[10]
简单的说:
第一个定义了10个整型数据;
第二个定义了10个指针且每个指针都必须指向INT数据;
第三个定义了1个指针且这个指针只能指向INT  a[10]的数组
不过在引用时,还有点不会写。望大侠们修改了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则