C语言学习(6)第六章 指针 6.1-6.6

目录

6.1 内存

6.2 指针的相关概念

6.3 指针的定义方法

6.4 指针的分类

6.5 指针和变量的关系

6.6 指针和数组元素之间的关系

6.6.1 指针变量可以存放数组元素的地址

6.6.2 数组元素的引用方法

6.6.3 指针的运算


6.1 内存

存储器:存储数据的器件

1、外存:外存又叫外部存储器,长期存放,掉电不丢失。常见的有硬盘、flash、rom、光盘、磁带等

2、内存:内存又叫内部存储器,暂时存放,掉电丢失。常见的有ram、DDR(电脑的内存条)

(1)物理内存:实在存在的存储设备

(2)虚拟内存:操作系统虚拟出来的内存

操作系统会在物理内存和虚拟内存之间做映射,32位系统下,每个进程的寻址范围是4G,地址是0x00 00 00 00~ 0x ff ff ff ff

数据存放在物理内存,我们看到的是虚拟地址。

运行程序时操作系统会将虚拟内存4G进行分区,方便对内存进行管理

(1)堆区:在动态申请内存的时候,在堆里开辟内存

(2)栈区:主要存放局部变量

(3)静态全局区:

a.未初始化的静态全局区:静态变量(定义时用static修饰的变量),或全局变量,没有初始化的放在此区;

b.初始化的静态全局区:全局变量、静态变量、赋过初值的放在此区。

(4)代码区:存放程序代码

(5)文字常量区:存放常量

内存以字节为单位存储数据,可以将程序中的虚拟寻址空间看成一个很大的一维数组。

6.2 指针的相关概念

系统给虚拟内存的每个存储单元分配了一个编号,从0x 00 00 00 00 ~ 0x ff ff ff ff,这个编号称为地址,指针就是地址.

指针变量:用一个变量来存储一个地址编号。

32位平台下地址总线是32位的,所以地址是32位编号,指针变量要存放32位数据,所以是4个字节的。

注意:1、32位平台下任何类型的指针变量都是4个字节;

2、某一类型的指针变量只能对应类型的变量的地址,比如整形的指针变量,只能存放整形变量对应的地址。

字符变量char ch = 'b';ch占1个字节,它有一个地址编号:比如0x 00001fff,这个地址编号就是ch的地址。

整形变量 int a = 0x12 34 56 78;a占4个字节,故占有4个字节的存储单元,所以有4个地址编号,认为编号最小的那个是其地址,也就是说0x00002000是a的地址。

6.3 指针的定义方法

1、简单的指针变量

数据类型* 指针变量名;

比如:int* p;  //定义了一个指针变量p,这个p指向的是一个整形数据

*是用来修饰变量的,表示变量p是一个指针变量。

注意:如果一行定义多个指针变量,每个指针变量前面都要加*来修饰:

int *p,*q;//定义了两个指针变量p和q

int *p,q;//定义了指针变量p和整形变量q

int a = 0x1234abcd;

int* p;//定义了一个指针变量p,*修饰变量p,表示它是一个指针变量

p = &a;//把a的地址赋给p,&是一个取址符,可以说p指向a

int num;

num = *p;

在调用的时候,*是取值的意思,*p也就是p指向的变量,也就是a,所以num = *p和num = a的效果是一样的。

a的值是0x1234abcd,假设a的地址是0xbfe89868。

int main()

{

int a = 100, b = 200;

int* p_1, * p_2 = &b;

p_1 = &a;//p_1保存了a的地址,p_2保存了b的地址

printf("a = %d ,p_1 = %p, *p_1 = %d\n", a,p_1, *p_1);//*在调用的时候是取值的意思,*p_1也就是取p_1对应的值,也就是a

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

return 0;

}

运行结果是:

a = 100, p_1 = 00A8FBB0, * p_1 = 100

b = 200, p_2 = 00A8FBA4, *p_2 = 200

注意:p_1是局部变量,如果不赋初值,他的初值是随机的,称为野指针。

2、指针的大小:32位平台下任何类型的指针变量都是4个字节。

