【问题标题】:Address of a string literal and array字符串文字和数组的地址
【发布时间】:2020-07-25 21:25:24
【问题描述】:
    int main(){
        char *str1="Hi", *str2 = "Bye";
        printf("%u,%u\n",&str1,str1);
        int arr[5]={1,2,3,4,5};
        printf("%u,%u",arr,&arr);
    }

这里发生了什么? str&str 给出不同的地址,arr&arr 给出相同的地址。

我的理解是arr 指向第一个元素的地址,即&arr[0]&arr 也会给出相同的地址,但它是整个arr[5] 的地址。如果我们将&arr 增加 1,那么它将指向 arr[4] 的下一个元素。但问题是为什么这个过程在字符串的情况下是不同的。请帮助我在这里形象化这个概念。

【问题讨论】:

标签: c arrays string pointers string-literals


【解决方案1】:

在 C 语言中,所有字符串文字实际上都存储为(只读)字符数组,包括空终止符。与任何其他数组一样,它们会衰减为指向其第一个元素的指针。

对于您代码中的str1,编译器生成了一些类似于此的代码:

// Compiler-generated array for the string
char global_array_for_Hi[] = { 'H', 'i', '\0' };

int main(void)
{
    char *str1 = global_array_for_Hi;
    ...
}

您的指针变量str1 指向该数组的第一个元素('H')。当您打印 str1 的值时,这就是您得到的值。

当您打印&str1 的值时,您将获得变量str1 本身的位置,您将获得指向str1char ** 类型的指针。

从图形上看,它看起来像

+-------+ +-----+ +-----+-----+------+ | &str1 | --> | str1 | --> | 'H' | '我' | '\0' | +-------+ +-----+ +-----+-----+------+

对于arr,您有一个数组,它衰减为指向其第一个元素的指针。当您使用arr 时,它与&arr[0] 相同(这是因为arr[i] 完全等于*(arr + i))。 arr(和&arr[0])的类型是int *

当你使用&arr 时,你会得到一个指向整个数组的指针,它的类型是int (*)[5]

&arr[0]&arr 的位置相同,但它们的类型非常不同。


在相关说明中,printf 格式说明符 %u 用于打印 unsigned int 类型的值。要打印指针(更具体地说是void * 类型的值),您必须使用格式说明符%p。格式说明符和参数类型不匹配会导致未定义的行为

【讨论】:

  • 如果我定义 str1[]="Hi" 那么它的行为就像我提到的数组一样?
  • 我是 C 新手。为什么你/编译器将 char 数组声明为全局的?
  • @MichaelSchäfer 所有文字字符串都是“全局”的,并且具有整个程序的生命周期。参见例如this string literal reference了解详情。
  • @Someprogrammerdude 从您的链接 字符串文字不可修改。像char *str1="Hi" 这样的字符串文字是不可修改的。在您的示例中 str1 是可修改的。你在声明数组时忘记了一个常量,还是编译器以不同的方式处理它?
  • @MichaelSchäfer 问题是编译器创建的字符串数组不是const。尝试修改一个会导致未定义的行为,因此它们实际上是只读的(正如我提到的),但它们不是const。除了const,标准C 中没有办法将数组标记为“不可修改”,但由于它们不是const,所以我省略了。
【解决方案2】:

array 不是指针。它是连续的内存块。这就是array && &array地址相同的原因

pointer 是一个保存引用的单独对象。所以pointer - 给你指针持有的引用,&pointer 给你对pointer 本身的引用。由于指针是一个单独的对象,因此您有不同的地址

【讨论】:

    【解决方案3】:

    在 C 中,像“Hi, Guys”这样的常量字符串存储在共享内存中。 考虑以下示例:

    str = "Hi, there";
    

    上面代码中的字符串是连续存储的。变量str 指向 字符串的第一个字符,所以这里是字符'H'。 因此,str 给出了存储在内存某处的第一个字符的地址。 &str 给出变量 str 本身的地址。

    数组中的大小写与上述不同。 数组是变量(当然,常量变量),它包含数组的第一个元素(即 &arr[0])的地址。当您执行&arr 时,它将与&arr[0] 相同。 &arr 实际上是整个数组的地址,与数组第一个元素的地址相同。

    注意:打印&arr + 5&arr[0] +5,你可能会得到一些光。

    【讨论】:

    • 是的 ravi 我知道数组的行为方式,但我对 str[] 和 *str 感到困惑,我认为它们的工作方式相同,但它们不同。顺便说一句,感谢您的回答。
    【解决方案4】:

    1.

    char *str1="Hi";
    printf("%u,%u\n",&str1,str1);
    

    首先,您使用了错误的转换说明符,%u 用于 str1&str1,这会调用未定义的行为。对于str1,应为%s,对于&str1,应为%p

    char *str1="Hi";
    printf("%p,%s\n",(void*) &str1, str1);
    

    解释:

    str1 是指向字符串文字"Hi" 的第一个元素的地址的指针。 &str1 是指针 str1 本身的地址。这就是与下面数组的版本的区别。


    2.

    int arr[5]={1,2,3,4,5};
    printf("%u,%u",arr,&arr);
    

    这里又是错误的转换说明符。如果要打印arr 的第一个元素,则应为%d%i,因为arrint 的数组,而不是unsigned int%p,如果要打印第一个元素:

    int arr[5]={1,2,3,4,5};
    printf("%p,%p",(void*) arr, (void*) &arr);
    

    解释:

    arr(在数组到指针衰减规则之后)衰减为指向arr 的第一个元素的指针,而&arr 实际上是指向arr 的第一个元素的指针。他们实际上评估相同。


    注意,为了使代码符合 C 标准,必须转换为 void*

    【讨论】:

      【解决方案5】:

      在此声明中

      char *str1="Hi", *str2 = "Bye";
      

      声明了两个局部变量str1str2,具有自动存储持续时间。

      它们由具有静态存储持续时间的字符串文字的第一个字符的地址初始化。

      所以str1 的值是字符串文字"Hi" 的第一个字符的地址。表达式&str1的值就是局部变量str1本身的地址。

      你可以这样想象

      &str1 ---> str1 ---> "Hi"
      

      数组是内存的连续范围。所以数组本身的地址和它的第一个元素的地址是一样的。是数组占用的内存范围的地址。

      你可以这样想象

              | 1 | 2 | 3 | 4 | 5 |
              ^
              |        
      &arr----
              ^
              |
      arr-----
      

      请注意,表达式中使用的数组指示符(极少例外)将转换为指向其第一个元素的指针。所以使用din printf的调用表达式arr等价于&arr[0]

      关于你的评论

      Vlad 我对字符串常量有疑问,为什么我们不能修改它 char *s="HI" 但我们可以修改这个 char *s[]="HI" 我知道第二个 如果它是简单的数组,但请你清楚为什么我不能修改 字符串常量

      然后根据 C 标准(6.4.5 字符串文字)

      7 不确定这些数组是否是不同的,前提是它们的 元素具有适当的值。 如果程序试图 修改这样的数组,行为未定义。

      注意这个声明

      char *s[]="HI";
      

      无效。声明了一个指针数组。所以要初始化它你必须写

      char * s[] = { "HI" };
      

      您可以通过分配其他字符串文字来更改数组的元素。也就是说,您可以更改指针本身,而不是指针指向的字符串文字。

      【讨论】:

      • Vlad 我对字符串常量有疑问,为什么我们不能修改这个 char *s="HI" 但我们可以修改这个 char *s[]="HI" 我知道第二个如果它是简单的数组,但请你清楚为什么我不能修改字符串常量。
      • @SanjayVerma 查看我附加的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多