3

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

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

例如,此代码是否正确,或者是否需要指针地址初始化?

char *p_message;
*p_message = "Pointer";
4

4 回答 4

6

我不完全确定“数字指针”而不是“字符指针”是什么意思。在 C 中,achar是整数类型,所以它是算术类型。在任何情况下,指针都不需要初始化,无论它是否是指向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的第一个字符的地址。字符串文字存在于不同的内存段中,它们被认为是不可变的,并且它们的内存不需要专门在堆栈或堆上分配。'P'"Pointer"

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

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

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

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

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

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

于 2013-09-01T16:18:22.927 回答
4

无论指针指向什么类型,都不需要初始化。唯一的要求是您不得尝试将初始化的指针(从未分配给)用于任何事情。

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

于 2013-09-01T15:46:06.020 回答
1

首先,char is a numeric type,因此您的问题中的区别没有意义。如所写,您的示例代码甚至无法编译:

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");

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

于 2013-09-01T16:07:34.720 回答
0

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

于 2013-09-01T16:09:11.620 回答