指针和数组进阶剖析

 

目录

一,指针本质分析

1,传值调用和传址调用:

2,常量与指针:

二,数组本质分析

1,数组名

2,数组参数

三,指针和数组本质分析

1,指针和数组的运算:

1-1,指针运算:

1-2,数组运算:

2,指针和数组之间的相互转换


一,指针本质分析

定义:指针就是内存地址;指针变量就是用来存放内存地址的变量。

分析:只要是变量就有地址和占用内存,所以指针变量也有地址,并且占用内存。

注意:指针是不占用内存地址的,其本质是地址,地址不占用空间。指针变量才占用内存空间。

指针变量内存所占空间大小sizeof(type*)或sizeof(p)取决于计算机的位数:

32位数占用4字节;

64位数占用8字节;

128位数占用16字节;

。。。

int i = 1;
int* p = &i;
p    //指针变量,是i的地址。
*p   //去内存地址中保存的值。
&p   //指针变量占用的地址,是p的地址。

&和*是一对相反操作符,可以相互抵消。*&p=p

%p  打印地址。

1,传值调用和传址调用

只有通过地址,才可以在函数内部修改函数外部的变量:

#include <stdio.h>
​
int swap(int* a, int* b)
{
    int c = *a;
    *a = *b;
    *b = c;
}
​
int main()
{
    swap(&aa, &bb);
    printf("aa = %d, bb = %d\n", aa, bb);
    
    return 0;
}

通过指针进行传址调用,交换了aa和bb的值。

2,常量与指针:

const int* p ;    //p可变,p指向的内容不能变。

int const* p ;    //p可变,p指向的内容不能变。

int* const p;    //p不可变,p指向的内容能变。

const int* const p ;   //p和p指向的内容均不能变。

口诀:左数又指(数:地址保存的数据;指:指向的地址)

const p :常量指针


二,数组本质分析

数组是相同变量的有序集合。

在数组a[ ]中:

  • a :数组第一个元素地址。

  • &a :数组名的地址

  • &a[0] : 数组第一个元素地址

1,数组名

数组名可以看作一个常量指针,意思就是指针的起始地址不能改变。在表达式中也就只能作为右值使用。

数组名不包含长度信息。

在下面场合数组名不能看作常量指针:

  • 数组名作为sizeof操作符的参数;sizeof(a)表示数组a[ ]的大小;

  • 数组名作为&运算符的参数;&a表示数组a[ ]的起始地址;

数组名其实并不是指针,不能等同于指针。

例如:int a[ ] ;  int* p ; 

&a表示数组a[ ]的起始地址;

&p表示指针p所占的内存地址;

2,数组参数

数组作为函数参数时,编译器将其编译为对应的指针。在这个过程中,数组的大小信息会丢失。所以一般情况下,会再定义一个参数表示数组大小。

void f(int a[]); <-> void f(int* a);

void f(int a[5]): <-> void f(int* a);

#include <stdio.h>
​
void func1(char a[5])
{
    printf("In func1: sizeof(a) = %d\n", sizeof(a));
    
    *a = 'a';
    
    a = NULL;
}
​
void func2(char b[])
{
    printf("In func2: sizeof(b) = %d\n", sizeof(b));
    
    *b = 'b';
    
    b = NULL;
}
​
int main()
{
    char array[10] = {0};
    
    func1(array);
    
    printf("array[0] = %c\n", array[0]);
    
    func2(array);
    
    printf("array[0] = %c\n", array[0]);
    
    return 0;
}


三,指针和数组本质分析

  • 数组名可看作(但不是指针)指向数组第一个元素的常量指针。

  • 数组名并不是数组地址,而是数组首元素的地址。

  • 函数的数组参数会退化为指针。

1,指针和数组的运算:

1-1,指针运算:

p+n ;<-- >(unsigned int)p + n*sizeof(*p);

注意:当用数组地址对指针p赋值时,p加减n就是指向当前元素前后变化n个位置的元素。

——指针之间的运算:

指针之间之支持减法运算,参与减法运算的指针同类型必须相同。

p1-p2;<-->((unsigned int)p1 - (unsigned int)p2) / sizeof(type);

注意:只有两个指针指向同一数组时,指针相减才有意义,意义为指针所指元素的下标差。当不指向同一数组时结果未定义。

——指针的比较:

指针可以进行关系比较(<,  >,  <=,  >=)

比较的前提是指向同一数组。

任意两个指针之间进行(==,  !=)无限制,但两者类型需要相同。

1-2,数组运算:

首先我们知道了a和&a意义不同;真正体现出实际的不同就是在它们两者的运算。

a+1;<-->(unsigned int)a + sizeof(*a);

&a+1;<-->(unsigned int)(&a) + sizeof(*&a);

          <-->(unsigned int)(&a) + sizeof(a);

sizeof(*a)代表数组元素的大小。

sizeof(a)代表整个数组的大小。

 
 
#include <stdio.h>
​
int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
    
    return 0;
}
// p1[-1]将输出5,p2[0]输出乱码(有理由的乱码), p3[1]输出3

2,指针和数组之间的相互转换

数组名可以看作常量指针;

同样的,指针也可以看作数组名使用。

例如:int a[ ] = {0};   int* p = a;   那么a[i]<->p[i];

下标形式VS指针形式:

下标形式转换成指针形式:

a[i]<->*(a+i)<->*(i+a)<->i[a];  

i[a]看起来很怪异,但是p[i]的另一种写法。

#include <stdio.h>
​
int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        p[i] = i + 1;
    }
    
    for(i=0; i<5; i++)
    {
        printf("a[%d] = %d\n", i, *(a + i));
    }
    
    printf("\n");
    
    for(i=0; i<5; i++)
    {
        i[a] = i + 10;
    }
    
    for(i=0; i<5; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
    
    return 0;
}

值得注意的是,指针能看作数组名,但并不是数组名:

#include <stdio.h>
​
int main()
{
    int a[]={1, 2, 3, 4, 5};
    extern int* a;
    
    printf("&a = %p\n", &a);
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);
​
    return 0;
}

おすすめ

転載: blog.csdn.net/m0_58702068/article/details/118772901