【问题标题】:Is char pointer address initialization necessary in C?C中是否需要char指针地址初始化?
【发布时间】:2013-09-01 15:44:12
【问题描述】:

我正在以自学的方式学习 C 编程。我知道数字指针地址必须始终被初始化,无论是静态的还是动态的。

但是,我还没有读到关于初始化 char 指针地址的强制需求。

例如,这段代码是正确的,还是需要指针地址初始化?

char *p_message;
*p_message = "Pointer";

【问题讨论】:

    标签: c pointers allocation memory-address


    【解决方案1】:

    我不完全确定您所说的“数字指针”与“字符指针”是什么意思。在 C 中,char 是整数类型,因此它是算术类型。在任何情况下,指针都不需要初始化,无论它是否是指向char 的指针。

    你的代码有错误使用*p_message而不是p_message来设置指针的值:

    *p_message = "Pointer"          // Error!
    

    这是错误的,因为鉴于 p_message 是指向 char 的指针,*p_message 应该是 char,而不是整个字符串。但就首次声明时需要初始化char 指针而言,这不是必需的。所以这样就好了:

    char *p_message;
    p_message = "Pointer";
    

    我猜你的部分困惑是因为这不合法:

    char *p_message;
    *p_message = 'A';
    

    但是,这与指针是否正确初始化无关。即使作为初始化,这也会失败:

    char *p_message = 'A'; 
    

    由于与int *a = 5; 错误相同的原因,它是错误的。那为什么错了?为什么会这样:

    char *p_message;
    p_message = "Pointer";
    

    但这失败了?

    char *p_message;
    *p_message = 'A';
    

    这是因为没有为'A' 分配内存。当您有p_message = "Pointer" 时,您将分配p_message 字符串文字"Pointer" 的第一个字符'P' 的地址。字符串文字存在于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要专门在堆栈或堆上分配。

    但是chars 和ints 一样,需要分配在堆栈或堆上。要么您需要声明一个char 变量,以便堆栈上有内存:

    char myChar;
    char *pChar;
    pChar = &myChar;
    *pChar = 'A';
    

    或者你需要在堆上动态分配内存:

    char* pChar;
    pChar = malloc (1); // or pChar = malloc (sizeof (char)), but sizeof(char) is always 1
    *pChar = 'A';
    

    所以在某种意义上,char 指针与intdouble 指针不同,因为它们可以用来指向字符串字面量,您不必为此在堆栈上分配内存(静态)或堆(动态)。我认为这可能是您的实际问题,与内存分配而不是初始化有关。

    如果您真正询问的是初始化而不是内存分配:就初始化而言,指针变量与任何其他变量没有什么不同。就像未初始化的int 变量在初始化之前会有一些垃圾值一样,指针在初始化之前也会有一些垃圾值。如您所知,您可以声明一个变量:

    double someVal;    // no initialization, will contain garbage value
    

    在代码后面有一个赋值来设置它的值:

    someVal = 3.14;
    

    同样,使用指针变量,你可以有这样的东西:

    int ary [] = { 1, 2, 3, 4, 5 };
    int *ptr;          // no initialization, will contain garbage value
    ptr = ary;
    

    这里,ptr 没有被初始化为任何东西,但后来被分配了数组第一个元素的地址。

    有些人可能会说初始化指针总是好的,至少到NULL,因为您可能会在指针被分配任何实际(非垃圾)值之前无意中尝试取消引用,并且取消引用垃圾地址可能会导致您的程序崩溃,或者更糟的是,可能会损坏内存。但这与在声明 int 变量时始终将其初始化为零的警告并没有什么不同。如果您的代码在按预期设置变量值之前错误地使用了变量,我不确定该值是否为零、NULL 或垃圾是否重要。


    编辑。 OP 在评论中询问:您说“字符串文字存在于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要专门在堆栈或堆上分配”,那么如何分配发生了吗?

    这就是语言的工作原理。在 C 中,字符串文字是语言的一个元素。 C11 标准在第 6.4.5 节中规定,当编译器将源代码翻译成机器语言时,它应该将双引号中的任何字符序列转换为 char 的静态数组(或 wchar_t,如果它们是宽字符)并附加一个NUL 字符作为数组的最后一个元素。这个数组然后被认为是不可变的。标准说:If the program attempts to modify such an array, the behavior is undefined.

    所以基本上,当你有这样的陈述时:

    char *p_message = "Pointer";
    

    该标准要求将双引号字符序列"Pointer" 实现为内存中某处的char 的静态、不可变、NUL 终止数组。通常,实现将此类字符串文字放置在内存的只读区域中,例如文本块(以及程序指令)。但这不是必需的。给定实现处理此数组/NUL 终止序列的char/字符串文字的确切方式取决于特定的编译器。但是,因为这个数组存在于内存中的某个地方,你可以有一个指向它的指针,所以上面的语句确实有效。

    与函数指针的类比可能有用。正如函数的代码作为指令序列存在于内存中的某处,并且您可以拥有指向该代码的函数指针,但您不能更改函数代码本身,因此字符串文字也作为序列存在于内存中的char 并且您可以拥有一个指向该字符串的char 指针,但您不能更改字符串文字本身。

    C 标准仅针对字符串文字指定此行为,而不针对 'A' 之类的字符常量或5 之类的整数常量。留出内存来保存这些常量/非字符串文字是程序员的责任。因此,当编译器遇到如下语句时:

    char *charPtr = 'A';    // illegal!
    int *intPtr = 5;        // illegal!
    

    编译器不知道如何处理它们。程序员没有在堆栈或堆上留出这样的内存来保存这些值。与字符串文字不同,编译器也不会为它们留出任何内存。所以这些说法是非法的。

    希望这更清楚。如果没有,请再次发表评论,我会尝试澄清更多。

    【讨论】:

    • 是的,我打错了,我不明白为什么我不需要分配字符串文字。感谢您的帮助,但我仍然不完全了解它们的分配是如何工作的。你说 String 字面量存在于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要专门分配在堆栈或堆上,那么分配是如何发生的呢?
    【解决方案2】:

    初始化不是需要,不管指针指向什么类型。唯一的要求是您不得尝试使用未初始化的指针(从未分配给任何东西)。

    但是,出于美观和维护的原因,应始终在可能的情况下进行初始化(即使只是对NULL)。

    【讨论】:

    • 在实践中,总是初始化(通常是NULL)指针变量是 IMNSHO 的一个好习惯。请注意,如果该初始化是无用的(因为变量是在第一次使用之前设置的)优化编译器会跳过它。
    • 那么,在上面的例子中,我应该将它的地址初始化为另一个 char 变量,对吗?
    • @user2718426:在上面的例子中,你应该只做char *p_message = "Pointer";
    • 准确地说,您不应该使用未初始化且从未分配过值的指针。 (初始化和赋值是不同的东西。你可以在赋值后使用未初始化的指针。)
    • @OliCharlesworth 哦,那是一个打字错误,反正这样,我不知道指针的地址。像char = var; p_message = &var 这样的东西不是更好吗?
    【解决方案3】:

    首先,char 是数字类型,所以你的问题中的区别没有意义。如所写,您的示例代码甚至无法编译:

    char *p_message;
    *p_message = "Pointer";
    

    第二行是违反约束的,因为左边是算术类型,右边是指针类型(实际上,最初是数组类型,但在这种情况下它衰减为指针类型)。如果你写了:

    char *p_message;
    p_message = "Pointer";
    

    那么代码是完全有效的:它使p_message 指向字符串文字。但是,这可能是也可能不是您想要的。另一方面,如果你写了:

    char *p_message;
    *p_message = 'P';
    

    char *p_message;
    strcpy(p_message, "Pointer");
    

    然后代码将通过(第一个示例)将* 运算符应用于无效指针或(第二个示例)将无效指针传递给需要指向对象的有效指针的标准库函数来调用未定义的行为能够存储正确数量的字符。

    【讨论】:

      【解决方案4】:

      不需要,但仍建议用于简洁的编码风格。 此外,您发布的代码完全错误且无法正常工作,但您知道这一点并且只是将其作为一个简单的示例来写,对吧?

      【讨论】:

        猜你喜欢
        • 2011-04-10
        • 2012-06-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-09
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        相关资源
        最近更新 更多