C语言指针复习

一、指针

1、指针概念:

地址:内存中每个字节单位都有一个编号(门牌号)

指针:指针就是地址

指针变量:用于存放地址的变量就叫做指针变量

2、格式

存储类型    数据类型 *指针变量名
int *p //定义了一个指针变量名
例子:
int a_p = 10;
int *p = &a_p;
char b_p = 'h';
char *q = &b_p;

3、指针操作符

* : 取内容,取地址里面存放的内容
& : 取地址符,取变量的地址
*&a == a :* &互为互逆运算
&*a  a是变量 错误使用
     a是指针 则可取到内容的地址

4、初始化

指针变量在使用前不仅要定义还要初始化,未初始化的指针变量不能随便使用,会产生野指针
1、将普通变量地址赋值给指针变量
int p_p = 5;
(1) int *p = &p_p;
(2) int *P = NULL;
    p = &p_p;
    printf("%d %d\n",p_p,*p);// 5 5
    printf("%p %p",&p_p,p);//打印的都是p_p这个变量的地址
2将数组的首地址赋值给指针变量
char str[10= "hello";
(1) char *p = str;//指针指向的数组的首地址,即指向了字符'h'
3将指针变量里面保存的地址赋值给另一个指针变量
int num = 2;
int *p = #
int *q = p;

5、指针运算

5.1算数运算:

char str[32] = "world";
char *p = str;
 p++;// 指针向高地址方向移动一个数据单位,指针指向发生变化
 p--; // 指针向低地址方向移动一个数据单位,指针指向发生变化

int *p; p++;//移动了四个字节
double *p; p++;//移动了八个字节

p+n: 访问高地址方向第n个数据的地址,指针指向不发生变化
p-n: 访问低地址方向第n个数据的地址,指针指向不发生变化

问:p 到 p+4 之间相隔了多少个地址// n: 数据的个数,体现不出来字节的大小
偏移了多少地址:n * sizeof(数据类型)

俩个地址之间的差 = 两个地址之间相隔元素的个数
int num = 100;
double sum = 200;
int *p1 = NULL, *p2 = NULL;
double *q1 = NULL, *q2 = NULL;
p1 = #
p2 = p1 + 3;
q1 = ∑
q2 = q1 + 4;
printf("p1 = %p, p2 = %p\n", p1, p2);
printf("p2-p1 = %d\n", p2 - p1);
printf("q1 = %p, q2 = %p\n", q1, q2);
printf("q2-q1 = %d\n", q2 - q1);
// 验证了两指针相减的结果值不是地址量而是一个整数值

5.2关系运算

> < == != 
指针之间的关系运算比较的是它指向地址的高低
指向高地址的指针大于指向低地址的指针
char str[10]="hello"
char *= &str[1];
cahr *= &str[2];
> p; 为真

注意:指向不同类型的数组指针关系运算没有意义,指向不同区域的指针关系运算也没有意义(同一个数组进行比较)

  1. 指针的大小

32位操作系统  指针4字节

64位操作系统  指针8字节

7、指针修饰

1. const常量化
    
    1) const int num = 10;
        int const num = 10;
        num = 20; // 错误的,进行了const修饰,因为num不能改变
        //这样可以,因为const修饰num,并没有修饰*p
        int *p=&num;
        *p=20;//可以改变
    2) const int *p;  // 修饰 *p, 指针指向的内容不能修改,指针的指向可以修改的
        int const *p;//修饰的也是*p
        
        int a = 10;
        const int *p = &a;
        1.1) *p = 20; // 错误,因为 *p 被修饰
        1.2) int b = 20; p = &b // 可以
    3)int *const p//修饰的是p,可以修改内容 不能修改指向
        int a = 10;
        int b = 20;
        int *const p = &a;
            2.1) *p = 20; // 可以
            2.2) p = &b; // 不可以, p 被 const 修饰,所以不能修改
2.void 
    void a;//不允许修饰变量
    void *p;//p是一个任意类型的指针
    使用场景:函数参数或函数返回值
    注意:通过void类型指针进行取内容时,需要对地址进行强转,在哪里使用
    在哪里强制转换

8、二级指针

一级指针:存放变量的地址

二级指针:存放一级指针的地址

格式:存储类型 数据类型 **指针变量名
int a=10;
int *p = &a;
int **q = &p;
访问a的值:
a *p *(*q)

二、指针与数组

直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存放变量地址的变量去访问变量(通过指针访问)

  1. 指针与一维数组
int a[5]={1,2,3,4,5};
int *p=a;//a为数组首地址

直接访问:通过数组名访问

间接访问:通过指针来访问

地址元素对应

地址

内容

元素

p

a

&a[0]

3

a[0]

*a

p[0]

*p

p+1

a+1

&a[1]

4

a[1]

*a+1

p[1]

*p+1

p+2

a+2

&a[2]

5

