C语言指针及结构体

本文深入解析C语言中的指针概念,包括基本定义、指针变量、多级指针、数组与指针的关系、动态内存分配及结构体与联合体中的指针应用,通过实例帮助理解指针操作。

指针介绍

指针:就是内存地址 定义指针变量 int* p = &a;//把a的地址赋值给p

#include <stdio.h> 
#include <stdlib.h> 
main() { 
    int * p; //p 是变量的名字, int * 是一个类型 //这个变量存放的是int类型变量的地址。 
    int i =3; 
    p=&i; 
    system(“pause”); 
}
/*

int * p; //p是变量的名字, int * 表示p变量的数据类型是存放int类型的地址的数据类型
int * p; 不表示定义了一个名字叫做*p的变量
int * p; 应该这样理解: p是变量名, int *是数据类型 ,p变量的数据类型是int*
所谓int * 类型 实际就是存放int变量地址的类型	
int i = 3;
p = &i;
1. p保存了i的地址, 因此p指向i
2. p不是i,i也不是p,修改p的值不影响i的值,修改i的值也不会影响p的值
3. 如果一个指针变量指向了某个普通变量, 则 *指针变量  就完全等同于  普通变量
    
例子:	
	    
如果p是个指针变量,并且p存放了普通变量i的地址	    
则p指向了普通变量i	    
*p  就完全等同于  i
	     
在所有出现*p的地方都可以替换成i	     
在所有出现i的地方都可以替换成*p	    
*p 就是以p的内容为地址的变量 

*/

指针和指针变量的关系:

指针就是地址,地址就是指针

地址就是内存单元的编号

指针变量是存放地址的变量

指针和指针变量是两个不同的概念

但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样

 

* 的三种用法

1. 乘法

2. int* p:定义一个指针变量p,p中存放一个内存地址,这个地址所存放的数据规定是int型

3. *p:取出p中保存的内存地址存放的数据

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int a =12; //在内存中开辟一个空间 
    int* p = &a; //定义指针变量 p ,并把a的地址赋值给p; 
    int** q = &p; //二级指针,指向指针变量的指针; 
    printf("%#x \n",&a); //&运算是用来取地址的 
    printf("a的地址 %#x \n",p); 
    printf("a的值 %d \n",*p); 
}

如下图上述代码中的变量a 指针p q在内存中的关系图

 

多级指针:

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int a = 33; 
    int* p = &a; //注意int* 可以看作一个特殊类型,和普通的变量声明(int a = 33)对比的去理解                 
    int** q = p; 
    int*** m = q; 
    printf("a的值%d\n",a); 
    printf("a的值%d\n",*p); 
    printf("a的值%d\n",**q); //**q相当于*(*q),*q等价于p ,*p 就是a的值; 
    printf("a的值%d\n",***m);//***m等价于**(*m),而*m等价于q; 一层层替换就好理解了 
}

指针常见问题

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int *p; //野指针,不能随便赋值例如 p = 2,这种是错误的; 
    int i ; 
    p = &i; 
    *p = 11; 
    printf("i 的值%d \n",*p); 
    double a = 2.11; 
    p = &a; 
    printf("i 的值%lf \n",*p); 
    double *q ; 
    double b = 3.22; 
    q = &b; 
    printf("b的值%lf \n",*q); 
}

C语言值传递和地址传递(引用传递)

  • 数据传递

所有语言所有平台,都只有值传递,引用传递传递的值是内存地址

值传递(目的:互换a,b两个数值,这种值传递无法达到目的)

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int a = 5; 
    int b = 12; 
    printf("a = %d,b = %d \n",a ,b); 
    swp(a , b); 
    printf("a = %d,b = %d \n ",a ,b); 
} 
void swp(int i , int j){ 
    int tmp = i; 
    i = j; 
    j=tmp; 
}

以上运行结果

a = 5,b = 12

a = 5,b = 12

 

  • 地址传递(目的:互换a,b两个数值,地址传递可以达到目的)
#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int a = 5; 
    int b = 12; 
    printf("a = %d,b = %d \n ",a ,b); 
    swp(&a , &b); 
    printf("a = %d,b = %d \n ",a ,b); 
} 
void swp(int *i , int *j){ 
    //int *tmp = *i; 
    int tmp = *i; 
    *i = *j; 
    *j= tmp; 
}

运行结果:

a = 5,b = 12

a = 12,b = 5

