结构中指针表示法和点表示法之间的混淆[重复]

Posted

技术标签:

【中文标题】结构中指针表示法和点表示法之间的混淆[重复]【英文标题】:Confusion between Pointer Notation and Dot Notation in Structs [duplicate] 【发布时间】:2021-12-19 07:56:11 【问题描述】:

我正在尝试将 Struct 中的所有名称设为小写,以便我可以比较它们并删除它们。

int removeNameCard(NameCard *idCard, int *size)
    char name[80];
    char *ptr;
    char rubbish;
    int a = 0, c = 0;
    printf("removeNameCard():\n");
    printf("Enter personName:\n");
    scanf("%c", &rubbish); // Why is there a '\n' char here??
    fgets(name, 80, stdin);
    if((ptr = strchr(name, '\n')))
        *ptr = '\0';
    
    if((*size) == 0)
        printf("The name card holder is empty\n");
        return 0;
    
    // Convert everything to Lower Case first
    while(name[a])
        name[a] = tolower(name[a]);
        a += 1;
    
    printf("tolower(): %s", name);
    for(int b = 0; b < *size; b += 1)
        // Why is this Dot Notation when I passed in a pointer to the Struct?
        while (idCard[b].personName)[c])
            (idCard[b].personName)[c] = tolower((idCard[b].personName)[c]);
            c += 1;
        
    
    for(int i = 0; i < *size; i += 1)
        if((idCard[i].personName) == name)
                                    printf("%d. This is from Holder: %s, This is from User: %s", i,(idCard[i].personName),name);
            printf("The name card is removed\n");
            printf("nameCardID: %d\n", idCard[i].nameCardID);
            printf("personName: %s\n", idCard[i].personName);
            printf("companyName: %s\n", idCard[i].companyName);
            int k = 0;
            do
                idCard[i+k].nameCardID = idCard[i+k+1].nameCardID;
                strcpy((idCard[i+k].personName),(idCard[i+k+1].personName));
                strcpy((idCard[i+k].companyName),(idCard[i+k+1].companyName));
            while((i+k+1) != (*size + 1));
        
    
    return 0;

但是,我很困惑为什么编译器要求我使用点表示法而不是指针表示法,因为我认为我将 Struct 的地址传递到 *idCard 所以如果我没记错的话它应该是一个指针?

我尝试像这样访问 Struct 的每个名称中的每个单独的字符是不是错了? (身份证[b].personName)[c]

谢谢

【问题讨论】:

我不禁想到你的minimal reproducible example 可能会更小。 如果idCardNameCard结构的数组,那么idCard[b]就是这样一个NameCard结构objectidCard[b] 不是指向结构的指针(将使用“箭头”运算符 -&gt;)。这就是为什么在idCard[b].personName 中应该使用“点”运算符. 另外,(idCard[b].personName)[c] 不需要括号,idCard[b].personName[c] 也可以。 顺便说一句:if((idCard[i].personName) == name) 不是比较字符串的方法, @Antoine 是的,它极大地帮助了我理解这两个运算符存在的原因,尽管我可能需要编写更多代码才能完全理解评论的完整解释。非常感谢,我学到了很多:> 【参考方案1】:

但是,我很困惑为什么编译器要求我使用点表示法而不是指针表示法,因为我认为我将 Struct 的地址传递到 *idCard 所以如果我没记错的话它应该是一个指针?

数组大多只是指向数组中第一个元素的指针(编译器可能知道数组大小除外)。

因为数组大多只是指针;对于整数数组myInt = myIntArray[x]; 就像myInt = *(myIntArray + x); - 指针取消引用由数组索引暗示。请注意,要访问数组中int 中间的char,您可以(不可移植地)使用myChar = *((char *)(myIntArray + x)) + offset_of_char_in_int); 之类的东西;这有点像访问结构数组中的字段(因为它们都在访问较大事物数组中的较小事物)。

对于结构数组;索引数组会导致取消引用(就像它对整数数组所做的那样);所以myIDcardStruct = idCard[i]; 就像myIDcardStruct = *(idcard + i);。因为数组索引隐含了取消引用,myIDcardStruct 不是指针。

-&gt; 运算符就像将请求结构的字段的偏移量添加到地址,将地址转换为请求字段的类型,然后取消引用。换句话说,myInt = myStructPointer-&gt;myIntField; 就像 myInt = (*myStructPointer).myIntField; 就像 tempAddress = (void *)myStructPointer + offset_of_myIntField; myInt = *((int *)tempAddress);