int main()

{

char* p1;

short int* p2;

int* p3;

long int* p4;

float* p5;

double* p6;

printf("sizeof(p1) = %d, ", sizeof(p1));

printf("sizeof(p2) = %d, ", sizeof(p2));

printf("sizeof(p3) = %d, ", sizeof(p3));

printf("sizeof(p4) = %d, ", sizeof(p4));

printf("sizeof(p5) = %d, ", sizeof(p5));

printf("sizeof(p6) = %d, ", sizeof(p6));

return 0;

}

sizeof(p1) = 4, sizeof(p2) = 4, sizeof(p3) = 4, sizeof(p4) = 4, sizeof(p5) = 4, sizeof(p6) = 4,

6.4 指针的分类

按指针指向的数据的类型来分

1、字符指针

字符型数据的地址,只能存放字符型变量的地址编号

char *p;

char ch;

p = &ch;

2、短整型指针

只能存放短整型变量的地址编号

short int *p;

short int si;

p = &si;

3、整型指针

只能存放整型变量的地址编号

int *p;

int i;

p = &i;

注意:多字节变量会占用多个存储单元,而每个存储单元都有对应的地址编号,C语言规定存储编号最小的那个编号是多字节变量的地址编号。

4、长整型指针

只能存放长整型变量的地址编号

long int *p;

long int li;

p = &li;

5、float型指针

只能存放float型变量的地址编号

float *p;

float f;

p = &f;

6、double型指针

只能存放double型变量的地址编号

double int *p;

double int df;

p = &df;

7、函数指针

8、结构体指针

9、指针的指针:保存指针变量的地址

10、数组指针

11、通用指针 void*:可以保存任何类型变量的地址

6.5 指针和变量的关系

1、指针可以存放变量的地址编号

int a = 100;

int* p;

p = &a;

p保存了a的地址,也可以说p指向了a。

2、在程序中引用变量的方法:

(1)直接通过变量的名称

(2)通过指针变量来引用变量

int *p;//在定义的时候,*不是取值的意思,而是修饰的意思,表示p是个指针变量

p = &a;//取a的地址给p赋值,p保存了a的地址

*p = 100;//在调用的时候*是取值的意思,等价于指针指向的变量a

int*p = &a;//可以在定义指针变量的同时给其赋值

//交换两个数的值

int main()

{

int a, b, tmp;

int* p1 = &a, * p2 = &b;

printf("输入a和b的值\n");

scanf_s("%d %d", &a,&b);

tmp = *p1;//tmp = a

*p1 = *p2;

*p2 = tmp;

printf("a = %d, b = %d\n", a, b);

return 0;

}

3、对应类型的指针只能保存对应类型数据的地址,如果想让不同类型的指针相互赋值时,需要强制类型转换,或者使用void*,可以保存任意类型数据的地址.

(1)*p 取值,如果是字符指针取1个字节,整形指针取4个字节,double类型指针取8个字节。

(2)p++指向下一个对应类型的数据,字符指针++指向下一个字符数据,指针存放的地址编号+1,而整形指针++指向下一个整形数据,指针存放的地址编号+4,double类比。

int main()

{

int a = 0x12345678, b = 0xabcdef66;

char* p1, * p2;

int* p = &a;

printf("%0x %0x\n", a, b);

p1 = (char*)&a; //将a的地址(整形数据的地址)转换为char*(字符数据的地址)

p2 = (char*)&b; //将b的地址(整形数据的地址)转换为char*(字符数据的地址)

printf("%0x %0x\n", *p1, *p2);

p1++;

p2++;

printf("%0x %0x\n", *p1, 0xff & (*p2));

printf("%0x", *p);

}

运行的结果是:

12345678 abcdef66

78 66 因为p1是字符指针,而p1指向的是a的最小编号地址,对应最后的一个字节,也就是78

56 ef

12345678

6.6 指针和数组元素之间的关系

6.6.1 指针变量可以存放数组元素的地址

