【问题标题】:Detailed difference between pointer and array指针和数组的详细区别
【发布时间】:2017-05-31 03:03:28
【问题描述】:

下面是C语言代码:

void test1(void);

int main(){
    test1();
    return 0;
}

void test1(void){
    char c,
        *p = &c,
        *sp1[3] = {"this 1","this 2", "this 3"},    //pointers array
        (*sp2)[5] = {"this 1","this 2", "this 3"},  
        //2 dimensions array declaration in method 1 
        sp3[][5] = {"this 1","this 2", "this 3"};   
        //2 dimensions array declaration in method 2

    p++;    //ok 
    sp1++;  //error 
    sp2++;  //ok
    sp3++;  //error     
}

我读过指针是一个变量,所以增量是可以的,但数组名不是变量,所以赋值是不可接受的。

在上面的代码中,p是一个指针,所以增量是对的; sp1,sp3都是数组名,自增会报错,但是为什么sp2可以呢?

在同一本书中,有如下功能:

//print lines 
void writelines(char *lineptr[],int nlines){
    while(--nlines >= 0){
        printf("%s\n",*lineptr++);
    }
}

在这个函数中,lineptr 是一个数组的名称,但是在它上面应用了一个增量。为什么?是不是因为C中参数是按值转换的,所以lineptr其实是一个指针?

【问题讨论】:

  • stackoverflow.com/questions/581657/… 这可能会帮助您更清楚地理解指针。
  • *sp1[3]指向 char 的指针数组(*sp2)[5]指向 char 类型数组 5 的指针sp3[][5] 是一个二维数组(数组数组 char[5])。 注意:,在最后两种情况下,当您未能提供足够的存储空间时,您都会调用未定义的行为,例如"this 2"(对于 nul-terminating char 是 6 个字符 + 1 个字符)。您的5 至少需要为7
  • char cc 声明为 未初始化 字符。 *p = &c 创建 p 作为 指向 char 的指针(只有 1 个字符的存储空间),p++; 导致 p 指向为 c 分配的存储空间之外,任何尝试取消引用 p 会调用未定义的行为。
  • "在这个函数中 lineptr 是一个数组的名称" -- 是和不是。 lineptr 可能是一个 指针数组,每个指针指向 nlines 行之一的开头,但是当作为参数传递给函数时,数组被转换为 指针,所以您在函数中实际拥有的是一个 指向 char 指针的指针char **,您可以轻松地递增它。 (如果是二维数组,参数应该是char (*lineptr)[WIDTH], int nlines...

标签: c arrays pointers


【解决方案1】:

除了简单地与 指针 和数组搏斗以尝试将你的头脑围绕在它们的使用上(这是你学习 C 语言时间的一个极好用途)之外,你的代码试图完成什么还不清楚。

考虑到这一点,让我们看看您的声明并尝试帮助理解它们。首先让我们看看cp(我已经为你初始化了'c'以避免使用未初始化的值):

    char c = 'a',
        *p = &c,

c 被简单地声明为char,并自动存储在函数堆栈上创建的1-charp 被声明为字符指针(或指向字符的指针),其值被初始化为地址 c

(指针只是一个变量,它保存其他东西的地址作为它的值。一个普通变量,例如c保存一个立即值,在这种情况下'a'p只是保存地址c 作为它的值,例如p 指向c)

接下来声明一个指针数组,这些指针被初始化以保存initializer中提供的字符串字面量的地址,例如 p>

        *sp1[] = {"this 1","this 2", "this 3"},

注意:如果您提供每个指针的初始化,则无需指定 3

在这里,您有效地将 3-pointers 数组声明为 char *。其中sp1[0] 指向"this 1"sp1[1] 指向"this 2",等等...

接下来,您声明一个 指向 5 个字符数组的指针(或指向 5 个字符数组的指针)注意: 如我的评论所示,这还不够 2 -chars,因此它被声明为指向下面的7-chars 数组的指针。现在这里是您需要注意的地方。由于您声明了一个 指向数组的指针,它本身没有为 7 个字符分配任何存储空间,(它只是一个指向已经包含 7 个字符的东西的指针)。你有几个选择。

由于sp3char[][7] 类型,您可以在sp3 之后声明sp2 并为其分配兼容数组的地址,例如

    *sp1[] = {"this 1","this 2", "this 3"},
    sp3[][7] = {"this 1","this 2", "this 3"},
    (*sp2)[7] = sp3,  

sp2 指向sp3 的开头,或者您可以使用C99 复合文字,为sp2 声明之后的文字创建存储空间,例如

        *sp1[] = {"this 1","this 2", "this 3"},
        /* using a compound literal to create storage */
        (*sp2)[7] = (char [][7]){"this 1","this 2", "this 3"},  
        sp3[][7] = {"this 1","this 2", "this 3"},

最后,您可以为sp2 动态分配存储空间(我们将在下次讨论)。现在,由于您只是在这里与指针搏斗,因此您不能简单地 increment sp3 它是一个数组,但是...您可以声明一个指向其开头的指针并将其递增作为您的一部分运动,例如

        sp3[][7] = {"this 1","this 2", "this 3"},
        *p3 = *sp3; /* pointer to first element in sp3 */

现在将拼图的所有部分放在一起并练习每个指针的增量,您可以执行类似于以下的操作。 (注意:在增加 p++ 之后,我不会取消引用 p 的值,而是取消引用 p - 1 以便它仍然指向有效的存储空间)

#include <stdio.h>

void test1 (void);

int main (void) {
    test1();
    return 0;
}

void test1 (void) {
    char c = 'a',
        *p = &c,
        *sp1[] = {"this 1","this 2", "this 3"},
        /* using a compound literal to create storage */
        (*sp2)[7] = (char [][7]){"this 1","this 2", "this 3"},  
        sp3[][7] = {"this 1","this 2", "this 3"},
        *p3 = *sp3; /* pointer to first element in sp3 */

    p++;        /* advance p to one-char-past-c     */
    (*sp1)++;   /* advance pointer to sp1[0] by one */
    sp2++;      /* advance pointer to next char[7]  */
    p3++;       /* advance pointer to sp3[0] by one */

    printf ("(p - 1): %c\n*sp1: %s\n*sp2: %s\np3  : %s\n",
            *(p - 1), *sp1, *sp2, p3);
}

使用/输出示例

$ ./bin/arrayptrinc
(p - 1): a
*sp1: his 1
*sp2: this 2
p3  : his 1

注意:如果您选择在sp3 之后声明sp2 并让sp2 指向sp3 的开头,则输出相同:

    char c = 'a',
        *p = &c,
        *sp1[] = {"this 1","this 2", "this 3"},
        sp3[][7] = {"this 1","this 2", "this 3"},
        (*sp2)[7] = sp3,  
        *p3 = *sp3; /* pointer to first element in sp3 */

虽然我不确定这是否正是您的想法,但我认为这与您的锻炼目标很接近。如果没有,请告诉我。如果您有任何其他问题,请仔细查看并告诉我。

【讨论】:

  • 非常感谢。这是我第一次在“stackoverflow”上提问,我对你的热情和专业的回答感到惊讶,我学到了很多东西,包括很多我以前没有意识到的细节。衷心感谢。
  • 很高兴为您提供帮助。 C 可能是您可以学习的最优雅、最灵活的语言之一。它为您提供了对内存的位级控制,但它要求您测量和说明您使用的内存。无论您选择哪种语言,C 都会使您成为更好的程序员,因为您将学会如何在字节和位级别上计算和管理内存。祝你编码顺利。
猜你喜欢
  • 2011-09-22
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
  • 2021-05-06
相关资源
最近更新 更多