在C语言中的解引用

9

我刚开始学习C语言,所以请多关照。根据我目前了解的指针知识:

int * test1; //this is a pointer which is basically an address to the process 
             //memory and usually has the size of 2 bytes (not necessarily, I know)
float test2; //this is an actual value and usually has the size of 4 bytes,
             //being of float type
test2 = 3.0; //this assigns 3 to `test2`

现在,我还不完全理解的是:
*test1 = 3; //does this assign 3 at the address 
            //specified by `pointerValue`?
test1 = 3;  //this says that the pointer is basically pointing 
            //at the 3rd byte in process memory, 
            //which is somehow useless, since anything could be there
&test1; //this I really don't get, 
        //is it the pointer to the pointer? 
        //Meaning, the address at which the pointer address is kept?
        //Is it of any use?

同样地:
*test2; //does this has any sense?
&test2; //is this the address at which the 'test2' value is found? 
        //If so, it's a pointer, which means that you can have pointers pointing 
        //both to the heap address space and stack address space. 
        //I ask because I've always been confused by people who speak about 
        //pointers only in the heap context.

1
指针在70年代有两个字节。现在通常是八个字节,因为它必须寻址一个64位的内存空间。 - Diego Basch
test2 没有任何意义,它可能甚至无法编译。 - Diego Basch
啊,我明白了,但是它也可以有4个字节,对吧?因为32位内存空间现在也很常见。 - Meda
2
指针大小是与实现相关的,这意味着它取决于您编译到的平台。 - Dancrumb
你大部分是正确的。&test2 返回一个指向浮点数的指针。&test1 返回一个指向整型指针的指针。*test2 是无效的,因为你不能解引用浮点数或任何其他“标量”值——只能解引用指针。 - Hot Licks
@Dancrumb的回答解答了你所有的问题。我建议你阅读这篇写得很好的教程,以更好地理解指针:http://cslibrary.stanford.edu/106/ - Bharat
3个回答

3

很好的问题。

你的第一个块是正确的。指针是一个变量,它持有某些数据的地址。该指针的类型告诉代码如何解释被该指针持有的地址的内容。

这个结构:

*test1 = 3

这被称为指针解引用。也就是说,您可以访问指针所指向的地址,并像普通变量一样读取和写入它。注意:

int *test;
/*
 *    test is a pointer to an int - (int *)
 *   *test behaves like an int - (int)
 *
 * So you can thing of (*test) as a pesudo-variable which has the type 'int' 
 */

上述只是我使用的记忆工具。
很少会给指针分配数值……除非你在为某个特定环境开发,该环境有一些“众所周知”的内存地址。但就你目前的水平而言,不必过于担心这一点。
使用:
*test2

最终会导致错误,因为您试图取消引用并非指针的内容,因此可能会出现某种系统错误,因为谁知道它指向哪里。
&test1和&test2确实是指向test1和test2的指针。
指向指针的指针非常有用,搜索“指向指针”的指向指针将带您访问比我更好的一些资源。

1

看起来你已经掌握了第一部分。

顺便说一下:关于在哪里放置那个*符号,有各种不同的约定。我更喜欢将其与变量名嵌套在一起,例如int *test1,而其他人则更喜欢int* test1。我不确定将其浮动在中间有多常见。

另一个顺带的想法:test2 = 3.0将浮点数3赋给test2。使用test2=3也可以实现相同的结果,在这种情况下,3会被隐式转换为浮点数。你选择的约定可能在清晰度方面更安全,但并非绝对必要。

非顺带的:

*test1=3确实将3赋给test指定的地址。

test1=3是一行有意义但我认为毫无意义的代码。我们不知道内存位置3上有什么,是否安全触及它,甚至是否允许触及它。

这就是为什么使用类似于

int var=3;
int *pointy=&var;
*pointy=4;
//Now var==4.

命令&var返回变量var的内存地址,并将其存储在pointy中,以便稍后我们可以使用*pointy进行访问。

但我也可以做这样的事情:

int var[]={1,2,3};
int *pointy=&var;
int *offset=2;
*(pointy+offset)=4;
//Now var[2]==4.

这就是你可能合理看到像test1=3这样的东西的地方:指针可以像数字一样添加和减去,因此您可以存储这样的偏移量。

&test1是一个指向指针的指针,但对我来说听起来有点混乱。它实际上是存储test1值的内存地址。而test1恰好将另一个变量的地址存储为其值。一旦您开始以这种方式思考指针(内存中的地址,存储在那里的值),它们就变得更容易使用...或者至少我认为是这样。

我不知道*test2是否具有“意义”。原则上,它可能会有用,因为我们可以想象*命令将把test2的值视为内存中的某个位置,并返回它找到的值。但由于您将test2定义为浮点数,很难预测我们会在内存中结束的位置,设置test2=3不会将我们移动到任何位置的第三个位置(查阅IEEE754规范以了解原因)。但如果编译器允许这样的事情,我会感到惊讶。

让我们来看另一个快速的例子:

int var=3;
int pointy1=&var;
int pointy2=&pointy1;
*pointy1=4;  //Now var==4
**pointy2=5; //Now var==5

所以你可以像这样链接指针,一排接一排的。如果你有一个指向动态内存中许多结构体地址的指针数组,并且这些结构体本身包含指向动态分配的其他东西的指针,那么这种情况可能会出现。当使用指向指针的指针时,你可能会知道。现在,不要太担心它们。


1

首先让我们加入一些混淆:单词“指针”可以指一个带有指针类型的变量(或对象),也可以指一个带有指针类型的表达式。在大多数情况下,当人们谈论“指针”时,他们指的是指针变量。

指针可以(必须)指向一个“东西”(标准术语中的“对象”)。它只能指向正确类型的“东西”;指向int的指针不应该指向float对象。指针也可以是NULL;在这种情况下,没有东西可指。

指针类型也是一种类型,指针对象也是一个对象。因此,构造指向指针的指针是允许的:指向指针的指针只存储指针对象的地址。

指针不能成为:

  • 它不能指向一个值:p = &4; 是不可能的。4是一个字面值,它没有被存储在对象中,因此没有地址。
  • 对于表达式也是一样的:p = &(1+4); 是不可能的,因为表达式“1+4”没有位置。
  • 对于返回值也是一样的:p = &sin(pi); 是不可能的;返回值不是一个对象,因此没有地址。
  • 标记为“register”的变量(现在几乎不再使用)不能有地址。
  • 你不能取一个位域的地址,基本上是因为这些可以比字符小(或者有更细的粒度),因此不同的位掩码可能有相同的地址。

以上骨架存在一些“例外”(空指针、强制类型转换、指向数组对象之外的一个元素),但为了清晰起见,这些应该被视为细化/修正,我个人认为如此。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接