双指针与指针数组(数组 vs *数组[])

21

我不太确定这两者之间有什么区别。我的教授写道“array与*array[]相同”,并且我们被演示了一个使用**array的例子(所以在课后,我尝试将其与*array[]交换,但它没有起作用),请问这两个是否真的如他所写?无论如何,这节课是关于动态内存分配的。

一旦我改变了双指针,这行代码就开始抛出错误。

    lines = malloc(sizeof(char*));

还有一些其他地方需要重新分配内存。

@2 当然,这是整段代码

对于下面的评论,[]中没有任何内容,因为他的陈述是

    **array = *array[]

大更新

很抱歉给您造成任何不便,我在写这篇文章的时候太累了,以下是完整的代码,没有做任何修改。

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

    char **lines;     // global text buffer, organized as an array of lines

    // --------------------------------------------------------------------------------
    // initialize global buffer
    void initialize()
    {
      lines = malloc(sizeof(char*));
      lines[0] = NULL;
    }

    // --------------------------------------------------------------------------------
    // return number of lines in buffer
    int countLines()
    {
      int count = 0;
      while(lines[count++]) ;
      return count-1;
    }

    // --------------------------------------------------------------------------------
    // print one line
    void printLine(int line)
    {
      printf("Line %d: %p %p %s\n",line, &lines[line], lines[line], lines[line]);
    }

    // --------------------------------------------------------------------------------
    // print all lines
    void printAll()
    {
      int num_lines = countLines();
      int line = 0;
      printf("----- %d line(s) ----\n",num_lines);
      while (line < num_lines)
        printLine(line++);
      printf("---------------------\n");
    }

    // --------------------------------------------------------------------------------
    // free whole buffer
    void freeAll()
    {
      int line = countLines();
      while (line >= 0)
        free(lines[line--]);
      free(lines);
    }

    // --------------------------------------------------------------------------------
    // insert a line before the line specified
    void insertLine(int line, char *str)
    {
      int num_lines = countLines();

      // increase lines size by one line pointer:
        lines = realloc(lines, (num_lines+2) * sizeof(char*));

      // move line pointers backwards:
      memmove(&lines[line+1], &lines[line], (num_lines-line+1)*sizeof(char*));

      // insert the new line:
      lines[line] = malloc(strlen(str)+1);
      strcpy(lines[line],str);
    }

    // --------------------------------------------------------------------------------
    // remove the specified line
    void removeLine(int line)
    {
      int num_lines = countLines();

      // free the memory used by this line:
      free(lines[line]);

      // move line pointers forward:
      memmove(&lines[line], &lines[line+1], (num_lines-line+1)*sizeof(char*));

      // decrease lines size by one line pointer:
        lines = realloc(lines, num_lines * sizeof(char*));
    }

    // --------------------------------------------------------------------------------
    // insert a string into specified line at specified column
    void insertString(int line, int col, char *str)
    {
      // make room for the new string:
      lines[line] = realloc(lines[line], strlen(lines[line])+strlen(str)+1);

      // move characters after col to the end:
      memmove(lines[line]+col+strlen(str), lines[line]+col, strlen(lines[line])-col);

      // insert string (without terminating 0-byte):
      memmove(lines[line]+col, str, strlen(str));
    }

    // --------------------------------------------------------------------------------
    // MAIN program
    int main()
    {
      initialize();

      printAll();
      insertLine(0,"Das ist");
      printAll();
      insertLine(1,"Text");
      printAll();
      insertLine(1,"ein");
      printAll();
      insertLine(2,"kurzer");
      printAll();
      printf("lines[2][4] = %c\n",lines[2][4]);
      insertString(2,0,"ziemlich ");
      printAll();
      removeLine(2);
      printAll();

      freeAll();
      return 0;
    }

4
最好贴上一些代码示例。上下文很重要。 - juanchopanza
3
http://c-faq.com有几篇回答讨论了这个问题(在第6节中)。 - Steve Summit
1
数组不是指针。你的教授有误,应该阅读C标准。 - too honest for this site
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - too honest for this site
区别至少在于分配和初始化。 - red0ct
显示剩余9条评论
3个回答

23

如果您在问题中参考的代码是由您的教授提供的作为指针数组和指向指针的指针使用示例,我不确定这门课程实际上对您有多大帮助。我怀疑它可能是作为一个调试练习提供的,或者可能是您尝试解决问题的一种方法。无论如何,如果您启用了警告并进行简单的编译,您将发现需要注意的许多问题,然后再进行代码调试。

