指针
Suzhou 522 0
取地址运算

sizeof是一个运算符可以给出某个字节或某个变量在内存中所占的字节数,写法是:sizeof(i)。

#include <stdio.h>

int main()
{
    double a;
    int b = 6;
    printf("sizeof(double)=%ld\n",sizeof(double));
    printf("sizeof(b)=%ld\n",sizeof(b));
    return 0;
}
sizeof(double)=8
sizeof(b)=4

--------------------------------
Process exited after 0.01784 seconds with return value 0

上述代码可知double占用8字节内存,b占用4个字节的内存,1个字节是8个比特。


运算符&的作用是取得变量的地址,所以&的操作数必须是变量。 ::: tip C语言的变量是放在内存中的,每一个变量都有相应的占有一定的内存空间(可以用sizeof获得),占有内存空间就会有相应的地址。 :::

#include <stdio.h>

int main(void)
{
    int i = 0; 
    printf("0x%x\n",&i);
    printf("%p\n",&i);
    printf("%lu\n",sizeof(int));
    printf("%lu\n",sizeof(i));
    printf("\n");
    int p = &i;
    printf("0x%x\n",p);
    printf("%p\n",p);
    return 0;
}
0x62fe18
000000000062FE18
4
4

0x62fe18
000000000062FE18

--------------------------------
Process exited after 0.01819 seconds with return value 0

::: tip %p 是以16进制的形式输出内存地址。不过%p的输出字符为8个前2个为00。 %x 也是以16进制的形式输出内存地址。%x只输出6个字符,前面加0x表示是16进制数。 ::: ::: warning 通过sizeof计算出的变量的地址大小和数据类型,和int是否完全相同,和编译器的架构有关。 :::

#include <stdio.h>

int main(void)
{
    int i = 0; 
    int p;
    printf("%p\n",&i);
    printf("%p\n",&p);
    printf("%d",sizeof(p));
    return 0;
}
000000000062FE1C
000000000062FE18
4
--------------------------------
Process exited after 0.02182 seconds with return value 0

如上,16进制中的C表示12,和8相比相差4,即p的大小。 通过观察上述代码和结果,可以发现1C的位置是变量i,18的地址是变量p。先定义的变量i在比后定义的变量p更高的地方。原因是变量i和变量p都是本地变量,分配在内存堆栈中,在堆栈中分配变量是自顶向下分配的,即先定义的变量地址更高,后定义的变量地址更低。


有关数组的地址

#include <stdio.h>

int main(void)
{
    int a[10];   //定义一个10个int组成的数组a
    printf("%p\n",&a);
    printf("%p\n",a);  //把数组的名字试图交给printf取地址
    printf("%p\n",&a[0]);  //取出数组a中第一个元素(a[0])的地址
    printf("%p\n",&a[1]);
    return 0;
}
000000000062FDF0
000000000062FDF0
000000000062FDF0
000000000062FDF4

--------------------------------
Process exited after 0.01508 seconds with return value 0

上述代码结果表明,&a == a == &a[0] < a[1],继续计算下去可得数组a中的元素位置相隔都是4。


指针
scanf

如果能够取得变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量? 指针类型的变量是保存地址的变量。通常使用p来表示指针。 如果有一个变量int i,就可以有一个int p = &i;。星号*表示p是一个指针指向int,变量i的地址传给p。 ::: tip 当p中的值为i的地址时,可以认为p指向i。 ::: ::: warning int p,q;和int p,q都表示p是一个指针指向int,q是一个普通int类型变量。 *p表示p是指针,不能认为是int。如果认为p和q都是指针,需要写成int p,q; ::: 变量的值是内存的地址。 普通变量的值是实际值,指针变量的值是具有实际值的变量的地址。

作为参数的指针

当把一个指针作为参数的时候可以用以下写法

#include <stdio.h>

void f(int *p);  //指针p作为f函数的参数
int main(void)
{
    int i = 0;
    printf("&i=%p\n",&i);
    f(&i);  //调用f函数时要把i的地址交给函数f,不能把变量i本身或i的值交给f
    g(i);  //把i的值传递给函数g 
    return 0;
 } 

 void f(int *p)
 {
     printf("p=%p\n",p);
 }
 void g(int k)
 {
     printf("k=%d\n",k);
 }
&i=000000000062FE1C
p=000000000062FE1C
k=0

--------------------------------
Process exited after 0.01575 seconds with return value 0

上述程序中,函数f取得的是变量i的地址,通过f函数中的指针p,可以得到main函数中的变量i的地址,即可以访问到变量i,而函数g中的变量k的值和main中的变量i相同,不能说明k和i有关系。

访问地址变量

访问意味着可读可写,如果有了变量地址,想要访问地址上的变量,需要用到运算符。 这里的*星号是一个单目运算符*(只有一个算子),用来访问指针的值所表示的地址上的变量。 星号加变量得到的指针可以做左值也可以做右值,即可以放在赋值号的右边读取,也可以放在赋值号的左边写入,如int k = p和p = k+1。

通过指针读写变量:

#include <stdio.h>

