--b51249e0700425bf x-next-cache-tags: _N_T_/layout,_N_T_/blog/layout,_N_T_/blog/[...slug]/layout,_N_T_/blog/[...slug]/page,_N_T_/blog/c%E8%AF%AD%E8%A8%80/pointer vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch Pointer(指针) | YBinary
Published on

Pointer(指针)

Authors

指针定义

Pointer是一个,这个值代表一个内存地址,因此指针相当于指向某个内存地址的路标。

字符表示指针,通常跟在类型关键字的后面,表示指针指向的是什么类型的值。 比如,char表示一个指向字符的指针,float*表示一个指向float类型的值的指针

int* intPtr;

上面示例声明了一个变量intPtr,它是一个指针,指向的内存地址存放的是一个整数

指针的大小

指针的大小与系统的体系结构(也称为字长)密切相关:

  • 32 位系统:在 32 位系统中,指针的大小通常是 4 个字节(32 位),因为 32 位系统的内存地址空间是 4GB,地址需要 32 位来表示。
  • 64 位系统:在 64 位系统中,指针的大小通常是 8 个字节(64 位),因为 64 位系统可以访问更大的内存地址空间,地址需要 64 位来表示。

因此,在 64 位系统中,任何类型的指针(无论是 char*、int* 还是 double*)的大小都是 8 个字节,因为它们都存储内存地址,而地址在 64 位系统中占 64 位(即 8 字节)。

* 运算符

*这个符号除了表示指针以外,还可以作为运算符,用来取出指针变量所指向的内存地址里面的值

void increment(int* p) {
  *p = *p + 1;
}

上面示例中,函数increment()参数是一个整数指针p。函数体里面,*p就表示指针p所指向的那个值对*p赋值,就表示改变指针所指向的那个地址里面的值

上面函数的作用是将参数值加1。该函数没有返回值,因为传入的是地址,函数体内部对该地址包含的值的操作,会影响到函数外部,所以不需要返回值。事实上,函数内部通过指针,将值传到外部,是 C 语言的常用方法

变量地址而不是变量值传入函数,还有一个好处。对于需要大量存储空间的大型变量,复制变量值传入函数,非常浪费时间和空间,不如传入指针来得高效。

& 取地址符

&运算符用来取出一个变量所在的内存地址。

int x = 1;
printf("x's address is %p\n", &x);

上面示例中,x是一个整数变量,&x就是x的值所在的内存地址。printf()的%p是内存地址的占位符,可以打印出内存地址。

上一小节中,参数变量加1的函数,可以像下面这样使用。

void increment(int* p) {
  *p = *p + 1;
}

int x = 1;
increment(&x);
printf("%d\n", x); // 2

上面示例中,调用increment()函数以后,变量x的值就增加了1,原因就在于传入函数的是变量x的地址&x。

&运算符与*运算符互为逆运算,下面的表达式总是成立。

int i = 5;

if (i == *(&i)) // 正确

指针变量的初始化

声明指针变量之后,编译器会为指针变量本身分配一个内存空间,但是这个内存空间里面的值是随机的, 也就是说,指针变量指向的值是随机的。 这时一定不能去读写指针变量指向的地址,因为那个地址是随机地址,很可能会导致严重后果。

int* p;
*p = 1; // 错误

上面的代码是错的,因为p指向的那个地址是随机的,向这个随机地址里面写入1,会导致意想不到的结果。

正确做法是指针变量声明后,必须先让它指向一个分配好的地址,然后再进行读写,这叫做指针变量的初始化。

int* p;
int i;

p = &i;
*p = 13;

上面示例中,p是指针变量,声明这个变量后,p会指向一个随机的内存地址。 这时要将它指向一个已经分配好的内存地址,上例就是再声明一个整数变量i,编译器会为i分配内存地址, 然后让p指向i的内存地址(p = &i;)。完成初始化之后,就可以对p指向的内存地址进行赋值了(*p = 13;)

为了防止读写未初始化的指针变量,可以养成习惯,将未初始化的指针变量设为NULL

int* p = NULL;

NULL在 C 语言中是一个常量,表示地址为0的内存空间,这个地址是无法使用的,读写该地址会报错。

例子

#include<iostream>
using namespace std;

void test(int *p)
{
  int a=1;
  p=&a;
  cout<<p<<","<<*p<<endl; // 输出 0x22ff44,1
}

int main(void)
{
    int *p=NULL;
    test(p);
    if(p==NULL)
    cout<<"指针p为NULL"<<endl;  // if中的p == null, 输出 指针p为NULL
    system("pause");
    return 0;
}

重点在p为什么是null? 事实上,在main函数中声明了一个指针p,并赋值为NULL,当调用test函数时,事实上传递的也是地址,只不过传递的是指针地址。 也就是说将指针作为参数进行传递时,事实上也是值传递,只不过传递的是地址。 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参, 即上面程序main函数中的p为何与test函数中使用的p不是同一个变量,存储2个变量p的单元也不相同(只是2个p指向同一个存储单元), 那么在test函数中对p进行修改,并不会影响到main函数中的p的值。

Refernce

https://wangdoc.com/clang/pointer

--b51249e0700425bf--