在C语言中,char指针地址初始化是否必要?

3
我正在自学C编程。我知道数值指针地址必须始终静态或动态地初始化。
然而,我还没有看到有关必须初始化char指针地址的强制性要求的相关内容。
例如,这段代码是否正确,是否需要指针地址初始化?
char *p_message;
*p_message = "Pointer";
4个回答

6

我不太确定你所说的“数字指针”与“字符指针”的区别。在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'的地址。 字符串字面值位于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要在堆栈或堆上专门分配。
但是像int一样,char也需要在堆栈或堆上分配。 要么需要声明一个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字符作为数组的最后一个元素。然后,此数组被视为不可变。标准说:如果程序尝试修改这样的数组,则行为未定义。

因此,基本上,当您有像这样的语句时:

char *p_message = "Pointer";

标准要求将由双引号括起来的字符序列 ""Pointer"" 实现为一个静态、不可变、以 NUL 结尾的 char 数组,存储在内存中的某个位置。通常情况下,实现会将这样的字符串字面量放置在只读内存区域,例如文本块(与程序指令一起)。但这并非必须。对于特定编译器如何处理此数组/以 NUL 结尾的 char/字符串字面量的内存分配方式,取决于该编译器的具体实现。然而,由于该数组存在于内存中的某处,因此您可以拥有指向它的指针,因此上述语句在法律上是有效的。
使用函数指针的类比可能会有所帮助。就像函数的代码作为一系列指令序列存在于内存中,您可以拥有指向该代码的函数指针,但您无法更改函数代码本身一样,字符串字面量也存在于内存中作为一系列 char,您可以拥有指向该字符串的 char 指针,但您无法更改字符串字面量本身。
C 标准仅为字符串字面量而不是字符常量(如 'A')或整数常量(如 5)指定了此行为。设置存储这些常量/非字符串字面量的内存是程序员的责任。因此,当编译器遇到类似以下语句的情况时:
char *charPtr = 'A';    // illegal!
int *intPtr = 5;        // illegal!

编译器不知道如何处理它们。程序员没有在栈或堆上保留足够的内存来保存这些值。与字符串字面值不同,编译器不会为它们分配任何内存。因此,这些语句是非法的。
希望这样更清楚了。如果还不清楚,请再评论一次,我会尽力澄清一些。

是的,我打错了字,而且我不明白为什么不需要分配字符串字面量。谢谢你的帮助,但我仍然不完全理解它们的分配方式。你说_字符串字面量存在于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要在堆栈或堆上专门分配_,那么分配是如何发生的呢? - Heikki

4

初始化不是必需的,无论指针指向什么类型。唯一的要求是您不能尝试使用未初始化的指针(从未被分配)进行任何操作。

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


在实践中,始终初始化(通常为NULL)指针变量是我个人认为的一个好习惯。请注意,如果该初始化是无用的(因为变量在第一次使用之前已设置),优化编译器将跳过它。 - Basile Starynkevitch
所以,在上面的例子中,我应该将它的地址初始化为另一个char变量,对吗? - Heikki
在上面的例子中,你只需要执行char *p_message = "指针"; - Oliver Charlesworth
要精确使用术语,您不应该使用未初始化且从未赋值的指针。(初始化和赋值是不同的事情。在分配了一个值之后,您可以使用未初始化的指针。) - Eric Postpischil
@OliCharlesworth 噢,那是打字错误,无论如何,就像这样,我不知道指针的地址。使用类似 char = var; p_message = &var 这样的东西会更好吧? - Heikki

1

首先,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");

如果代码执行以下操作,即(第一个示例)将*运算符应用于无效指针,或者(第二个示例)向期望指向能够存储正确数量字符的有效对象的标准库函数传递无效指针,则代码将调用未定义行为。

0

虽然不是必须的,但建议使用它以保持代码风格的整洁。 另外,你发布的代码完全错误且无法运行,但你知道这一点并只是为了快速示例,对吧?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接