学习c语言,不学指针,等于没学。可见指针在c语言中有多重要。指针是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针定义:指针也就是内存地址,指针变量是用来存放内存地址的的变量。指针的作用是:通过指针不仅可以对数据本身,还可以对存储数据的变量地址进行操作。不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。指针使得 C 语言能够更高效地实现对计算机底层硬件的操作,而计算机硬件的操作很大程度上依赖地址,指针便提供了一种对地址操作的方法,在一定意义上,指针是c语言的精髓.所以一定要耐心看完。
此篇文章将从以下几点初步介绍指针,后续还会陆续总结更新。
1.指针是什么
2.指针和指针类型
3.野指针
4.指针运算
5.指针和数组
6.二级指针
7.指针数组
1.指针是什么
对指针理解的两个要点:
1、指针是内存单一个最小单元的编号,也就是地址。最小单元编号是一个字节。
2、我们通常说指针变量,就指指针是一个变量,用来存放内容地址的变量总结:指针就是变量,这个变量是地针。
那么我们就可以这样理解。
(1)了解内存
内存是存储区域,它是一块大的空间这个空间的大小可能是4G,8G,32G,64G,128G等。
我们给每个字节编上一个编号,这个编号就是地址,这个地址就是编足指针就像我们在学校住的寝室一样.我们的寝室在生活区A栋330这就是一个地址编号。只是叫法不同一样,但本质都是一样的。
#include<stdio.h>
int main()
{ int a = 0;//创建变量的本质是向储存空间,变量a申请4个字节的内存空间。
char c = 'w';//本质是申请1字节空间,申请空间大小由变量类型决定;
int* pa = &a;//创建指针变量pa,pa指向a的地址
char* pc = &c;
printf("a申请的内存空间大小=%d\n", sizeof(a));
printf("a的地址->%p\n", &a);
printf("c申请的内存空间大小=%d\n", sizeof(c));
printf("c的地址->%p\n", &c);
printf("%p\n", &pc);//pc的地址
printf("%p\n", &pa);//pa的地址
printf("%d\n", sizeof(pa));//指针变量所占的内存空间大小
printf("%d\n", sizeof(pc));//都是4字节,不论类型;
return 0;
}
.
如何编址?
经过仔细计算我们发现对于32位机器,假没有32根地址线,那么每根地址线在寻址时有高电平和低电平就是(0或1)两种情况;
那么32根地址线产生的地址:
32个0或1组成一个字节的地址
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000011
..........................
11111111111111111111111111111110
11111111111111111111111111111111
那就有2的32次方个地址
每个地址标识一个字节,有(2^32Byte==2^32/1024kB==2^32/1024/1024MB=2^32/1024/1024^1024GB=4GB就空间进行编址。
64位机器由64个0或1。
总结:
这里我们明白,在32位机器上,地址是32个0或1组成的二进制序列,也就是32个比特位组成。每8个bit等于一个字节,那地址就得用4个字节空间来存储。所以一个指针变量的大小为4个字节。同理64位机器上,一个指针变量的大小为8个字节,地址是唯一标示一个内存单元的。
取地址符号和解用符号
#include<stdio.h>
int main()
{
int a = 9;
int* pa = &a;//&取地址符号,取出a的地址放在指针pa中;
printf("第一次对a操作->%d", *pa);//*解引用符号取出a的值;
*pa = 10;//pa = a的地址 *pa = a的地址里的值;
printf("%d", *pa);
return 0;
}
2.指针和指针类型
对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小。比如int整型变量分配4个字节,char字符型变量分配1个字节等等。被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方。指针作为一个变量用来存放地址,可以通过指针来改动变量。
#include<stdio.h>
int main()
{
int a = 4;
int* pa = &a;
char* pc = &a;
double b = 0;
double* pd = &a;
printf("%d", sizeof(b));
return 0;
}
从上面的程序抱错中我们int *类型的值不能被其他类型的指针使用,每一种指针类型都有对应的数据类型。
#include<stdio.h>
int main()
{
int a = 4;
int* pa = &a;
char c = '0';
char* pc = &c;
double b = 0;
double* pb= &b;
printf("pa=%p\n", pa);
printf("pa+1=%p\n", pa+1);
printf("pc=%p\n", pc);
printf("pc+1=%p\n", pc + 1);
printf("pb=%p\n", pb);
printf("pb+1=%p\n", pb +1);
return 0;
}
3.野指针
接下来我们来讲野指针,从前面我们知道指针就是地址,既然指针等于地址,那么可得野指针等于野地址,那什么是野地址呢?就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)。
3.1野指针的成因
(1)指针未初始化。
(2)指针越界访问。
(3)指针指向的空间释放了。
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p =&arr;
//int* pa;//这里创建了一个指针但是没有初始化,指向的地址不明。
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);//这里求数组arr元素的个数,sz=10
for (i = 0; i <=sz/*sz=10*/; i++)
{
*p = i;//访问了下标为0 1 2 3 4 5 6 7 8 9 10 指针访问11个数组元素造成指针越界运行报错。
printf("%d ", *p);
p++;
}
return 0;
}
3.2如何规避野指针
1.指针初始化
2.小心指针越界
3.指针指向空间释放,及时置NULL
4.避免返回局部变量的地址避免返回栈空间的地址
5.指针使用之前检查有效性
4指针运算
指针 + - 指针
#include<stdio.h>
#define N_VALUES 5
float values[N_VALUES];
float* vp;
int main()
{
for (vp = &values[N_VALUES - 1]; vp >= &values[0];)
{
*--vp = 1;
printf("%0.0lf\n", *vp);
}
return 0;
}
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
int a = 23000;
char c = 'wfee';
// printf("%d\n", &a - &c);//err两个指针相减的前提是:指针指向的同一块连续的空间
return 0;
}
指针的关系运算
规定:允许指何数组元素的指针与指向数组元素最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素,之前的那个内存位置的指针进行比较。指针一指针等于指针和指针之间的元素个数,只内在一个数组内使用同一块连续空间。
5.指针和数组
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;//数组名就是数组首元素的地址arr->&arr[0];
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;//指针p指向arr[0],p+i后指针指向arr[i]的位置并将其赋值为i+1;
printf("%d\n", arr[i]);
}
return 0;
}
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//数组名就是数组首元素的地址arr->&arr[0];
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;//指针p指向arr[0],p+i后指针指向arr[i]的位置,并将这个位置的值赋为i+1;
printf("%d\n", *(p+i));
}
return 0;
}
运行上面两段代码,我们可以发现数组和指针不是一个东西.数组能够存放一组数,连续的空间,数组的大小取决于元素个数指针是一个变量,是存放地址的,4/8个字节
联系就是:
数组名是地址(指针),数组把首元素的地址,交给一个指针变量后可以通过指针来访问数组。
6.二级指针
三级指针用来存放二级指针的地址
#include<stdio.h>
int main()
{
int a = 101;
int* pa1 = &a;
int** pa2 = &pa1;//二级指针指向一级指针的地址;
printf("%p\n", *pa2);//打印创建一级指针时向内存空间申请储存一级指针变量的地址;
printf("%d\n", *pa1);//通过指针pa1指向的地址取出这个地址的值;
printf("%d\n", **pa2);//通过指针pa2指向pa1的地址取出这个地址里的值,
//这个值为变量为a的地址,取这个地址里的值,这个值为1010
return 0;
}
7.指针数组
指针数组存放指针的数组这个数组里面的每一个元素都是一个地址
//int main()
//整型数组-存放整型的数组
int arr[101];
//字符数组-存放字符的数组
char arr2[501];
//指针数组-存放指针的数组
int* arr3[500];//存放整型指针的数组
//char* arr4[6]//存放字符指针的数组
return0;
#include<stdio.h>
int main()
{
int a = 101;
int bb = 102;
int ccc = 103;
int d4 = 104;
int* arr[4] = { &a,&bb,&ccc,&d4 };
//定义一个int*数组元素是地址
int i = 0;
for (i = 0; i <= 4; i++)
{
printf("%d\n", *(arr[i]));
}
return 0;
}