
学习导航:>
指针理解的2个要点:

指针变量
- #include
- int main()
- {
- int a = 10;//在内存中开辟一块空间
- int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
- //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。
- return 0;
- }
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压和低电平(低电压)就是(1或者0);那么32根地址线产生的地址就会是:00000000 00000000 00000000 0000000000000000 00000000 00000000 00000001...11111111 11111111 11111111 11111111这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空闲进行编址。同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,自己计算。
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
- int num = 10;
- p = #
- char* pc = NULL;
- int* pi = NULL;
- short* ps = NULL;
- long* pl = NULL;
- float* pf = NULL;
- double* pd = NULL;
char* 类型的指针是为了存放 char 类型变量的地址。short* 类型的指针是为了存放 short 类型变量的地址。int* 类型的指针是为了存放 int 类型变量的地址。
- #include
- //演示实例
- int main()
- {
- int n = 10;
- char* pc = (char*)&n;
- int* pi = &n;
-
- printf("%p\n", &n);
- printf("%p\n", pc);
- printf("%p\n", pc + 1);
- printf("%p\n", pi);
- printf("%p\n", pi + 1);
- return 0;
- }
其执行结果:

我们发现,
当指针为char*类型时,指针+1,其步长为1。
当指针为int*类型时,指针+1,其步长为4。
- //演示实例
- #include
- int main()
- {
- int n = 0x11223344;
- char* pc = (char*)&n;
- int* pi = &n;
- *pc = 0; //重点在调试的过程中观察内存的变化。
- *pi = 0; //重点在调试的过程中观察内存的变化。
- return 0;
- }

- #include
- int main()
- {
- int* p;//局部变量指针未初始化,默认为随机值
- *p = 20;
- return 0;
- }
由于p为随机值,所以我们通过解引用操作符修改这块未知的空间所存放的值是极其危险的。
2.指针的越界访问
- #include
- int main()
- {
- int arr[10] = { 0 };
- int* p = arr;
- int i = 0;
- for (i = 0; i <= 11; i++)
- {
- //当指针指向的范围超出数组arr的范围时,p就是野指针
- *(p++) = i;
- }
- return 0;
- }
1. 指针初始化2. 小心指针越界3. 指针指向空间释放即使置NULL4. 避免返回局部变量的地址5. 指针使用之前检查有效性
- #include
- int main()
- {
- int* p = NULL;
- //....
- int a = 10;
- p = &a;
- if (p != NULL)
- {
- *p = 20;
- }
- return 0;
- }
同2.1.
指针减指针可以用来计算字符数组的长度或字符串的长度
例如:
- int my_strlen(char* s)
- {
- char* p = s;
- while (*p != '\0')
- p++;
- return p - s;
- }
- #include
- int main()
- {
- char str[] = "hello world";
- int len = my_strlen(str);
- printf("%d", len);
- return 0;
- }
当我们在写一个循环时,一不注意就会发生数组的越界访问
例如:
- for(vp = &values[N_VALUES]; vp > &values[0];)
- {
- *--vp = 0;
- }
- //既有向前的越界访问,又有向后的越界访问,可谓是糟糕透顶
我们将代码修正如下:
- for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
- {
- *vp = 0;
- }
- #include
- int main()
- {
- int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
- printf("%p\n", arr);
- printf("%p\n", &arr[0]);
- return 0;
- }
运行结果:

可见数组名和数组首元素的地址是一样的。
- int arr[10] = {1,2,3,4,5,6,7,8,9,0};
- int *p = arr;//p存放的是数组首元素的地址
- #include
- int main()
- {
- int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
- int* p = arr; //指针存放数组首元素的地址
- int sz = sizeof(arr) / sizeof(arr[0]);
- int i = 0;
- for (i = 0; i < sz; i++)
- {
- printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);
- }
- return 0;
- }
运行结果:

- #include
- int main()
- {
- int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
- int* p = arr; //指针存放数组首元素的地址
- int sz = sizeof(arr) / sizeof(arr[0]);
- int i = 0;
- for (i = 0; i < sz; i++)
- {
- printf("%d ", *(p + i));
- }
- return 0;
- }
a的地址存放在pa中,pa的地址存放在ppa中。
pa是一级指针,而ppa是二级指针。
对于二级指针的运算有:
那指针数组是怎样的?
我们通过仿照大概可以写出来:
- int* arr3[5];
- char* arr4[6];
我们可以这样描述它,arr3数组包含5个元素,每个元素都是int* 类型的。
小结:指针的内容远不止这些,这里我们只是对指针做出简单的认识,更加高阶的指针知识请参照c语言进阶系列-指针详解。
本章完!