关于您所参考的代码,虽然您可以自由使用全局文本缓冲区,但最好不要使用全局缓冲区,并根据需要传递数据的指针。有些情况下,例如各种回调函数等需要全局数据,但通常来说,这些是例外而不是规则。

你的问题基本上归结为“如何正确使用指针数组和双指针(指向指针的指针)变量”。在一个答案中完全涵盖这个主题是不可能的,因为有太多的情况和场景,其中一个或另一个可以(或应该)被使用以及原因。然而,一些示例将有助于您了解基本差异。

指向类型的指针数组(例如char *array[])开始。它通常以该形式作为函数参数出现。当作为变量声明时,它后面跟着一个初始化。例如:

char *array[] = { "The quick",
                  "brown fox",
                  "jumps over",
                  "the lazy dog." };

char *array[];本身作为变量声明是无效的,因为中间缺少数组大小。[..]。当全局使用时,如您的示例中,编译器将接受该声明,但会警告该声明假定有一个元素

上面声明的array的元素是指向类型为char的指针。具体来说,这些元素是指向声明创建的字符串字面值的指针。可以通过array[0],...array[3]中的相关指针访问每个字符串。

类型的指针指向另一个指针(双指针),正如其名称所示。它是一个指针,它保存一个指针作为其值。简单来说,它是一个指向另一个指针的指针。它可用于通过将array的地址赋值给它来访问上述数组的成员:

char **p = array;

其中 p[1] 或者 *(p + 1) 指向 "brown fox",等等。

或者,可以动态分配一些指向指针的指针来创建一个指向类型的指针数组,该数组可以被分配和重新分配以处理访问或存储未知数量的元素。举个例子,从 stdin 读取未知行数的简短示例:

#define MAXL 128
#define MAXC 512
...
char **lines = NULL;
char buf[MAXC] = {0};
lines = malloc (MAXL * sizeof *lines);
size_t index = 0;
...
while (fgets (buf, MAXC, stdin)) {
    lines[index++] = strdup (buf);
    if (index == MAXL)
        /* reallocate lines */
}

以上是 lines,一个指向指针的指针类型,最初为 NULL,用于分配 MAXL(128)个指向字符的指针。然后从 stdin 中读取行到 buf 中,每次成功读取后,会分配内存来保存 buf 的内容,并将每个内存块的起始地址分配给每个指针 line[index],其中 index0-127,当 index 增加到 128 时,index 会被重新分配以提供更多的指针并继续读取。

这个主题之所以比任何一个答案都要大,是因为指针数组或指向类型的指针可以指向任何类型(例如 int、struct、或作为结构体成员指向不同类型或函数等...)。它们可以用于链表、目录列表的返回(例如 opendir),或其他许多方式。它们可以静态初始化、动态分配、作为函数参数传递等等... 只是涉及太多不同的上下文,无法覆盖它们所有。但在所有情况下,它们都遵循此处和其他答案中看到的通用规则,在 StackOverflow 上有数千个答案都是这样的。

最后我将以一个简短的示例结束,您可以用它来查看数组和双指针的不同基本用法。我在源代码中提供了其他注释。这只提供了一些不同的基本用法以及静态声明和动态分配:

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

int main (void) {

    /* array is a static array of 4 pointers to char, initialized to the 
       4 string-literals that a part of the declaration */
    char *array[] = { "The quick",
                    "brown fox",
                    "jumps over",
                    "the lazy dog." };
    /* p is a pointer-to-pointer-to-char assigned the address of array */
    char **p = array;
    /* lines is a pointer-to-pointer-to-char initialized to NULL, used
       below to allocate 8 pointers and storage to hold 2 copes of array */
    char **lines = NULL;
    size_t narray = sizeof array/sizeof *array;
    size_t i;

    printf ("\nprinting each string-literal at the address stored by\n"
            "each pointer in the array of ponters named 'array':\n\n");
    for (i = 0; i < narray; i++)
        printf (" %s\n", array[i]);

    printf ("\nprinting each string using a pointer to pointer to char 'p':\n\n");
    for (i = 0; i < narray; i++, p++)
        printf (" %s\n", *p);

    p = array;
    printf ("\nprinting each line using a pointer to pointer"
            " to char 'p' with array notation:\n\n");
    for (i = 0; i < narray; i++)
        printf (" %s\n", p[i]);

    /* allocate 8 pointers to char */
    lines = malloc (2 * narray * sizeof *lines);

    /* allocate memory and copy 1st 4-strings to lines (long way) */
    for (i = 0; i < narray; i++) {
        size_t len = strlen (array[i]);
        lines[i] = malloc (len * sizeof **lines + 1);
        strncpy (lines[i], array[i], len);
        lines[i][len] = 0;
    }

    /* allocate memory and copy 1st 4-strings to lines 
       (using strdup - short way) */
    // for (i = 0; i < narray; i++)
    //     lines[i] = strdup (array[i]);

    /* allocate memory and copy again as last 4-strings in lines */
    p = array;
    for (i = 0; i < narray; i++, p++)
        lines[i+4] = strdup (*p);

    p = lines; /* p now points to lines instead of array */
    printf ("\nprinting each allocated line in 'lines' using pointer 'p':\n\n");
    for (i = 0; i < 2 * narray; i++)
        printf (" %s\n", p[i]);

    /* free allocated memory */
    for (i = 0; i < 2 * narray; i++)
        free (lines[i]);
    free (lines);

    return 0;
}