通过传递地址,返回多个数据

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int a = 5; 
    int b = 12; 
    printf("a = %d,b = %d \n",a ,b); 
    swp(&a , &b); 
    printf("a = %d,b = %d \n ",a ,b); 
} 
void swp(int *i , int *j){ 
    *i += 15 ; 
    *j += +10;
    *j = ++*j; 
}

运行结果:

a = 5,b = 12

a = 20,b = 23

主函数中获取子函数中的地址

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int* m; 
    function1(&m); 
    printf("主函数中获取i的值%d \n", *m); 
    system("pause"); 
} 
void function1(int** a){ 
    int i = 20; 
    printf("i的值 %d \n",i); 
    *a = &i; 
}

画图解释以上代码:

 

数组

一维数组名是个指针常量 ,C中不检测数组长度,所以也没有Java中数据下标越界的异常;

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    char c[] = "Hello";

    int i[5] = {1,2,3,4,5};
    printf("%c \n",c[0]);
    printf("%#x \n",&c[0]);
    printf("%#x \n",&c[1]);
    printf("%#x \n",&c[2]);
    printf("%#x \n",&c[3]);
    printf("%#x \n",&c);//数组的地址就是数组中第一个元素的地址

    char* cp = &c;
    printf("%c \n",*cp);//取出数组中第一个元素 
    printf("%c \n",*(cp+1));//取出数组中第二个元素;指针可以做加减运算,但前提是地址是连续的     
    printf("%c \n",*(c+1));//c[1]等价于*(c+1) int* ip =&i; printf("%#x \n",(ip+0));     
    printf("%#x \n",(ip+1));//第二元素的地址和第一个元素的地址相差了4,因为int值在内存中占4个字节  printf("%#x \n",(ip+2));
    printf("%#x \n",(ip+3));
}

指针长度

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int* ip; 
    char* cp; 
    double* dp; 
    int* mp; 
    int i; 
    char c; 
    double d; 
    ip=&i; 
    cp = &c; 
    dp = &d; 
    printf("%d \n",sizeof(ip)); //打印输出都是4,长度是4个字节 
    printf("%d \n",sizeof(cp)); 
    printf("%d \n",sizeof(dp)); 
    printf("%d \n",ip-mp); //没有任何意义,错误写法 
}

动态申请堆内存,C中是通过malloc函数完成的;

#include <stdio.h> 
#include <stdlib.h> 
main(){ 
    int i = 10 ;//栈内存 
    char a[10];//栈内存 
    int* p; 
    p = malloc(sizeof(int) * 10);//申请用于存放10个int类型大小的内存空间;堆内存中
    *p = 21; 
    *(p+1) = 11; 
    printf("%#x \n",p); 
    printf("%d \n",*(p+1)); 
    free(p);//释放后不能在使用指针p了
}

结构体

#include <stdio.h>
#include <stdlib.h>

//函数也是有地址的,所以可以定义函数指针
void study(){
    printf("调用了study()函数\n");
}


//定义结构体
struct student{
    int age;
    int height;
    char sex;

    //结构体不能定义函数 ,但可以定义函数指针;函数的指针定义int (*pf)(int x, int y);
    //定义函数指针
    void (* studyS)();
}


main(){
    //struct student st = {22,180,'w'};
    //printf("结构体长度:%d\n",sizeof(st));

    //定义结构体变量
    struct student st = {22,180,'w',study}; //把函数名study传进去,studyS函数指针就指向了study()函数
    printf("%d \n",st.age);
    printf("结构体长度:%d\n",sizeof(st));
    //第一种函数调用;
    st.studyS();

    //第二种,结构体也有指针
    struct student* stp = &st;
    (*stp).studyS();

    //第三种,这种调用stp必须是结构体指针
    stp->studyS();
}

联合体

联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。

#include <stdio.h>
#include <stdlib.h>


main(){
    //联合体定义
    union{char c;int i;short s} un; //多个数据类型之间用;分号分隔

    un.c = 'm';
    un.i = 3;
    printf("%c\n",un.c);
    printf("%d\n",un.i);

    printf("%d\n",sizeof(un));//联合体的长度由类型种所有变量长度的最大长度绝对,例如un的最大长度 就是int的长度;

}

枚举

#include <stdio.h>


enum WeekDay
{
    Monday = 20,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};


int main(void)
{
    //int day;
    enum WeekDay day = Sunday;

    printf("%d\n",day);
    system("pause");
    return 0;

}

自定义类型

#include <stdio.h>
#include <stdlib.h>

typedef int hehe;
main(){
    hehe i =10; //hehe相当与int类型
    printf("%d \n",i);

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ang_qq_252390816

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值