变量存放在内存中,有地址编号,我们定义的数组是多个相同类型的变量的集合,而每个变量都占用内存空间,都有地址编号,所以指针变量也可以存放数组元素的地址

int a[5] = { 1,5,7,9,11 };

int* p;

int main()

{

int i;

for (i = 0; i < 5; i++)

{

p = &a[i];

printf("& a[%d] = %p\n", i, p);

p++;

}

return 0;

}

运行的结果是:

& a[0] = 0075A034

& a[1] = 0075A038

& a[2] = 0075A03C

& a[3] = 0075A040

& a[4] = 0075A044

可见,数组中的元素在内存中都是连续存储,地址是相邻的。整形数据占用4个字节的存储空间,编号最小的地址编号是它的地址,所以相邻的整形数据的地址编号相差4。

整形指针++指向下一个整形变量,指针存放的地址编号+4。

6.6.2 数组元素的引用方法

1、数组名[下标]

2、指针名+下标

c语言规定,数组的名字就是数组的首地址,即第0个元素的地址,即&a[0],是个常量。

int a[5]; //a 就是a[5]这个数组的首地址。

int *p;

p = a;

p[2] = 100; //相当于a[2] = 100;注意:p和a不同,p是个指针变量,a是个常量(a[0]的地址)

int main()

{

int a[5];

printf("a = %p\n", a);

printf("a = %p\n", &a[0]);

return 0;

}

a = 00CFFE70

a = 00CFFE70

可见,a 就是 a[0]的地址。

3、指针变量的运算加取值的方法

int a[5];

int *p;

p = a; //p指向a[0]

*(p+2) = 100; //相当于a[2] = 100;p是第0个元素的地址,p+2是第2个元素的地址

4、通过数组名加取值的方法

int a[5];

*(a+2) = 100; //相当于a[2] = 100;a是第0个元素的地址,a+2是第2个元素的地址

int main()

{

int a[5] = { 1,4,6,9,11 };

int* p;

p = a; //p指向a[0]

printf("a[2] = %d\n", a[2]);

printf("a[2] = %d\n", p[2]);

printf("a[2] = %d\n", *(p+2));

printf("a[2] = %d\n", *(a+2));


printf("p = %p a = %p\n", p,a);

printf("p+2 = %p a+2 = %p", p + 2,a+2);

return 0;

}

a[2] = 6

a[2] = 6

a[2] = 6

a[2] = 6

p = 004FFC4C  a = 004FFC4C

p + 2 = 004FFC54  a + 2 = 004FFC54

6.6.3 指针的运算

1、指针可以加一个整数,往下指几个它指向的变量,结果还是个地址

char a[5];

char* p;

p = a;

p+2; //p是a[0]的地址,那么p+2是a[2]的地址

如果p存放的地址编号是2000,那么p+2存放的地址编号是2002

2、两个相同类型的指针可以比较大小,但只有同时指向一个数组的元素时比较大小才有意义,下标小的元素的地址比下标大的元素的地址要小

int main()

{

int a[10];

int* p, * q;

p = &a[1];

q = &a[6];

printf("& p = %p, & q = %p\n", p, q);

if (p < q) printf("p < q ");

else if (p = q) printf("p = q");

else printf("p > q");

return 0;

}

运行的结果是:

&p = 012FFD54, & q = 012FFD68

p < q

3、两个相同类型的指针可以做减法,但只有同时指向一个数组的元素时做减法才有意义,减法的结果是两个指针指向的中间有多少个元素

int main()

{

int a[5];

int* p, * q;

p = &a[1];

q = &a[4];

printf("&p = %p, &q = %p\n", p, q);

printf("q - p = %d\n", q - p);

return 0;

}

运行的结果是:

&p = 006FF8E8, & q = 006FF8F4

q - p = 3

注意:q和p之间相隔了12个字节,但做减法的结果是得到中间的元素个数,是整形而不是地址编号。

4、两个相同类型的指针可以相互赋值,如果类型不同的元素想要相互赋值,必须进行强制类型转换

int *p;

int *q;

int a;

p = &a; //p指向a

q = p; //用p的值给q赋值,那么q也指向a了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值