第29课 指针和数组分析(下)

1. 数组的访问方式

(1)以下标的形式访问数组中的元素:如a[i];

(2)以指针的形式访问数组中的元素:如*(a+i)

2. 下标形式 VS 指针形式

(1)指针形式以固定增量在数组中移动时,效率高于下标形式

(2)指针增量为1且硬件具有硬件增量模型时,效率更高

(3)下标形式指针形式的转换:  a[n]==*(a+n)==*(n+a)==n[a];

▲注意:

  ①现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率与指针形式相当

  ②但从可读性和代码维护的角度看,下标形式更优

【实例分析】数组的访问方式

 1 #include <stdio.h>
 2 
 3  
 4 
 5 int main(){
 6 
 7    
 8 
 9     int a[5] = {0};
10 
11     int* p = a;
12 
13     int i = 0;
14 
15  
16 
17     for(i=0; i<5; i++)
18 
19     {
20 
21        p[i] = i + 1; //利用指针给数组元素赋值
22 
23     }
24 
25  
26 
27     for(i=0; i<5; i++)
28 
29     {
30 
31        //以指针形式来访问数组元素
32 
33        printf("a[%d] = %d
",i,*(a+i));
34 
35     }
36 
37  
38 
39     for(i=0; i<5; i++)
40 
41     {
42 
43        //以下标形式来访问数组元素
44 
45        i[a] = i + 10; //比较另类,相当于a[i] = i + 10
46 
47     }
48 
49  
50 
51     for(i=0; i<5; i++)
52 
53     {
54 
55        //通过指针来访问数组元素
56 
57        printf("p[%d] = %d
",i,p[i]);
58 
59     }
60 
61  
62 
63     return 0;
64 
65 }

【编程实验】数组和指针不同

//ext.c

//实验时,本文件应为.c,而不能是头文件。因为头文件会被直接包含在相应文件中,

//而.c文件是分别编译的

int a[5] = {1, 2, 3, 4, 5}; //在该文件中,a被看作一个数组

//29-2.c

 1 #include <stdio.h>
 2 
 3  
 4 
 5 //命令行编译这两个文件:gcc 29-2.c ext.c
 6 
 7  
 8 
 9 int main(){
10 
11    
12 
13    //外部文件中a被定义成一个数组。int a[5] = {1, 2, 3, 4, 5};
14 
15  
16 
17     //实验1:
18 
19     extern int a[]; //本文件,如果这样声明,a仍被看作一个数组
20 
21   
22 
23     printf("&a = %p
", &a); //这里的&a为整个数组的地址
24 
25     printf("a = %p
",a);    //a为首元素的地址,数值上等于&a
26 
27     printf("*a = %d
",*a);  //打印出第1个元素,即1
28 
29  
30 
31  
32 
33     //实验2:
34 
35 /*
36 
37     extern int* a;  //如果这样声明,编译器将a当成是一个int型的
38 
39                     //指针来使用。
40 
41   
42 
43     printf("&a = %p
", &a); //会从符号表中查到指针a的地址,指向数组
44 
45     printf("a = %p
",a);    //从指针a处,取一个int型的数据,即第1个元素,为1
46 
47     printf("*a = %d
",*a);  //试图从地址0x01处取出数据,违规内存访问错误。
48 
49 */ 
50 
51     return 0;
52 
53 }

3. a和&a的区别

(1)a数组首元素的地址

(2)&a整个数组的地址,即可将数组看成是一种数据结据。如int[5];

(3)a&a区别在在指针运算

  a + 1 == (unsigned int)a + sizeof(*a);//a代表首元素地址,*a表示第1个元素

  &a + 1 == (unsigned int)(&a) + sizeof(*&a) ==  (unsigned int)(&a) + sizeof(a);

【实例分析】指针运算经典问题

 1 #include <stdio.h>
 2 
 3  
 4 
 5 int main(){
 6 
 7    
 8 
 9     int a[5] = {1, 2, 3, 4, 5};
10 
11     int* p1 = (int*)(&a + 1); //指向数组最后面,即第5个元素的后面
12 
13     int* p2 = (int*)((int)a + 1);//指向数组的起始地址+1byte偏移处
14 
15     int* p3 = (int*)(a + 1);  //指向第2个元素
16 
17  
18 
19     printf("p1[-1] = %d
",p1[-1]);//输出第5个元素
20 
21  
22 
23     //数组a在内存中从低地址到高地址的分布如下:(小端模式,低字节放入低地址)
24 
25     //01 00 00 00,02 00 00 00,03 00 00 00,04 00 00 00,05 00 00 00
26 
27     //p2指向数组起始地址+1字节的偏移处,即01的后面,从这个地方开始读
28 
29     //出4个字节,00 00 00 02,根据小端模式规则,该值为0x02 00 00 00,
30 
31     printf("p2[0] = 0x%X,p2[0] = %d
",p2[0],p2[0]);//0x02000000==33554432 
32 
33  
34 
35     printf("p3[1] = %d
",p3[1]); //输出第3个元素,即3
36 
37  
38 
39     return 0;
40 
41 }

4. 数组参数

(1)数组作为函数参数时编译器将其编译为对应的指针。因此,一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

     void f(int a[])等价于void f(int* a);

     void f(int a[5])等价于void f(int* a); //就是一个指针,丢失了数组长度的信息

【实例分析】虚幻的数组参数

 1 #include <stdio.h>
 2 
 3  
 4 
 5 void func1(char a[5]) //编译时,a被编译为一个指针,丢失了数组长度的信息
 6 
 7 {
 8 
 9     printf("In func1:sizeof(a) = %d
",sizeof(a));//a退化为指针,4
10 
11    
12 
13     *a = 'a'; //对指针所指向的内存进行存取(注意数组名也可以这样对内存
14 
15               //进行访问,如访问第1次元素*a,对i个元素*(a+i);这说明数组名
16 
17               //在使用上很像指针,但数组名并不是指针,见下面的分析
18 
19    
20 
21     //再次说明a是指针,而不是数组名
22 
23     a = NULL;//编译通过,a是指针类型。(而不是数组名,数组名不能作为左值)
24 
25 }
26 
27  
28 
29 void func2(char b[]) //b被编译成一个指针,与数组小大有没有被指定无关。
30 
31 {
32 
33     printf("In func2:sizeof(b) = %d
",sizeof(b));//b退化为指针,仍为4
34 
35    
36 
37     *b = 'b';
38 
39    
40 
41     b = NULL;//编译通过,b是指针类型
42 
43 }
44 
45  
46 
47 int main(){
48 
49    
50 
51     char array[10] = {0};
52 
53  
54 
55     func1(array);
56 
57     printf("array[0] = %c
",array[0]); //array[0] = a;
58 
59  
60 
61     func2(array);
62 
63     printf("array[0] = %c
",array[0]); //array[0] = b;
64 
65  
66 
67     return 0;
68 
69 }

5. 小结

(1)数组名指针使用方式相同数组名的本质不是指针指针的本质也不是数组

(2)数组名不是数组的地址,而是数组首元素的地址

(3)函数数组参数退化指针

原文地址:https://www.cnblogs.com/hoiday/p/10056187.html