C语言中的指针

0、介绍

int a = 10;

定义了一个 int 变量 名字 a 把 10 赋值 给 a

变量 a 的值是10

给了一个 4字节的 存储空间,有个编号

a = 20;//把 20 放进 a 代表的那个空间//写入

printf("a = %d",a);//读出

找到存储 a 的那个空间在哪,就是找到那个编号,就是地址

1、指针

为了方便管理内存,按字节为单位进行编号的。

地址: 分配给每个变量的内存单元都有一个编号,编号就是我们所说的地址,在C语言中指针和地址差不多。

变量的地址/指针 是可以直接打印出来的,%p

&取地址符

&变量名 --->取该变量的地址

2、指针变量

用来保存“地址/指针”的 “变量”,一般是用来保存 其他变量的地址/指针。

指针变量的 定义语法:

指向的变量的类型 * 指针变量名 [= 它指向的那个变量的地址];

指向的变量 : 指针变量保存了谁的地址(内存编号),就说指针变量指向了谁

例:

int a = 10;

想要用 指针变量 来保存 a 的地址

就是想指向 a, a 的类型是 int

-----> int * p = &a;

* 是指针变量的声明符

int 是 a 的类型是

p 是指针变量,&a 取 a 的地址 是 p 的初始值。

练习:

初始化一个字符型的变量,定义一个指针变量指向它

char c = '1';

int a;// 没有初始化,a的值是什么 随机

char * p;// 没有初始化,p指向哪,不知道, 这个情况我们一般称为 “野指针”

p = &c;// 赋值之后, p 指向了变量c , 现在就不是 “野指针”

//这两条语句就相当于 char * p = &c;

3、与指针相关的两个运算符

&取地址符单目运算符

&对象名 ----> 取这个对象的地址

*解引用运算符单目运算符

*对象名 ---->地址对应的那个对象

int a = 10;

*(&a) <-->a

int * p = &a;

*p <--> *(&a) <-->a

例:

float b = 3.1415;

float * q = &b;//指针变量q 保存了 b 的地址, q 指向了 b

// *q <--> b

printf("%f\n",b);//打印 b 的值 3.1415

printf("%f\n",*q);//打印 b 的值 3.1415

*q = 2.048;//相当于给 b 赋值

printf("%f\n",b);//打印 b 的值 2.048

printf("%f\n",*q);//打印 b 的值 2.048

分析:

double d = 1.234;

double * p;// 野指针 避免出现野指针

*p = 5.4321;// 段错误内存非法访问 访问不到 *p

printf("%f\n",d);

printf("%f\n",*p);

4、空指针

如果一个指针变量 的值 为 NULL,就把这个指针称为空指针

int * p = NULL;//p 是空指针

//int *p;

//p = NULL;

*p = 100;// 和 野指针 一样, 也不能访问空指针指向的对象

练习:

写一个函数,用来交换两个整数的值

void swap1(int a,int b)//int a = 3,b = 5;

{

int t = a;

a = b;

b = t;//a = 5; b = 3; 临时变量 , 用完就gg, 而且和 主调函数实参没联系

}

void swap2(int * a,int * b)//成功//int * a = &a2 int * b = &b2

{

int t = *a;//t = a2;t = *(&a2);

*a = *b;//a2 = b2; *(&a2) = *(&b2);

*b = t;//b2 = t;*(&b2) = t;

}

void swap3(int * a,int * b)//int * a = &a3 int * b = &b3

{

int *t = a;// t ---> at = &a3

a = b;// a ---> ba = &b3

b = t;// b ---> tb = &a3

}

int main()

{

int a1 = 3,a2 = 3,a3 = 3;

int b1 = 5,b2 = 5,b3 = 5;

swap1(a1,b1);

printf("a1 = %d,b1 = %d\n",a1,b1);

swap2(&a2,&b2);

printf("a2 = %d,b2 = %d\n",a2,b2);

swap3(&a3,&b3);

printf("a3 = %d,b3 = %d\n",a3,b3);

return 0;

}

分析:

void test(int *p)

{

printf("data = %d\n",data);// 没有 在 data 的作用域 , 不能通过名字直接访问

*p = 300;

printf("data = %d\n",*p);// 300

}

int main()

{

int data = 250;

printf("data = %d\n",data);

test(&data);

printf("data = %d\n",*p);//

}

--------------------------

int * test()

{

int data = 125;

return &data;

}

int main()

{

int *p = test();

printf("data = %d\n",*p);

}

****注意: 不能返回局部变量的地址

5、数组和指针

