编程语言应用

首页 » 常识 » 问答 » C语言的指针够复杂了,为什么还要分一级二
TUhjnbcbe - 2023/4/30 21:21:00
中科出席第十届健康中国论坛大会 http://nb.ifeng.com/a/20180108/6287990_0.shtml

虽然相比于其他高级编程语言,C语言的语法非常简单,但是它指针部分确实比较抽象,事实上,C语言的指针语法是不少初学者的噩梦,网络上有很多关于指针的有趣问题,前几天有读者向我问了下面这样的问题。

C语言为什么要分多级指针?

C语言为什么要分多级指针?

这位读者认为,C语言中的指针其实就是用于存储特定变量地址的,下面是一段C语言代码:

inta=5;

int*b=a;

指针b存储了变量a的地址,没有什么问题。现在,假设需要存储指针b的地址,那么C语言代码可以按照下面这样写:

inta=5;

int*b=a;

int**c=b;

此时内存布局如下图所示,请看

C语言程序内存布局

显然,二级指针c指向了指针b的地址。一切似乎都很自然,但是这位读者有疑问:既然C语言中的指针其实就是用于存储特定变量地址的,那为什么还要分多级指针呢?

详细来说,变量c里存放的起始是b的地址,而b的地址和a的地址在数值上并无区别,因此将c定义为int*类型,按理说足以存储b的地址,因此下面这样的C语言代码应该可以正常实现需求的:

inta=5;

int*b=a;

int*c=b;

但是,为什么上面这几行C语言代码会产生警告呢?也即为什么编译器认为int*c=b;是不妥的?

为什么上面这几行C语言代码会产生警告

讨论

首先应该指出,C语言中的指针并不仅仅用于存储变量的地址,它还可以描述程序如何解释某段内存的值。这一点在我之前的文章里已经较为深入的讨论过,感到迷惑的读者可以再回头看看。

例如,对于char类型的指针:char*pc=0x;,*pc表示地址0x处的sizeof(char)=1个字节。若是定义为int类型(假设占用4字节空间)指针:int*pi=0x;,则*pi表示地址0x处的sizeof(int)=4个字节。

此外,pc+1是指下一个char对象处,也即0x处,而pi+1则是值下一个int对象处,也即0x处。

再回到这位读者的问题,C语言标准并没有保证int*和int**指针具有相同的的大小,因此对于int**ppi;,*ppi表示的其实是sizeof(int*)个字节,而ppi+1则是指下一个int*对象处,也即0x+sizeof(int*)处。

C语言标准并没有保证int*和int**

显然,一级指针pi和多级指针ppi是不同的。

此外,C语言中的多级指针在函数定义中也有不同的用途,例如下面这段C语言代码:

voidfoo(T*p){

*p=new_value();

}

voidbar(void){

Tval;foo(val);

}

T表示任意数据类型。如果将T改为指针的形式,相关C语言代码如下:

voidfoo(P**p){

*p=new_value();

}

voidbar(void){

P*val;foo(val);

}

这两段C语言代码本质上其实完全相同

这两段C语言代码本质上其实完全相同,只是数据类型在表现上有所差异:形参p总是比变量val多一级间接寻址。

可见,C语言中的多级指针和一级指针还是有所差异的。

不过,要是仅考虑这位读者所说的C语言中的指针其实就是用于存储特定变量地址的,那么使用一级指针完全足够了,毕竟C语言程序中的地址其实就是一个整数而已。事实上,只要保证没有精度损失,使用任意数据类型存储地址,都是没有问题的。

再考虑开头的问题:

inta=5;

int*b=a;

如果仅仅需要存储指针b的地址,使用void*指针也是可以的:

void*p=b;

甚至,使用long整数类型存储都是可以的:

longp=(long)b;

这里假设long类型的宽度大于或者等于指针类型的宽度。

要保证没有精度损失

最后

这里再强调一次,存储指针地址时,使用一级int*指针,void*指针,甚至long类型都是可以的,但是若想正确引用原本地址指向的内容,则仍需将其转换为应有的格式。

例如,我们使用p存储指针b的地址,实际上最终需要索引的有意义的数值是变量a存储的数值5。若是希望使用p索引数值5,那么强制类型转换是不可避免的:

int**ppi=(int**)p;

原因其实我们都讨论过了,C语言程序解释地址指向的内存时,是需要我们提供给其解释方式的,而指定其原本数值类型的目的就在于此。

点个
1
查看完整版本: C语言的指针够复杂了,为什么还要分一级二