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

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

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

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

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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值