数组中每个元素和普通变量一样,也会在内存占个空间,有地址,并且数组元素之间的地址是相邻。

int s[8];

定义了一个数组,名字是s 有8个 int 的元素;

定义一个指针变量,用来保存 s[0] 的地址

int * p = &s[0];

*p <--> s[0]

数组名有些情况下可以当作数组的首地址(第一个元素的地址,&s[0])

假设 &s[0] ---> 0xffff1234567898

&s[1] ---> 0xffff123456789c

&s[0] + 4 == &s[1] ??? 有问题

if(&s[0] + 4 == &s[1])//加了 4 * sizeof(int) 个字节

{

printf("哦嚯,没错\n");

}

else

{

printf("哦嚯,错了\n");

}

指针做加减:

p + i(p 是 一个指针,i 是整数)

***不是简单数值上面的加减,而是加减 i 个 p指针的步长。

指针的步长: 是它(指针)指向的类型那个所占的内存字节数。

如:

char *p;// p 的步长 1

int *q;// q 的步长 4

练习:

int a[10] = {10,9,8,7,6,5,4,3,2,1};

int * p = a;//int * p = &a[0]; p = &a[0]

printf("%d\n",*p);//10 *(&a[0])

printf("%d\n",*p+1);//11(*(&a[0])) + 1

printf("%d\n",*(p+1));//9*(&a[1])

printf("%d\n",*(p+3));//7*(&a[3])

printf("%d\n",*(a+3));//7*(&a[3])

int *q = &a[3];// q = &a[3]

printf("%d\n",*q);//7*(&a[3])

printf("%d\n",*(q+5));//2*(&a[8])

对于数组 xxx a[n];// xxx 代表类型

总结:

(1) 访问数组元素可以用下标法,也可以用指针法

a[i] <----> *(a+i)

0<= i < n 整数

(2) 对于指针p 注意类型

已知 p = &a[i];

*(p+j) <----> *(a+i+j) i + j 不能超过范围

--推导:*(p+j) <----> *(&a[i] + j) <----> *(&a[i+j]) <----> a[i+j]

练习:

分析以下:

输入 hello 输出啥

int main()

{

char s[10];

//scanf("%s",s);// 相当于 &s[0]

scanf("%s",s+2);//_-_-h-e-l-l-o-_ _

printf("%s\n",s+2);//hello s[2] 开始输出 + 2 * sizeof(char)

printf("%s\n",s+3);//ello s[3] 开始输出

return 0;

}

6、数组作为函数参数

int array_sum(int a[],int n)//int a[] <===> int *a = aaa = &aaa[0]

{

int sum = 0;

for(int i = 0;i < n;i++)

{

sum += a[i];

}

return sum;

}

int array_sum(int *a,int n)//int *a = aaa = &aaa[0];int *a; a = aaa;

{

int sum = 0;

for(int i = 0;i < n;i++)

{

sum += a[i];// a[i] <===> aaa[i]//sum += aaa[i];不能这么写

}

return sum;

}

int main()

{

int aaa[10] = {1,3,5,7,8,10,12,2,4,6};

int sum = array_sum(aaa,10);//调用的时候不用 []

printf("该数组的最大值是%d\n",max);

}

//*(a) <----> *(&aaa[i]) <----> *(&aaa[i]) <----> aaa[i]

注意:

int a[] 相当于 int *a ,a不是一个数组,只是退化成一个指针

调用函数的时候,实参是数组首地址

-------------------------------

指针变量 所占内存空间大小是多少?

int * a;

char * b;

float * f;

由它指向的对象类型//错的

指针变量存的是 地址

地址有范围(寻址范围):

4G:

0 ~ 4 * 1024 * 1024 * 1024 - 1

0 ~ 4 * 2^30 - 1

地址范围:0 ~ 2 ^ 32 - 1

32位 是 几个字节 4字节

8G:

0 ~ 8 * 1024 * 1024 * 1024 - 1

0 ~ 8 * 2^30 - 1

0 ~ 2 ^ 33 - 1

这种情况下,指针变量如果还是4字节,无法保存地址,

所以 在 32 位的机器,指针变量占 4字节,64 位 占 8字节

7、作业:

1、写一个函数,求一个字符串的长度

"df\0ghjkl\0\0"

依次比较各个元素,直到匹配到'\0',就结束,没有就继续++

最后返回 个数

2、写一个函数,求两个字符串哪个大

只要A遇到'\0' B还没遇到'\0' B 大

"df\0ghjkl"

"dfghjkl"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

新棉衣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值