C:指针题

C: pointer questions

关于以下代码的两个问题:

  1. 有和没有malloc()语句有什么区别:

    p = (char *) malloc (20 * sizeof(char) );
    

    仅仅是内存位置的不同吗?指针变量 'p' 在没有该语句的情况下处于 STACK 中,而在具有该语句的 HEAP 中?

  2. 为什么语句是

    printf("%s\n", p);
    

    不是

    printf("%s\n", *p);
    

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

int main () {

   char movie[] = "forrest gump";
   char *p;

   p = (char *) malloc (20 * sizeof(char) );
   p = movie;

   printf("%p\n", p);
   printf("%p\n", movie);
   printf("%s\n", p); // to print "forrest gump"

   free(p);

   return 0;
}
  1. 大多数情况下,是的。 malloc 分配堆 space 和 returns 一个指向它的指针。当函数进入时,stack space 被分配给任何局部变量。在这种情况下,数组 movie 为 13 个字节("forrest gump" 带有空终止符,可能更多取决于对齐问题和字符宽度)。 movie 将使用指定的数据进行初始化。如果字符串更长,它将从编译器设置的常量数据中的副本复制(直到最近十年左右,它会对任何字符串这样做,但现在看来至少有一些编译器初始化直接使用机器代码的短字符串)。还有space为指针p分配在栈上

  2. printf%s 的行为定义为期待指针。将 'string' 放在堆栈上(而不是指向它的指针)会使语言的实现严重复杂化,因为它们的大小是可变的。字符串在 C 中不是特殊的结构,因为它们在许多其他语言中都是如此。在 C 中,它们只是一个以 null 或零字节结尾的字符数组(只有字符串库函数关心这一点——编译器不关心,除了可能的错误检查)。

问题 1

添加行号注释的代码:

char movie[] = "forrest gump";                 // 1
char *p;                                       // 2

p = (char *) malloc (20 * sizeof(char) );      // 3
p = movie;                                     // 4

printf("%p\n", p);                             // 5
printf("%p\n", movie);                         // 6
printf("%s\n", p); // to print "forrest gump"  // 7

free(p);                                       // 8

我会忽略 malloc() 中的 return 值未被检查,并且我不会被 malloc() 中的 return 值强制转换,只要如果您省略 malloc() 的声明,您的编译器会告诉您——但有很多人不同意这一点。 * sizeof(char) 不是必需的,最好写成 * sizeof(*p).

代码存在内存泄漏,并且由于内存泄漏而表现出未定义的行为。

第 1 行没问题。第 2 行同样很好,尽管可以通过将它与第 3 行组合来初始化变量。

第 3 行在这种情况下是安全的,但分配的固定大小在其他情况下会成为问题。

第 4 行是一个主要问题。这是泄漏。您刚刚践踏了 return 由 malloc() 编辑的内存的唯一指针,因此内存不可挽回地丢失了。诚然,这个程序很快就会退出(如果它没有先崩溃的话),但总的来说,这很糟糕。你应该写:

strcpy(p, movie);

或者也许:

memmove(p, movie, sizeof(movie));

第 5 行将打印存储在 p 中的地址。正如所写,这是 movie 的地址,因为第 4 行中的赋值。可以说,它应该用强制转换来编写,因为 %p 格式说明符采用 void *:

printf("%p\n", (void *)p);

在实践中,当 pchar * 时,您将无处不在。我学会了在一台机器上编程(在有 C 标准之前的日子里),其中 char * 地址的位表示与同一内存位置的 anything_bigger * 地址不同。在这样的机器上,如果代码正在处理 struct Something * 甚至 int *,则需要强制转换为 void *

第 6 行产生与第 5 行相同的输出,并具有相同的注意事项。

第7行没问题,打印出p指向的数据,也是movie指向的数据。

第 8 行是调用未定义行为的地方。由于第 4 行中的分配,您试图释放未分配的 space,这是一场严重的灾难。它通常会导致崩溃;这绝不是一个好主意。

代码应该写得似是而非:

char movie[] = "forrest gump";
char *p = malloc(sizeof(movie)); // or: char *p = malloc(strlen(movie) + 1);
if (p != 0)
{
    strcpy(p, movie);
    printf("%p\n", p);
    printf("%p\n", movie);
    printf("%s\n", p);
    free(p);
}

问题 2

您的问题 2 是关于:

printf("%s\n",  p);
printf("%s\n", *p);

第一个是正确的。 p 是一个 char *,而 %s 转换规范需要一个 char *。第二个不正确,因为 *pchar,但 %s 期望 char *。相反,它获取一个小整数(如果您的代码集基于 ASCII,则为 102)并尝试将其视为地址。这是行不通的;它通常会导致崩溃,因为整个第一页内存(1 KiB 或 4 KiB)通常被映射为无效。

您可以使用:

printf("%c\n", *p);

这将打印 f.