在C中解除引用和引用指针的流程

时间:2018-04-19 19:38:57

标签: c pointers memory-management

我正在通过CPP学院的CLA课程材料进行第二次阅读,我仍然在努力寻找指针。这是我想要了解的代码片段。我在下面的代码中评论了我的逻辑。

#include <stdio.h>
#include <stdlib.h>


int main(void) {
//      we are dereferencing pointer t of type float
//      to = integer literal 1 + float pointer to memory location size 
//      of 2xfloats.
//      I am guessing that this memory location will be filled with 
//      all zeroes or this assignment is invalid.
//      This initial assignment is completely confusing me
//      and rest of my logic/reading this snippet is probably wrong :D

float *t = 1 + (float *) malloc(sizeof(float) * sizeof(float));
printf("%f\n",*t);      //0.00000

t--;                    //we move  pointer to next zero in memory?!
printf("%f\n",*t);      //0.00000

*t = 8.0;               //we dereference pointer t and assign literal 
                        //8.0. This will be [0] position
printf("%f\n",*t);      //8.00000

t[1] = *t / 4.0;        //we assign index position [1] to (*t (which 
                        //is previously assigned to 8.0) divided by 
                        //4.0 . This will assign t[1] to 2)
printf("%f\n",*t);      //2.00000

t++;                    //we move pointer to next position [1] = 2
printf("%f\n",*t);      //2.00000

t[-1] = *t / 2.0;       //moving to position [0] and assigning it to 1
printf("%f\n",*t);      //2.00000
free(--t);
return 0;
}

道歉我没有提到这不是工作计划的一部分。这是一长串的短片&#34;技巧&#34;片段以检查对材料的理解。我将详细介绍您提供的答案。谢谢大家:D

3 个答案:

答案 0 :(得分:2)

&lt; gratuitous rant&gt;

如果这个例子代表了CPP研究所提供的课程材料的整体质量,那么我非常强烈建议您到其他地方寻找您的教育和/或认证。

除非这个例子标题为&#34;如何 NOT 编写使用指针的代码&#34; (在这种情况下,非常有效),此代码糟糕。它不仅令人困惑,还充满了糟糕的做法和不确定的行为。

&lt; / gratuitous rant&gt;

让我们从明显的问题开始:

float *t = 1 + (float *) malloc(sizeof(float) * sizeof(float));

这 - 我不知道这应该说明什么,除了如何编写真正令人困惑的代码。

sizeof (float)计算float中的字节数,大多数现代平台上的字节数为4个字节。平衡该值意味着您为N个对象留出足够的空间,其中每个对象的宽度为N个字节 - IOW,如果sizeof (float)为4,则为4个空间提供足够的空间对象,如果sizeof (float)为8,则为8个对象提供足够的空间。

这是不寻常的指定所需对象数量的方式。我们在现实世界中无聊的人只写sizeof (float) * N,其中N是我们想要的对象数量(实际上,我们写sizeof *t * N)。

好的,我们为N个对象分配了足够的空间并返回指针 - 加1?指针算法将指向类型的大小考虑在内,因此向指针添加1表示&#34;指向指向类型的下一个对象&#34; (数组下标运算符a[i] 定义*(a + i) - 给定起始地址a,计算i&#39}的地址。在a之后的对象并取消引用结果)。

基本上,t开始指向动态缓冲区中的第二个对象。把它拿出来,你得到这样的东西:

      +---+
      |   |
      +---+
t ->  |   |
      +---+
      |   |
      +---+
      |   |
      +---+

malloc未初始化动态缓冲区 - 缓冲区的内容为 indeterminate 。不能保证驻留在该存储区中的位模式对应于0.00的值。由于对象未初始化,尝试访问其值会导致未定义的行为 - 行

printf("%f\n", *t);

可能导致0.0000的输出,或者它可能会导致其他一些随机值,或者您可能会遇到运行时错误,或者......

声明

t--;

从指针中减去1,使其指向缓冲区中的 first 元素:

      +---+
t ->  |   |
      +---+
      |   |
      +---+
      |   |
      +---+
      |   |
      +---+

*t = 8.0;               //we dereference pointer t and assign literal 
                        //8.0. This will be [0] position
printf("%f\n",*t);      //8.00000

正确,尽管编写

会更清楚一些
t[0] = 8.0;
printf("%f\n", t[0]);

在处理您作为数组处理的内容时,最好使用数组下标表示法。

t[1] = *t / 4.0;

此代码伤害。说真的,混合数组和指针表示法可以保证引起胃灼热。这样写得更好

t[1] = t[0] / 4.0;

声明

t++;

向指针添加1,让我们回到之前的状态,我们指向数组的第二个元素。

t[-1] = *t / 2.0;

这段代码非常适合paddlin&#39;。负指数是坏juju。由于t现在指向阵列的第二个元素,这不会爆炸,但它只是......只是...... 不要这样做。如果有人将这样的代码转发给我进行审核,我会非常努力地回过头来让他们感受一周。说真的,不要那样做

以下是应编写代码的方式:

#include <stdio.h>
#include <stdlib.h>

#define N 2 // assuming you only want 2 floats.

int main(void) {
  /**
   * Since the type of t is float *, the *expression* *t has
   * type float - thus, sizeof *t == sizeof (float).  Oh, and
   * the cast on malloc is unnecessary, and under C89 will suppress
   * a useful diagnostic if you forget to include stdlib.h.  
   */
  float *t = malloc( sizeof *t * N ); 

  if ( t ) // ***ALWAYS*** check the result of malloc, calloc, or realloc
  {
    t[0] = 8.0; 
    printf( "%f\n", t[0] );      // 8.000

    t[1] = t[0] / 4.0;
    printf( "%f\n", t[1] );      //2.00000

    t[0] = t[1] / 2.0;         
    printf( "%f\n", t[0]);

    free(t);                     // release the memory
  }
  return 0;
}

请注意,如果您希望将分配的内存初始化为all-bits-0,则可以使用calloc而不是realloc

float *t = calloc( N, sizeof *t );

请注意,所有位-0并不一定与0.000对应。

答案 1 :(得分:1)

几乎所有的假设似乎都是正确的; 只有第一个“我猜这个内存位置将被所有零填充或者这个赋值无效”可能会更具体一点:

float *t = 1 + (float *) malloc(sizeof(float) * sizeof(float));

为几个浮点值分配内存;实际上sizeof(float) * sizeof(float)很少有意义,因为可用浮点值的数量取决于浮点数的大小。它不会使用0初始化内存,因此内存未初始化。并且 - 这是现在最重要的事情 - 像printf("%f\n",*t);一样访问未初始化的值会产生未定义的行为。允许编译器执行任何操作,甚至完全忽略该语句。

所以你实际上写了

float *t = 1 + (float *) calloc(sizeof(float), 4);

calloc将内存初始化为0,并且浮动数量更具确定性。

答案 2 :(得分:0)

第一个printf("%f\n",*t);会导致undefined behaviour读取未初始化的内存位置并将结果传递给标准库函数。

如链接中所述,这意味着程序的整个输出毫无意义。甚至没有必要阅读超出这一行的代码。