目录
一,内存
- 内存是电脑重要存储器,计算机中所有程序的运行都是在内存中进行的;
- 为了有效利用内存,把内存划分为一个个小的内存单元,每个内存单元大小为1字节(byte);
- 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,称为内存单元的地址;
- 32位机器,即32根地址线/数据线,最大可用内存大小4GB(2^32byte);
- 64位机器,即64根地址线/数据线,最大可用内存大小2^34GB(2^64byte,非常大);
二,指针
- 指针即内存地址(编号),指针变量即存放内存地址(编号)的变量;
- 变量可使用 & 操作符来访问其地址;
- 指针变量可使用*(解引用)操作符来获取地址指向的变量;
- NULL空指针不可解引用;
- 32位,指针大小为4byte;
- 64位,指针大小为8byte;
空指针,是特殊指针,不指向任何实际的对象或函数(即不指向任何有效地址),一般指向0地址(取决于系统实现);访问空指针会报错,是因为其指向的逻辑地址没有对应的物理地址;
int main()
{
char s = 'a'; //字符'a'对应ASCII码为97
char* p = &s; //*表示是指针,char*表示存放char类型的指针
printf("%d\n", sizeof(p)); //测试指针变量的大小
}
//结果:4
int main()
{
char s = 'a'; //字符'a'对应ASCII码为97
char* p = &s;
printf("%p\n", p); //结果:0x006FFDEB
printf("%c\n", *p); //结果:a
return 0;
}
注:
- 查看内存地址,按F10进入调试,在菜单内存选择内存1(调试-窗口-内存-内存1),在地址栏内输入&s,即可查看s变量的内存地址;
- 0x006FFDEB,0x表示十六进制形式,006FFDEB — 每个数表示4位,每两个数表示8位一个字节,这里有8个数32位4个字节,即地址有4个字节(32位机器下);
三,指针类型
type + *
- type,表示指针指向内存地址处的数据类型,如char*,就表示指向内存地址处的数据类型为字符型;
- *,表示其是指针;
int main()
{
char s = 'a';
char* p = &s; //表示指针变量p,指向的数据为char类型
return 0;
}
指针类型的意义
- 指针 +/- 整数,决定了指针向前或者向跳过多大字节数,如int就跳过4个字节;
- 指针的解引用,决定了解引用时,访问的字节个数,如int就访问4个字节;
int main()
{
char a = 'a';
int b = 10;
char* pa = &a;
int* pb = &b;
printf("%p %p\n", pa, pa + 1); //结果:010FFA1B 010FFA1C,相差1个字节
printf("%p %p\n", pb, pb + 1); //结果:010FFA0C 010FFA10,相差4个字节
return 0;
}
注:
//不可连续创建多个指针类型变量
//b类型为int
int* a, b;
//应改为
int*a, *b;
四,野指针
野指针,即指针指向的位置是不可知的(如随机的、不正确的、没有明确限制的);
野指针的成因:
- 创建指针时未初始化;
- 应赋值NULL或指向合法内存;
- 指针越界访问;
- 避免越界;
- 指针指向的空间释放(动态内存空间会讲解到);
- 给释放的指针空间,赋值NULL;
规避野指针,最重要的是使用前检查指针的有效性;
int main()
{
//未初始化指针,为随机值
char* a;
int* b;
*a = 'a'; //对未初始化指针,直接使用(解引用)会报错
return 0;
}
int main()
{
//指针越界访问
char arr[] = "abcd"; //有五个元素,末尾有隐藏的字符'\0'
char* p = arr + 5; //数组名arr,即表示指针(且是首元素的地址)
printf("%c", *(p));
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int)); //开辟动态内存,即40个字节的内存
if (NULL == p)
printf("%s\n", strerror(errno));
else
{
int i = 0;
for (i; i < 10; i++)
*(p + i) = i;
}
//打印
int i = 0;
for (i; i < 10; i++)
printf("%d ", *(p + i));
//释放空间
free(p); //此时p空间已释放,即为野指针
p = NULL; //为避免野指针,可给p赋予null
return 0;
}
五,指针的运算
- 指针加减整数
- 指针减指针(表示指针间的元素个数,且指向的是同一块空间)
- 指针的关系运算
标准规定:
- 允许指向数组元素的指针,与指向数组最后一个元素后面的内存位置的指针比较,但是不允许与指向第一个元素之前的内存位置的指针进行比较。
//指针加减整数
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i; i < 10; i++)
{
*(p + i) = i; //p+i表示指针跳过i个字节,*(p+i)与arr[i]相同,即数组的每个元素
}
return 0;
}
//指针减指针
int main()
{
char arr[5] = "abcd";
char* p = arr;
char* pend = arr;
while (*pend != '\0') //pend指向'\0'时,即循环结束
{
pend++;
}
printf("%d\n", pend - p);
return 0;
}
//结果:4
int main()
{
char arr[5] = { 'a','b','c','d','e' };
char* p = arr + 5;
for (p; p > arr;)
{
*--p = 0;
}
return 0;
}
//要避免以下写法,因为最后一次判断是arr-1 > arr
int main()
{
char arr[5] = { 'a','b','c','d','e' };
char* p = arr + 4;
for (p; p >= arr; p--)
{
*p = 0;
}
return 0;
}
六,指针和数组
- 数组名,表示数组首元素的地址;
- &数组名,表示整个数组的地址;
- arr[i] = i[arr] = *(p+i) = p[i] = i[p]
注:除以下两种情况外,其余情况arr均表示首元素地址
- &arr,此时数组名arr表示整个数组,是整个数组地址;
- sizeof arr,此时数组名arr表示整个数组,计算整个数组大小;
int main()
{
int arr[5] = { 1,2,3,4,5 };
printf("%p %p\n", arr, &arr[0]); //结果:00AFFDD8 00AFFDD8
printf("%p %p\n", arr, arr + 1); //结果:00AFFDD8 00AFFDDC 相差4,即一个int元素
printf("%p %p\n", &arr, &arr + 1); //结果:00AFFDD8 00AFFDEC 相差20,即5个int元素正好是整个数组
return 0;
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* p = arr;
printf("%d %d\n", *(p + 2), arr[2]); //结果:3 3
return 0;
}
int main()
{
int arr[] = { 1,2,3,4,5 }; //整型数组
int* p = arr;
printf("%d ", arr[2]);
printf("%d ", 2[arr]);
printf("%d ", p[2]);
printf("%d ", 2[p]);
printf("%d ", *(p+2));
return 0;
}
//结果:3 3 3 3 3
//其实[]是一个操作符,arr,2是操作数
//最后都会转换为 *(p+2)
七,二级指针
二级指针
- 即其指向的地址也是一个指针,但此指针指向的地址不是指针;
三级指针
- 即其指向的地址也是一个指针,且此指针指向的地址还是指针,但这个指针指向的地址不是指针;
其他,依次类推;
int main()
{
int a = 1;
int* p = &a; //指针,即一级指针
int** pp = &p; //二级指针
printf("%d\n", **pp); //结果:1
return 0;
}
八,指针数组
指针数组,即存放指针的数组;
int main()
{
int arr[] = { 1,2,3,4,5 }; //整型数组
int* parr[] = { arr,arr + 1,arr + 2,arr + 3,arr + 4 }; //指针数组
int i = 0;
for (i; i < 5; i++)
{
printf("%d ", *parr[i]);
}
return 0;
}
//结果:1 2 3 4 5