什么是地址?
1.在这儿我想从硬件的角度来解释地址是什么?
看上图,在计算机中,CPU(一块芯片)与内存(一块或几块芯片)是通过导线来连接的,在者之间,有三组总线,数组总选,负责数据的运输,控制总线,负责读数据写数组,那么在这儿呢,我们要关心的就是地址总线,它是为了管理内存单元,编址所用的总线,一个内存单元有8位,是8根数据线所连接的,如果要访问一个内存单元,则必须要有对应的地址,通过地址,来找到内存单元,对所指定的内存进行操作,那么地址总线的多少则决定了地址编码范围有多大,决定了内存空间有多大!
比如x86的机子,则有32根地址线,它所管理的内存单元有2^32个内存单元,
x64的机子,则有64根地址线,它所管理的内存单元有2^64个内存单元,明显,
位数越多,管理的内存越大!!!
地址,指针,指针变量的关系
(1)地址就是地址,就是内存空间单元的编号!
(2)指针就是地址,没什么区别,只是叫法不一样!
(3)指针变量的本质是变量,是在内存中占用的一小块空间,一般而言是(局部变量)在栈区开辟的一块空间,这块空间是放地址编号的!它也是有地址的!
对指针变量的理解
int main()
{
int a = 0x11223344;
int* pa = &a;
return 0;
}
上面代码很简单,创建了变量a,把a的地址存放到指针变量pa中,这儿怎么理解int*pa;?
#这儿int * pa = &a,*表明了变量pa是个指针变量,int 则表明了这是个指向int的指针变量!
指针变量的大小
#很简单,指针变量是存地址的,地址有多少位,则开辟的指针变量有多大?32位机器上—>4个字节,64位机器上—>8个字节,在这儿说一下,指针的大小跟指针类型一点关系没有,它是个编码,有什么关系!!!
int main()
{
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
return 0;
}
指针类型的意义
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
调试这段代码!!!
从上面可以看,int * 的指针执行完*pa = 0之后,a的每个字节被改成0了!
int main()
{
int a = 0x11223344;
char* pa = &a;
*pa = 0;
return 0;
}
调试这段代码!!!
从上面可以看,char的指针执行完pa = 0之后,a的第一个字节被改成0了,其它不变!
结论:指针类型的大小决定在解引用访问内存的时候,访问多大的空间,这个也能理解,比如int*的指针,对应的就int类型的变量,为所改变他在内存中的值量身打造!!!
int main()
{
int a = 10;
int* pa = &a;
char* pc =(char*)&a;
printf("pa = %p\n", pa);
printf("pc = %p\n", pc);
printf("pa+1 = %p\n", pa + 1);
printf("pc+1 = %p\n", pc + 1);
return 0;
}
我们可以看出,char* 类型的指针变量+1跳过1个字节,int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
void*指针
void*指针是一种无具体类型的指针,这个指针非常重要,它可以接受任何类型的指针,由于它自己没有什么特定的类型,所以他不能解引用操作,也不能进行指针的运算!!!
int main()
{
int a = 10;
//int* pa = &a;
//char* pc = &a; //warning
void* pv = &a;
*pv = 10;//err
pv++; //不能加
//void* 就是存储不同类型的地址
return 0;
}
const关键字
int main()
{
const int a = 10; //a不能被修改了,但是a的本质还是变量,const仅仅是在语法上做了限制,所以我们习惯上叫a是常变量
//a = 20; //修改报错
printf("a = %d\n", a);
return 0;
}
*变量a被const修饰之后,不能修改a的值了,这是因为c语言在设计的时候在语法层面做了限制,a的本质还是变量,怎么证明?看看反汇编就知道!!!
const int a = 10;
mov dword ptr [ebp-8],0Ah
//对应的汇编代码还是这样,10装进了ebp-8的地质单元中,没有任何修改!!!
const修饰指针,看下面代码!!!
int main()
{
const int a = 10;
const int* p = &a; //限制的是*p
int* const p = &a; //限制的是p
//const放在*的左边,限制的是*p,意思是不能指针变量p修改p指向的内容
//const放在*的右边,限制的是p变量,也就是p变量不能被修改了,没办法在指向其它变量了
*p = 0; //err
printf("%d\n", a);
return 0;
}
*这儿呢?在语法层面是这样限制的!
*const int * p; 或int const * p;在这儿const关键字限制的是 * p,也就是p所指向的空间内容不让修改;
#int * const p;const限制的是p变量,p里面的内容不让修改!!!
指针运算
1)指针±运算
这个也很简单,指针+1操作就是跳过1个指针类型的大小,来到下一个单元的地址.
下面是一个通过指针访问数组的例子,很简但!!!
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
const int* p = &arr[0];
for (int i = 0; i < 10; i++)
{
//printf("%d ", *p);
//p++;
printf("%d ", *(p + i));
}
return 0;
}
2)指针-指针的运算
int my_strlen(const char* str)
{
char* start = str;
char* end = str;
while (*end++);
return end - start - 1;
}
int main()
{
int len = my_strlen("abc");
printf("%d", len);
return 0;
}
指针-指针的绝对值得到的是两个指针之间的元素!!!
指针-指针的前提是两个指针指向同一块空间!!!
刚才想了一下,如果是两个不同的类型的指针相减呢?
int main()
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
int* start = &arr[0];
char* end = (char*)&arr[1];
printf("%d\n", end - start); //4
//char* - int* 转化成了char*来减
char* start1 = (char*)&arr[0];
int* end1 = &arr[1];
printf("%d\n", end1 - start1);//1
//int* - char*转化成了int*来减
char* start2 = (char*)&arr[0];
char* end2 = (char*)&arr[1];
printf("%d\n", end2 - start2);//4
int* start3 = &arr[0];
int* end3 = &arr[1];
printf("%d\n", end1 - start1);//1
return 0;
}
上面的例子我不确定,结果是在vs2022下测试的结果!有时间了,查看汇编代码,看看具体怎么执行!!!
3)指针的关系运算
这个也没多大作用!!!(这个其实就是地址大小的比较)
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(int);
int* p = arr;
//arr是数组名,数组名其实是数组首元素的地址
while (p<arr+sz) {
printf("%d ", *p);
p++;
}
return 0;
}
完结!!!