【问题标题】:How can a character array be subtracted from a pointer?如何从指针中减去字符数组?
【发布时间】:2020-02-06 12:59:01
【问题描述】:

所以,我开始熟悉 C,此时我正在尝试理解指针。我从here 得到以下代码,但我无法理解,如何从指针中减去字符数组。

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

main() 
{   
char s[30], t[20];   
char *found; 

/* Entering the main string */   
puts("Enter the first string: ");   
gets(s);

/* Entering the string whose position or index to be displayed */   
puts("Enter the string to be searched: ");   
gets(t);

/*Searching string t in string s */   
found=strstr(s,t);   
if(found)
    printf("Second String is found in the First String at %d position.\n",found-s);    
else
    printf("-1");   
getch(); 
}

指针不只是给定变量/常量的地址吗?当减法发生时,字符数组会自动假设,因为操作是用指针发生的,所以减去它的地址?我在这里有点困惑。

提前致谢。

【问题讨论】:

  • 首先,永远不要永远使用gets。它是a dangerous function,多年前甚至已从 C 规范中删除。使用例如fgets` 代替。
  • 因为数组变量保存在内存中的第一个数组元素地址。当减去内存中的两个地址时,您会得到这两个地址之间的位置数
  • 我读到了gets的用法,不打算使用它,但我想展示源代码不变的示例
  • 我现在明白了,谢谢,还没有考虑过:)
  • 请注意found - s 的值是ptrdiff_t 类型。 ptrdiff_t 的正确格式说明符是 %td,而不仅仅是 %d。请参阅stackoverflow.com/questions/7954439/… 使用错误的格式说明符是undefined behavior 的一种形式。

标签: c arrays pointers implicit-conversion pointer-arithmetic


【解决方案1】:

假设您想知道表达式 found-s,那么您会减去两个指针。

数组自然衰减为指向其第一个元素的指针。这意味着普通的s 等于&amp;s[0],这就是这里发生的情况:found-s 等于found - (&amp;s[0])

并且减法有效,因为found 指向数组s 中的一个元素,所以指针是相关的(这是指针减法的要求)。结果是两个指针之间的差异(以元素为单位)。

【讨论】:

    【解决方案2】:

    我无法理解,如何从指针中减去字符数组。

    从技术上讲,不能。但这并不会使呈现的代码无效。

    指针不只是给定变量/常量的地址吗?

    或多或少。指针是一个地址。有效的是某个对象或函数的地址。

    什么时候 减法发生字符数组自动假定 因为操作发生在一个指针上是减去它的地址?

    关闭。除了少数例外,无论它们出现在表达式中的何处,数组类型的值都会转换为指向第一个数组元素的指针。具体来说:

    除非它是sizeof 运算符的操作数,否则_Alignof 运算符,或一元 &amp; 运算符,或者是用于 初始化一个数组,一个类型为''array of type''的表达式是 转换为类型为 ''pointer to type'' 的表达式 到数组对象的初始元素并且不是左值。

    (C2011, 6.3.2.1/3)

    请注意,索引运算符[] 不属于例外情况。索引是一个指针操作。函数调用表达式同样也不例外,因此您实际上不能将数组传递给函数,无论看起来多么像这样做——相反,您最终传递了相应的指针。最直接的一点是,差异运算符- 也不例外,因此有问题的代码表示两个指针之间的差异,而不是数组和指针之间的差异。

    【讨论】:

    • C 2011 已取消。当前标准是 C 2018,并且从该段落中删除了“_Alignof_ 运算符”文本,因为数组对象永远不可能成为 _Alignof 的操作数。 (_Alignof 只接受类型操作数,不接受对象。)
    • 我觉得我或多或少地完全理解了指针。我写了一些代码来确保我得到它,而且很清楚。谢谢,没想到论坛这么简单:)
    【解决方案3】:

    C 编译器知道您正在使用的类型及其大小,因此当您进行指针运算时,C 编译器可以为您做一些智能的事情。

    例如,如果int *a = 0x10 那么a + 1 将给出0x14 而不是0x11。编译器知道 int 的大小是 4,因此当您将 1 添加到 int 指针的地址时,它会为您提供下一个 int 大小的对象的地址。

    同样,当您减去 2 个指针时(即found - s,编译器不会为您提供 2 个地址之间的字节数,它会为您提供它们之间的对象数,在 char(大小 1)的情况下为同样的事情。

    【讨论】:

      【解决方案4】:

      一个字符数组不能从一个指针中减去,但一个指针可以从另一个指针中减去,如果两个指针都指向同一个数组的元素或一个指向数组最后一个元素的元素。

      来自 C 标准(6.5.6 加法运算符)

      9 当两个指针相减时,两个指针都指向 相同的数组对象,或数组的最后一个元素 目的;结果是两者下标的差 数组元素。结果的大小是实现定义的,并且 它的类型(有符号整数类型)是在 标题。如果结果不能在对象中表示 该类型,行为未定义。换句话说,如果 表达式 P 和 Q 分别指向第 i 个和第 j 个元素 对于数组对象,表达式 (P)-(Q) 具有提供的值 i-j 该值适合 ptrdiff_t 类型的对象。此外,如果 表达式 P 指向数组对象的一个​​元素或一个 经过数组对象的最后一个元素,表达式 Q 指向 到同一数组对象的最后一个元素,表达式 ((Q)+1)-(P) 与 ((Q)-(P))+1 和 -((P)-((Q)+1)) 具有相同的值, 如果表达式 P 指向最后一个,则值为 0 数组对象的元素,即使表达式 (Q)+1 没有 指向数组对象的一个​​元素。106

      那么问题来了:found-s 表达式中的s 是什么?

      C 标准答案(6.3.2.1 左值、数组和函数指示符)

      3 除非它是 sizeof 运算符的操作数或一元 & 运算符,或者是用于初始化数组的字符串文字,an 具有“类型数组”类型的表达式被转换为 类型为“pointer to type”的表达式,指向初始 数组对象的元素 并且不是左值。如果数组 对象有注册存储类,行为未定义。

      所以在上面指出的表达式中,s 被转换为指向它的第一个元素的指针,事实上,为了清晰起见,表达式可以等效地重写

      found - &s[0]
      

      但是当然知道数组指示符到指针的这种隐式转换更容易编写

      found - s
      

      表达式的结果是两个指针之间的数组元素个数。

      有时初学者在对这种转换一无所知时会犯以下错误。他们写例如

      char s[] = "Hello";
      
      if ( s == "Hello" )
      {
          // ...do something
      } 
      

      然而,在 if 语句的条件下,存储的字符串不会被比较。比较了数组 s 的第一个元素和字符串字面量的第一个元素的地址。由于数组和字面量占用不同的内存范围,因此条件的结果为 false。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-20
        • 2015-03-01
        • 2013-06-22
        相关资源
        最近更新 更多