【问题标题】:Why isn't this pointer arithmetic allowed in C? [duplicate]为什么在 C 中不允许这种指针算术? [复制]
【发布时间】:2016-12-15 00:10:12
【问题描述】:
char arr[] = "Hello";
arr = arr + 1;  // error occurs

据我所知,具有数组类型的表达式被转换为指向数组初始元素的指针类型。因此,我希望arr = arr + 1(指向数组的第一个元素(arr)的指针成为指向数组的第二个元素的指针)能够工作。为什么这在 C 中不起作用?

【问题讨论】:

  • 因为谁教你的比数组和指针完全一样,过于简单化到了错误的地步。
  • 为什么我不能int i; &i = &i + 1;&i 是一个指针,对吧?
  • 指针算法没问题;问题在于作业
  • @MooingDuck 我确实意识到右侧发生了什么,但这不是 OP 的问题。问题在左边。数组不可赋值。因此,只要问题出在数组本身,就没有理由在这里谈论指针。即使 Array 名称是一个 Pointer,这并不意味着应该谈论这部分。下面的答案涵盖了所有这些。

标签: c arrays pointers


【解决方案1】:

arr + 1 确实是指向数组第二个元素的指针(即&arr[1])。

但是,这并不意味着您可以以某种方式将该指针值写回arr。你不能这样做至少有两个原因。

首先,arrchar 元素的数组,而不是指针。这里有一个明显的类型不匹配。

其次,作为一个数组,arr 一个 不可修改 左值。你不能改变arr 本身,你只能改变它的元素(这个区别有点难以理解,但它就在那里)。

最后,如果我们忽略更深层次的复杂性并专注于在顶层正式发生的事情,由于 array type decay 你的表达相当于

(char *) arr = (char *) arr + 1;

赋值是不可能的,因为左边是 [隐式] 类​​型转换的结果。在 C 类型转换中总是产生右值。您不能分配给右值。

换句话说,这里不允许的不是“指针算术”。指针算法很好。正是你对导致错误的指针算法的结果所做的事情。

【讨论】:

  • 那么这里可以说arr是一个常量数组变量吗?
  • @AbdulGafoor:最好不要,因为在 C 的上下文中说“常量数组变量”听起来像是 const char [6],而实际上它是 char [6]。不是数组是不变的,而是任何类型的数组变量都不能赋值。
  • @AbdulGafoor 你可以说arr 是一个“不可修改的左值”。获得您想要的效果的唯一方法是例如char arr[] = "Hello"; char *ptr = arr; ptr = ptr + 1; 并与 ptr 合作。
  • @AbdulGafoor 你唯一应该知道的是 arr 是一个数组而不是一个指针,而且数组是不可分配的
  • 所以修改arr是左值错误。
【解决方案2】:

数组是不可修改的左值。它们不能是赋值运算符的左操作数。

C11-§6.3.2.1:

可修改的左值是一个左值 没有数组类型,没有不完整的类型,[...]

§6.5.16/2:

赋值运算符应该有一个可修改的左值作为其左操作数。

在声明中

arr = arr + 1; 

arr= 运算符的左操作数,是数组类型。无法修改。
因此,语法错误的原因不是指针算法,而是语言对赋值运算符的约束。


请注意,在某些情况下,数组会衰减为指向其第一个元素的指针,尽管指针和数组是不同的类型。 数组不是指针。只有指针算法和数组索引是等价的。例如

char *ptr = &arr[0] + 1 => &(*(arr + 0)) + 1 => &(*arr) + 1 => arr + 1 // * and & nullify each other 

【讨论】:

  • 您甚至可以称其为“数组索引语法”:它将数组衰减为一个指针,然后对其进行索引。
【解决方案3】:

这是因为数组类似于指针,只是它们不能被修改。但是,您可以修改指向数组的指针。对于上面的例子,你可以这样做:

char arr[]="Hello";
char *ptr=arr;
ptr=ptr+1;

最初,指针ptr 将指向数组的第一个字符,即'H',修改值后,它将指向第二个字符,即'e'。您还可以执行以下操作:

char arr[]="Hello";
char *ptr=arr;
ptr=arr+1;

两者产生相同的效果,这表明arr+1 确实是指针运算。但是你不能修改arr 的值,因为它的类型是字符数组,而不是指向字符数组的指针。

【讨论】:

    【解决方案4】:

    据我所知,具有数组类型的表达式被转换为指向数组初始元素的指针类型。

    在大多数情况下都是如此。在以下情况下不正确:

    1. 使用 addressof 运算符 (&arr) 时。 &arr 的类型是 char (*)[6]。不是char**

    2. 使用 sizeof 运算符时。 sizeof(arr)6。如果它是一个指针,它将是一个指针的大小(在大多数常见平台上为 4 或 8)。

    3. 用作赋值运算符的 LHS 时。数组类型的变量是不可修改的。

    因此,我希望arr = arr + 1(指向数组的第一个元素(arr)的指针变成指向数组的第二个元素的指针)能够工作。为什么这在 C 中不起作用?

    表达式的 RHS 计算为指向 arr 的第二个元素的指针。但是,由于上述(3),该行不起作用。 arr 不是可修改的值。它不能用作赋值运算符的 LHS。

    【讨论】:

      【解决方案5】:

      char[] 不是指针,而char* 是指针。 这可行,但它是错误的解决方案:

      int main()
      {
          char *arr = "Hello";
          arr = arr + 1;  // Wrong!
          printf("%s\n", arr); // Output: ello
      }
      

      如果arr 使用malloc 进行堆分配,如果free 内存从arr+1 而不是arr 开始,则可能会发生内存泄漏。

      但是你可以这样做:

      int main()
      {
          char arr[] = "Hello";
          char *wordFromSecondLetter = &arr[0] + 1;
          printf("%s\n", wordFromSecondLetter); // Output: ello
      }
      

      或者像这样

      int main()
      {
          char arr[] = "Hello";
          printf("%s\n", &arr[1]); // Output: ello
      }
      

      【讨论】:

        【解决方案6】:

        因为arr 不是指针而是字符数组。您可以通过检查sizeof arr 来验证这一点。要获取指向 char 的指针,您应该使用 char *arr = "Hello";

        指针和数组最大的区别在于,可以直接给指针赋值,但不能对数组这样做。

        事实上,当你写arr + 1arr“衰减”指向它的第一个元素的指针,也就是arr == &arr[0]。所以arr + 1是合法的指针运算,但是将其值赋予arr,这是一个数组,是非法的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多