C语言指针入门,图解+实例

目录

1.指针是什么

那么地址,内存,指针又有怎样的关系?

如何去创建指针变量,并且存放地址呢?

内存空间有多大?

2.指针类型

那么不同类型的指针有什么作用呢?

3.野指针 

成因 :

4.指针运算

4.1指针±整数

4.1指针-指针

4.3指针的关系运算

5.指针和数组

6.二级指针

 7.指针数组


 

1.指针是什么

指针是内存中一个最小的单元编号,也就是地址

通常我们口中说的指针其实是指针变量:变量中存放着地址

那么地址,内存,指针又有怎样的关系?

为了对内存更方便的管理使用,我们把内存划分为一个一个小格子

一个内存单元是1个字节

C语言指针入门,图解+实例_第1张图片

为了很好的定位到内存单元,我们给每个单元一个编号,这个编号也就是地址

指针变量就是存放我们需要的地址,方便我们需要时直接使用

如何去创建指针变量,并且存放地址呢?

int a=0;

int* p=&a;//此处*代表创建的是指针变量,给p赋值的是a的地址

//int* 中int 告诉我们指针变量p指向的对象是int类型

下面我们以这段简单代码举例

int main(){
int a = 10;
int* p = &a;
return 0;
}

 这其中原理可以用图表示C语言指针入门,图解+实例_第2张图片

注:指针变量p存储的是a变量的首地址

因为我们只需要知道a变量首地址,依次向后+1,就可以知道a变量所有的地址

总结:地址标识内存,指针存放地址,而创建指针又需要新的内存空间

内存空间有多大?

这跟电脑是32位机器还是64位机器有关,对于32位机器,它有32根地址线,每根地址线在寻址时产生高电压和低电压对应的是1和0,32根就是2^32Byte=2^22KB=2^12MB=4GB(内存空间)

C语言指针入门,图解+实例_第3张图片

 在2^32种情况中,每种情况是32个0,1组成,是32个bit位,4个字节,而一个16进制数字占0.5个字节,所以地址是8个16进制数字组成,如

C语言指针入门,图解+实例_第4张图片

这样的地址可以标识一个1字节内存空间,2^32种地址标识2^32字节也就是4GB空间。

注:这里32位机器地址占4字节,但它代表的内存空间占1字节

总结:

  • 指针用来存放地址,地址唯一标识一块内存空间
  • 指针大小在32位平台是4字节,在64位平台是8字节

2.指针类型

不同类型的指针大小是否相同?

#include
int main() {
	int a = 0;
	int* pa = &a;

	char b = 'a';
    char* pb = &b;
	printf("%d\n", sizeof(pa));
	printf("%d", sizeof(pb));
	return 0;
}

结果都是4,我们发现不同类型的指针他们的大小是一样的依然是32位平台是4字节,在64位平台是8字节

那么不同类型的指针有什么作用呢?

我们来看这样一段代码

#include
int main() {
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;
	
	return 0;
}

 进行调试

C语言指针入门,图解+实例_第5张图片

我们看到给a赋16进制数时,a所在地址的内存中是11223344

C语言指针入门,图解+实例_第6张图片

 我们发现用int型指针pa间接访问a时,将*pa改为0,则a所在地址的内存全变成0

接下来我们将int型指针换成char型指针,再次进行调试

C语言指针入门,图解+实例_第7张图片

C语言指针入门,图解+实例_第8张图片

 结果发现用char型指针间接访问a时,将*pa改为0,结果a所在地址的内存只有44变成了00

结论1指针类型决定了,指针被解引用的时候,访问的权限。整形指针解引用访问4个字节,字符指针解引用访问1个字节

C语言指针入门,图解+实例_第9张图片

接下来我们再看一个例子:

#include
int main() {
	int a = 0;
	int* pa = &a;
	char* pb = &a;

	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	printf("%p\n", pb);
	printf("%p\n", pb + 1);
	return 0;
}

 C语言指针入门,图解+实例_第10张图片

 根据结果发现整形指针+1相当于一次跳过一个整形长度,地址+4;字符指针+1相当于一次跳过一个字符长度,地址+1

用图解释

C语言指针入门,图解+实例_第11张图片

 结论2:指针类型决定了指针向前或向后移动的距离

  • (int*)pa  +1    ——>地址  +1×sizeof(int)=地址  +4
  • (char*)pb   +1    ——>地址  +1×sizeof(char) =地址  +1

3.野指针 

概念:野指针就是指针指向位置是未知的,随机的

成因 :

1.指针未初始化

int main(){
int* p;//指针未初始化
*p = 0;
return 0;
}

我们创建了一个局部变量指针,但指针未赋地址,默认为随机值,这是不可知的

2.指针越界访问 