a[2]

*a+2

p[2]

*p+2

p+3

a+3

&a[3]

6

a[3]

*a+3

p[3]

*p+3

p+4

a+4

&a[4]

7

a[4]

*a+4

p[4]

*p+4

a和p本质上不同,a是地址常量,p是变量,a不能执行++操作,但是p可以

访问数组元素a[i]的值:
直接访问:a[i] *(a+i)
间接访问:p[i] *(p+i)

访问数组元素a[i]的地址:
直接访问:&a[i] a+i
间接访问:&p[i] p+i

例子

int a[3]={1, 2, 3};
int *p = a;
printf("%d\n", *p++);  // 1  再打印一次就是 2
printf("%d\n", *a++);  // 错误,a是地址常量
运算方法:
    1)++和*都是单目运算符,优先级相同
	 2)单目运算符从右向左运算
    
    *(p++); // 1
    (*p)++; // 打印出来1,实际上第一个数变成2;
    ++*p; // 打印出来是2,自加完之后的值
    ++(*p)// 同上
    *++p; // 先移动再取值,2
    *(++p)// 同上
  1. 指针和二维数组
直接访问
int a[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};  // a:第一行的首地址,a+1:第二行的首地址
在 a 前面加 *,表示将行地址降级成为列地址
*a;表示把行地址降级为列地址。
*a:第一行第一个地址
*a+1:第一行第二例地址 
*(a+1):第二行第一列地址
*(a+1)+1:第二行第二列地址

访问数组元素地址(a[i][j]的地址)
printf("%p\n",&a[i][j]);
printf("%p\n",*(a+i)+j);
printf("%p\n",a[i]+j);

访问数组元素值:
printf("%d\n", a[i][j]);
printf("%d\n", *(*(a+i)+j));
printf("%d\n", *(a[i]+j));

直接访问元素对应

地址

数组

元素

a[0]

&a[0][0]

*a

1

a[0][0]

*(*a)

*a[0]

a[0]+1

&a[0][1]

*a+1

2

a[0][1]

*(*a+1)

*(a[0]+1)

a[1]

&a[1][0]

*(a+1)

3

a[1][0]

*(*(a+1))

*a[1]

a[1]+1

&a[1][1]

*(a+1)+1

4

a[1][1]

*(*(a+1)+1)

*(a[1]+1)

a[2]

&a[2][0]

*(a+2)

5

a[2][0]

*(*(a+2))

*a[2]

a[2]+1

&a[2][1]

*(a+2)+1

6

a[2][1]

*(*(a+2)+1)

*(a[2]+1)

间接访问
int a[2][3]={1, 2, 3, 4, 5, 6};
int *= *a;
for (size_t i = 0; i < 2; i++)
{
    for (size_t j = 0; j < 3; j++)
    {
        printf("%p\n", &a[i][j]);
    }
}
    
printf("%p %p\n", a, a+1);  // 加了一行
printf("%p %p %d\n", p, p+1, *(p+5));  // 加了一个

  • 数组指针
定义:本质是指针,指向的是数组(又叫行指针)
格式:存储类型    数据类型    (*指针变量名)[列数]

int a[2][3] = {1,2,3,4,5,6};
int (*p)[3] = a;
for (size_t i = 0; i < 2; i++)
{
    for (size_t j = 0; j < 3; j++)
    {
        printf("%p\n", *(p+i)+j);
        printf("%d\n", *(*(p+i)+j));
        printf("%p\n", *(a+i)+j);
        printf("%d\n", *(*(a+i)+j));
    }
}
p: int (*)[n];   运算n个n个运算
p可以代替a进行元素访问,但是本质不同

访问数组元素地址(a[i][j]的地址)
*(p+i)+j)
p[i]+j
访问数组元素值:
*(*(p+i)+j)
*(p[i]+j)

大小:
sizeof(p) // 4
因为本质上还是指针,所以大小都是4字节

四、指针数组(本质还是数组)

定义:定义:本质是数组,里面存放的是指针
格式:存储类型 数据类型 *数组名[元素个数]
int *arr[2];
应用:
    1) 用于存放普通变量的地址
    int a = 10, b = 20, c = 30;
    int *arr[3] = {&a, &b, &c};
    
    访问b的地址;
		arr[1]  *(arr+1)

    访问b的值:
	    *arr[1]   *(*(arr+1)) 
2) 用于存放二维数组的每行第一个元素的地址(列地址)
int a[2][3] = {1, 2, 3, 4, 5, 6};
int *p[2] = { a[0], a[1] };

访问a[1][2]的值:
*(p[1]+2) *(*(p+1)+2)

访问a[1][2]的地址:
p[1]+2	*(p+1)+2

sizeof(p) = 8; // 因为 p 中包含了两个指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

满山的猴子我的腚最红

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

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

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

打赏作者

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

抵扣说明:

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

余额充值