欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-17 15:34:34
...

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

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

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

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

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指向b
printf("%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