这意味着如果你有一个结构数组(主要是指向数组中第一个结构的指针),索引数组会导致指针被隐式取消引用,使用-&gt;也会导致隐式取消引用;如果你两者都做,那么你已经(隐式地)取消引用了两次指针,这太过分了(因为它不是指向结构指针的指针,也不是指向结构的指针数组)。因为您只希望在必须在一个隐式引用(数组索引)或另一个隐式取消引用(-&gt;)之间进行选择时取消引用它;例如您可以在myInt = idCard[i].nameCardID;myInt = (idCard + i)-&gt;nameCardID; 之间进行选择。

当然重要的是让代码易于阅读,myInt = idCard[i].nameCardID;myInt = (idCard + i)-&gt;nameCardID; 更易于阅读。

【讨论】:

我们必须不断纠正“数组是指针”的概念,而写“数组大多只是指针”会损害这种努力。它不应该出现在这个答案中。足以说明idCard[i] 是一个结构对象,而不是指向它的指针,因此使用idCard[i].member 而不是idCard[i]-&gt;member 访问结构的成员。 @EricPostpischil:这种努力是错误的(弊大于利,尤其是当人们试图了解他们的代码实际上要求 CPU/硬件做什么时),应该受到损害。 我以前从未想过这样的数组,而且我认为我还没有完全理解所有内容。但是,我认为这是一本内容丰富的读物,我将尝试在将来编写更多代码时参考这两个答案!谢谢:>【参考方案2】:

但是,我很困惑为什么编译器要求我使用点表示法而不是指针表示法......

idCard[i] 是一个结构,而不是一个指向结构的指针,所以它的成员被访问为idCard[i].<i>member</i>,而不是idCard[i]-&gt;<i>member</i>

idCard[i] 是一个结构,因为每当x 是一个指针时,x[i] 就是x 指向的对象之一。它不是对象的地址。您可以使用x+i 计算对象的地址,然后您可以使用*(x+i) 引用该对象。而x[i]其实就是这样定义的; x[i] 定义为 *(x+i)。 (一般在表达式的情况下,<i>E1</i>[<i>E2</i>] 定义为(*((<i>E1</i>)+(<i>E2</i>)))。)

我尝试像这样访问 Struct 的每个名称中的每个单独字符是不是错了?:(idCard[b].personName)[c]

这会起作用,但括号是不必要的。您可以使用idCard[b].personName[c]。由于 C 语法,它已经被分组为(idCard[b].personName)[c]

【讨论】:

非常感谢您的澄清。但是,我仍然有点困惑。你的意思是如果x 是一个指向Struct 的指针,它会和Struct 有相同的成员,这就是为什么它可以使用x[i] 指向各种对象? 抱歉,为什么x+i 会给我对象的地址?我的印象是,如果我没有指定具体指向哪个成员/对象,指针将指向 Struct 的第一个成员?非常感谢 @Michelin_Boi:如果x 是指向结构的指针,则它没有任何成员。指针只是指向内存中的一个位置;它不包含成员。如果x 是指向结构的指针,则*x 指的是该结构。所以*x 就是那个结构,*x 有成员。所以(*x).personName 是会员。另外,x[0]*x 的结构相同,所以x[0].personName 是一个成员。 @Michelin_Boi: x+i 使用地址算法。如果我们有一个结构数组,比如数组A,而x 的类型是指向该类型结构的指针,那么我们可以让x 指向数组的任何元素。例如,x = &amp;A[3];x 设置为指向A[3]。地址运算的规则是,如果x 指向元素A[t],那么x+i 计算一个指向A[t+i] 的指针。在x = &amp;A[3]; 之后,然后x+2 指向A[5]。因此,如果x 指向A[0],那么x+i 指向A[0+i],等于A[i]。所以x+i 指向A[i],而*(x+i) A[i] 哦,我明白了,这很有意义!非常感谢您花时间向我解释!! :>

以上是关于结构中指针表示法和点表示法之间的混淆[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在C中使用指针表示法迭代双指针[重复]

计算当前位置和点之间的距离[重复]

数组是指针? [重复]

常量指针的混淆[重复]

javascript中点表示法和括号表示法之间的区别[重复]

Leetcode——寻找重复数