int main()
{
    int a[5]=0;
    int i=0;
    int* p=a;
    for(i=0;i<10;i++)
    {
        printf("%d ",*p);//指针指向的范围超出数组a的范围,造成野指针
        p++;
    }
    return 0;
}

3.指针指向的空间释放

#include
int* test() {
	int a = 10;//a的生命周期在test()中,出了这个函数自动销毁
	return &a;
}
int main() {
	int* p = test();
	*p = 20;
	return 0;
}

4.指针运算

4.1指针±整数

#include
int main() {
	int a[5] = { 1,2,3,4,5 };
	int i = 0;
	int* p = a;
	for (i = 0;i < 5;i++) {
		printf("%d", *p);
		p++;
	}
	return 0;
}

 这里p+1是首元素相后移动一个元素的地址,循环起来(+1向后一个元素),逐个访问每个元素地址

4.1指针-指针

指针-指针得到的是指针之间元素的个数,注:此处必须是大指针-小指针才能得到指针之间元素个数,否则得到负值。

前提:两个指针必须指向同一块空间(例如同时指向数组)

#include
int main() {
	int a[10] = { 0 };
	printf("%d\n", &a[9] - &a[0]);//指针同时指向数组
	return 0;
}

结果为9,说明中间有九个元素

C语言指针入门,图解+实例_第12张图片 

可用于求字符串长度,例如这段代码

#include
int my_strlen(char* str) {
	char* start = str;
	while (*str) {
		str++;
	}
	return str - start;//指针-指针得到中间元素个数
}
int main() {
	char str[] = "abcdef";
	int len = my_strlen(str);
	printf("%d", len);
	return 0;
}

4.3指针的关系运算

对于数组,数组序号由低到高变化,地址也由低到高变化,高地址大于低地址

例如下面这段,利用指针给数组赋值

int main() {
	int a[5];
	int* p;
	for (p = &a[5];p > &a[0];) {     //这里用到了指针的比较
		*--p = 0;
	}
	return 0;
}

原理是从最后一个元素的后面那个内存位置的地址,依次向左和0号比 

C语言指针入门,图解+实例_第13张图片

 

 我们也可以这样写的代码

int main() {
	int a[5];
	int* p;
	for (p = &a[5-1];p >= &a[0];p--) {
		*p = 0;
	}
	return 0;
}

C语言指针入门,图解+实例_第14张图片 与上面不同的是,最后跳出循环判定的是首元素之前那个内存位置的地址

 但有时这样写代码不被允许,

标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较

C语言指针入门,图解+实例_第15张图片

 通俗来说就是,尽量那指针p与最后一个元素a[4]后面的指针p1进行比较,不要与a[0]前的那个内存位置的指针进行比较


5.指针和数组

先看一个例子

#include
int main() {
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", a);
	printf("%p\n", &a[0]);
	return 0;
}

C语言指针入门,图解+实例_第16张图片

结果发现地址是一样的

结论:数组名为数组首元素地址


 但是有一个例外

如果数组名为数组首元素地址,那么用sizeof去求数组名(首元素地址)大小,得到的应该是4字节(32位环境),但事实真是这样吗?

#include
int main() {
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", sizeof(a));
	return 0;
}

C语言指针入门,图解+实例_第17张图片

结果我们发现结果是40,他似乎把所以数组元素地址大小都加了起来。

例外1:sizeof(数组名),数组名表示整个数组,计算的是整个数组大小

例外2:&数组名,数组名表示整个数组,取出的是整个数组的地址


 我们把数组名,&数组名做一个对比

#include
int main() {
	int a[10] = { 0 };
	printf("%p\n", a);
	printf("%p\n", a+1);

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

	printf("%p\n", &a);
	printf("%p\n", &a+1);
	return 0;
}

C语言指针入门,图解+实例_第18张图片

 我们发现这个结果,数组名与&首元素代表的都是首元素地址,首元素地址+1代表跳过一个元素

而&数组名代表整个数组的地址,+1则跳过整个数组


6.二级指针

指针的指针

我们用例子来说明

int main() {
	int a = 10;
	int* p = &a;//p是指针变量(一级指针)
	int** pp = &p;//pp是一个二级指针
return 0;
}

用之前的图来说明 

C语言指针入门,图解+实例_第19张图片

 简单的说明就是C语言指针入门,图解+实例_第20张图片


 

 7.指针数组

  • 整形数组——>存放整形的数组
  • 字符数组——>存放字符的数组
  • 指针数组——>存放指针的数组

定义方法与普通数组一样 

int main() {
	int a[5] = { 0 };//整形数组

	char b[5] = { 'w' };//字符数组

	int a1 = 0;
	int a2 = 0;
	int a3 = 0;
	int a4 = 0;
	int a5 = 0;
	int* c[5] = { &a1,&a2,&a3,&a4,&a5 };//指针数组
	return 0;
}

 

你可能感兴趣的:(C语言,c语言,开发语言,c++)