C语言基础 (四)指针与数组

来源:互联网 发布:淘宝联盟返现 编辑:程序博客网 时间:2024/05/17 03:55

数据保存在内存中,而每一块内存空间都有一个编号,称为内存地址。

指针变量用来存放这个地址编号的变量称为指针变量;

通过指针可以访问和处理指针所指向的对象,增加了访问数据的手段,使程序更加灵活。

指针本身也是一个变量,它存储的是另一个变量的地址。存放变量地址的变量是指针变量。因此,一个指针变量的值就是某个变量的地址。为了表示指针和他所指向的变量之间关系,在程序中用*符号表示指向。

32位系统中,指针变量都是占4个字节,64位系统占8个字节。指针变量中保存的只是地址编号,不管是什么类型的指针保存的都是地址编号。所以同一系统中什么类型的指针变量都是占相同的字节数。

打印地址编号%d输出的是十进制的地址 %p输出的是16进制的地址。

指针变量的四个特点

1、          它的值就是一个地址

2、          指针变量的值可以改变,存储一个新的地址,即指向别的变量;

3、          指针指向一个特定的类型,即指针本身也是有类型的。

4、          多个指针变量可以指向同一个值。

int a=5;    int *p=&a;           //在声明指针时,*放在类型后边,表示变量是指针变量。    *p=100;             //p是a的地址,*p就是a    //int*q=100;         //q指向地址编号为100 的存储单元,这是一种错误的写法    *p =*p * *p;    *p--;                 //* ++的优先级一样,结合性是从右到左,所以p会进行++就不再指向a    printf("%d\n",a);     //通过变量a访问数据5        printf("%d\n",*p);    //指针前加* 表示对指针的解引用,即访问指针指向的变量;                        //在声明之外的地方,*表示对指针解引用;声明指针变量 char *p int *p flaot * p 等等;野指针    int a=5;    int *p;//没有初始化的指针,叫做野指针,即指针指向不明确的指针*p=100;//指针的指向不明确,就不能解引用。必须先有地址再赋值;空指针  int*q=NULL;//NULL表示空 被初始化成null的指针叫空指针,指向内存编号为0的内存                   //空指针也不可以解引用空指针与野指针的区别:野指针:未被初始化的指针,里面的内容是垃圾地址,它的值是不明确的;空指针:被初始化的指针,里面的地址是0;注意不要解引用空指针和野指针    int a=5;    int b=10;    int *p;    p=&a;               //p本身的值是a的地址,p指向a    p=&b;               //修改p本身的值,存储一个新的地址,即指向新的变量,p指向bprintf("%d",*p);      //指针本身也是有类型的。要指向特定类型的变量。    int a=5;    int *p=&a;            //在声明指针时,*放在类型后边,表示变量是指针变量。    *p=100;             //p是a的地址,*p就是a    //int*q=100;         //q指向地址编号为100 的存储单元,这是一种错误的写法    *p =*p * *p;    *p--;                //* ++的优先级一样,结合性是从右到左,所以p会进行++就不再指向a    printf("%d\n",a);     //通过变量a访问数据5    printf("%d\n",*p);    //指针前加* 表示对指针的解引用,即访问指针指向的变量;                        //在声明之外的地方,*表示对指针解引用;    int a=5;    int *p;              //没有初始化的指针,叫做野指针,即指针指向不明确的指针   *p=100;              //指针的指向不明确,就不能解引用。    int *q=NULL;printf("%d",*q);      // 空指针不能解引用void change(int *x,int *y){   //参数是传一个指针变量。    int temp=0;            //通过解引用交换,变量的值;    if (*x < *y) {        temp=*x;        *x=*y;        *y=temp;    }//通过数组名地址,打印数组元素void pfarray(){    int a[5];    for (int i=0; i<5; i++) {        scanf("%d",&a[i]);    }    for (int k=0; k<5;k++) {        printf("%d",*(a+k));  a+k就是第k个元素的地址    }}void exchange(int *p,int *q){//相当于    int temp=*p;             //temp=a;    *p=*q;                   //a=b;    *q=temp;                 //b=temp}//修改形参不会影响实参  在传值,传指针时,都适用void changebyad(int *p,int *q){    int *temp=p;    p=q;    q=temp;       //在函数中只交换了形参p和q的指向,并没有修改p和q指向的变量    printf("&p=%p &q=%p\n",p,q ); //并不能实现值的交换}int *max(int ,int);#include <stdio.h>int main(){    int a=5;    int b=10;    int *r=max(a,b);    printf("%d",*r); //虽然也可以返回正确的值,但是不能这么用}int *max(int x,int y){//x,y是max函数的形参,只存在函数中,函数执行结束,p和q就会释放掉;    return x>y? &x:&y;//不要返回局部变量的地址,当局部变量的地址释放掉之后,指针就变成了野指针}#include <stdio.h>int main(){    int a=5;    int *p=&a;//p中保存的是a的地址    //指针变量也是一个变量,本身也有地址    int **q=&p;  // 保存一维指针地址的指针称为二维指针                //**q是二维指针。    printf("%d\n",a);    printf("%d\n",*p);    printf("%d\n",**q);    //q-->&p         关于指针要记住两个值    //*q-->p          1、指针本身的值    //**q-->*p        2、指针加* 的值    return 0;}void exchage (int **x,int **y){//利用二维指针进行地址的交换    int *temp=*x;              //temp=p;    *x=*y;                     //p=q;    *y=temp;                   //q=temp;}  指针和数组 int a[5]={1,2,3,4,5};    printf("%p\n",&a[0]);       //结果为0x7fff5fbff7b0    printf("%p\n",a);           //结果为0x7fff5fbff7b0                               //数组名a是数组中首元素的地址,即a【0】的地址                            //数组名a是指向数组首元素的指针    int *p=a;                  // p的值就是数组首元素的地址    //a=&a[1] 这样写是错的,a的值不能变,只能指向数组中的首元素。    printf("%p\n",p);    printf("%p\n",&a[1]);    printf("%p\n",&a[2]);return 0;int a[5]={1,2,3,4,5};int *p=a;    //printf("%p %p\n",&a[1],(p+1));    //首元素地址加1就是第二个元素的地址    //首元素地址加n就是第n+1个元素的地址(下标为n的元素的地址)//    for (int i=0; i<5;i++) {//        printf("%d\t",*(p+i));//p+i就是下标为i的元素的地址//    }    for (int k=0; k<5; k++) {        printf("%d\n",*(p+k));//指针加1并不是地址加1而是偏移一个元素    }    for ( p=a; p<=&a[4]; p++) {        printf("%d\t",*p);    }    printf("\n");    p--;//指针加1,指向后边一个元素,指针减1,指向前边一个元素(就算不是数组也能指回去)    printf("%d",*p);}  int a[5]={1,2,3,4,5};    int *p=a;    fun1(p);    printf("%d\n",*p);    for (int k=0; k<5; k++) {        printf("%d",*(p+k));    }}    void fun(int *q){        (*q)++;    }void fun1(int *q){        q++;            //只修改形参,不是影响实参    (*q)++;         // 第二个元素的值会加1;}//通过使用二维指针修改 指针变量m中的值,改变指针变量m的指向void fun2(int **x);void fun(int *x);int main(){    int a[5]={1,3,5,6,7};    int *m=a;    for (int i=0; i<5; i++) {        fun(m);         //使元素的值加1;        fun2(&m);      //指针变量m指向的元素一次向后加1   }    for (int k=0;k<5; k++) {        printf("%d",a[k]) ;    }        printf("\n");}void fun2(int **x){    (*x)++;       //*x就是指针变量m中存放的值即数组a的首地址 然后进行加加操作就指                          }               //向了数组中的下一个元素void fun(int *x){    (*x)++;       //*x就是*p 指向数组中元素的值,首元素执行 ++ 操作}内存分配局部变量,在函数中或某个程序块中声明的变量叫做局部变量。全局变量  在所有的函数的外边声明的变量叫做全局变量    int main(){    int a=5;         //该变量是局部变量,所在的内存区是在栈上    int *p=&a;      //p也是局部变量,也在栈上    const int b=10;   //b是常量,在常量区;                    //栈上的空间有程序自动分配和释放void *malloc(size_t size);  //不是空,是一个没有类型的指针,在堆区请求一块存储空间,size请求空间大小,malloc返回这个指针指向请求的堆空间,如果请求失败,则返回null;int main(){    int a=5;    int *p=&a;    int *q=(int *)malloc(4);//在堆区分配4个字节的空间,将该空间的地址返回值赋值给指针q;                     //这块空间没有名字,只能通过这块空间的地址去访问    *q=100;    printf("%d\n",*q);//q是栈上的指针保存堆空间的地址    float *pf=(float *)malloc(4);    *pf=5.9;    printf("%f\n",*pf);    free(q);    free(pf);//释放堆空间 空间指正释放一次,不能重复释放。释放掉之后就不要再访问了。    printf("%f\n",*pf);//释放后仍能被打印出来    //每个存储单元都有两个状态,一个占用状态,一个空闲状态    //分配空间就是把这块空间由空闲状态标记为占用状态,释放空间,就是把这块空间由占用状态标记为空闲状态,并没有抹掉这块空间的内容。    //p的值一直没有改变,空间释放后依然指向这块空间,变成野指针了。通过指针还可以继续访问这块空间    return 0;常量指针,指向常量的指针,指针本身的值可以修改,但指针加* 的值不能修改。//指针常量  指针本省是个常量,指针本身的值不能改,但指针加*的值可以改。//const 修饰谁,谁就不能变const int *p修饰的是*则指向的变量的值不能变//int *const p修饰的是p则p的值不能改变即存的地址不能改变;//int main(){//    int a=10;//    int b=20;//    const int *p=&a;//    int* const q=&b;//    p=&b;//  //  *p=100;//}//引用 常量引用//j的值不可以修改,i的值可以修改。传值:形参是实参的值的拷贝,形参的改变不会影响实参,单向传递。传指针:也是传值的一种方式,是通过形参简洁改变实参所指向的变量的值传应用:形参就是实参,改变形参就是改变实参,双向传递。局部变量和全局变量重名,全局变量会被隐藏,通过::作用域符号访问该全局变量引用定义已定义的全局变量 extern