void f(int *p);  //指针p作为f函数的参数
int main(void)
{
    int i = 6;
    printf("&i=%p\n",&i);
    f(&i);  //调用f函数时要把i的地址交给函数f,不能把变量i本身或i的值交给f
    g(i);  //把i的值传递给函数g 
    return 0;
 } 

 void f(int *p)
 {
     printf("p=%p\n",p);
     printf("*p=%d\n",*p);  //通过指针访问到了p所指的int i的值 
     *p = 10;  //修改指针*p的值 
 }
 void g(int k)
 {
     printf("k=%d\n",k);  //调用函数g查看变量i的值 
 }
&i=000000000062FE1C
p=000000000062FE1C
*p=6
k=10

--------------------------------
Process exited after 0.01728 seconds with return value 0

上述代码中第16行表示可以通过指针访问到变量,经过第17行修改指针值,第9行调用函数g时,访问main中变量i的值,看到变量i的值和指针的值一起发生变化,即完成了对变量值的修改。 ::: tip 上述程序中,变量i的地址通过&i传递给p,p的值是i的地址,p就代表了i,就可以通过修改p的指针来修改i的值,即针对*p的改动,实际是对变量i的值的改动。 :::


指针与数组
函数参数表中的数组

把数组作为值传递给一个函数,在函数的参数表中有一个变量接收这个数组,函数会接收到什么?实验程序如下:

#include <stdio.h>

void minmax(int a[] , int len , int *max , int *min);

int main(void)
{
    int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
    int min,max;
    printf("main sizeof(a)=%lu\n",sizeof(a)); //main中数组a的大小
    printf("main a=%p\n",a);  //main中查看数组a的地址
    minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);  //函数调用
    printf("a[0]=%d\n",a[0]);  //查看minmax函数中修改过的a[0]的值
    printf("min=%d,max=%d\n",min,max);

    return 0;
}

void minmax(int a[] , int len , int *min , int *max)
{
    int i;
    printf("minmax sizeof(a)=%lu\n",sizeof(a));  //minmax中数组a的大小
    printf("minmax a=%p\n",a);  //minmax中查看数组a的地址
    a[0]=1000;  //minmax中修改a[0]的值
    *min = *max = a[0];
    for( i=1 ; i<len ; i++ ){
        if( a[i] < *min ){
            *min = a[i];
        }
        if( a[i] > *max){
            *max = a[i];
        }
    }
}
main sizeof(a)=68
main a=000000000062FDD0
minmax sizeof(a)=8
minmax a=000000000062FDD0
a[0]=1000
min=2,max=1000

--------------------------------
Process exited after 0.01662 seconds with return value 0

上述程序中的第9行和第21行分别查看在函数调用前后数组a的大小,在输出结果中的第1行和第3行,函数调用前后的大小不同,函数调用后不能使用sizeof得到正确的元素个数。 第10行和第22行代码查看数组a的地址,输出结果完全一致,说明在minmax函数中的数组a就是main中的数组a。 第23行代码修改数组a中的某一个值,通过第12行代码,查看函数调用后数组a中值,输出结果第5行a[0]的值被修改。 由上述可以判定,int a[]就是指针,可以用*a代替a[],指针可以有第23行a[0]、第26行a[i]等数组的用法。 ::: tip 函数参数表中的数组实际上是指针,sizeof(a) == sizeof(int )。 对于这样的指针可以使用数组运算符[]进行运算。 ::: 指针 *上图并不是指类型等价,只是在出现在参数表中作为参数原型是等价的。**


数组变量是特殊的指针

::: warning 数组变量本身表达地址,取数组地址时可以不加&符号直接用数组变量的名字就可以得到数组的地址:int a[10];int p=a;无需使用&取地址。但是数组的每一个单元表达的是单个变量,需要用&取地址。a == &a[0]; ::: []运算符可以对数组做,也可以对指针做。如果有一个数组p[0],就相当于p。p[0] <==>a[0],如下:

#include <stdio.h>

int main(void)
{
    int min;
    int *p = &min;
    printf("*p=%d\n",*p);
    printf("p[0]=%d\n",p[0]);

    return 0;
}
*p=32765
p[0]=32765

第8行代码中的p[0]是指如果p所指的位置是一个数组,p[0]是数组的第一个单元。

*运算符可以对指针做,也可以对数组做,如下:

#include <stdio.h>

void minmax(int a[],int len,int *max,int *min);

int main(void)
{
    int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
    int min,max;
    minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    printf("*a=%d\n",*a);  

    return 0;
}

void minmax(int a[],int len,int *min,int *max)
{
    int i;
    a[0]=1000;
    *min = *max = a[0];
    for( i=1 ; i<len ; i++ ){
        if( a[i] < *min ){
            *min = a[i];
        }
        if( a[i] > *max){
            *max = a[i];
        }
    }
}
*a=1000

--------------------------------
Process exited after 0.01637 seconds with return value 0

上述代码第10行对数组a输出*a,可以得到a[0]的值。

数组变量是const的指针,数组变量不能做互相赋值,即int b[] = a是错误的写法,int b可以被看做是int * const b,const的含义是指b是常数,不能改变,即数组是一个常量指针

评论区

索引目录