如果您有任何问题,请告诉我。这是一个相对简单的规则集,可以应用于许多不同的方式和情境。


14
我的教授写道,**array*array[]是一样的。
在某些情况下是正确的,在其他情况下则不正确。
如果在函数中作为参数使用,
void foo(int **array) {}

就是一样的

void foo(int *array[]) {}

当声明为变量时,
int **array;

不一样
int *array[];

关于评论“为什么int **array;int *array[];不一样?”

让我们用int *arrayint array[]来回答这个问题。

下面是声明这些变量的一些有效和无效的方式。

int *array; // Ok. Uninitialized pointer.
int *array = nullptr; // ok.
int *array = new int; // ok.
int *array = new int[10]; // ok.

const size = sizeof(array); // Size of a pointer. For 32 bit
                            // pointers, this will be 4.
                            // For 64 bit pointers, this will be 8

与之形成鲜明对比的是
int array[]; // Not ok.
int array[] = nullptr; // Not ok.
int array[] = new int; // Not ok.
int array[] = new int[10]; // Not ok.
int array[] = {10, 20, 30}; // Ok.
int array[3] = {10, 20, 30}; // Ok. same as previous

const size1 = sizeof(array); // (size of int)*3
                             /// Unrelated to size of pointers

int array[5] = {10, 20, 30}; // Ok.
int array[5] = {10, 20, 30, 0, 0}; // Same as previus.

const size2 = sizeof(array); // (size of int)*5
                             /// Unrelated to size of pointers

更多关于这些差异的内容:
1.

int *array;足以声明一个变量,而int array[]则不行。后者的数组大小必须在编译时已知。

2.

sizeof运算符对int *array;int array[];的处理方式不同。

3.

使用int *声明的变量可以重新分配指向不同位置的指针,而使用int []声明的变量则不能重新分配。

int *array = nullptr;
array = new int; // 语法上没问题。
array = new int[10]; // 语法上没问题。

然而

int array[] = {10, 20, 30};
array = new int[3]; // 不行。编译器错误。
array = {50, 60, 70}; // 不行。编译器错误。

4.

使用int *声明的变量可以指向静态分配的内存和动态分配的内存。而使用int []声明的变量只能引用静态定义和常量大小的内存。

这些差异同样适用于 int **array;int *array[];

4
这是一个详尽的列表,列出了它们相同的地方! - M.M
1
当声明为变量时,int **array;int *array[];不同,但可以用作指向初始化的指针数组中包含的每个有效指针的指针。 - David C. Rankin
13
为什么 int **array;int *array[]; 不一样? - Cristian Gutu
@CristianGutu 请查看更新后的答案。 - undefined

0
简单来说: 如果你想使用指针类型,你可以使用 *array 或 *ptr,或者其他变量名。
如果你想使用指针数组,并且在执行之前已经知道需要多少个指针(例如,10个指针),那么你可以使用 *array[10] 或 *ptr[10]。
如果你想使用指针数组,但是在执行之前不知道需要多少个指针,那么你可以使用 **array 或 **ptr; 然后,你可以使用 malloc 或 new 运算符为你正在使用的任何数据类型分配内存,(数据类型可能是原始的,如 int、char 等,也可能是用户定义的,如结构体等)。

这个答案可以通过适当的代码格式化来改进。 - user16217248

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