我正在以自学的方式学习 C 编程。我知道数字指针地址必须始终被初始化,无论是静态的还是动态的。
但是,我还没有读到关于初始化 char 指针地址的强制需要。
例如,此代码是否正确,或者是否需要指针地址初始化?
char *p_message;
*p_message = "Pointer";
我正在以自学的方式学习 C 编程。我知道数字指针地址必须始终被初始化,无论是静态的还是动态的。
但是,我还没有读到关于初始化 char 指针地址的强制需要。
例如,此代码是否正确,或者是否需要指针地址初始化?
char *p_message;
*p_message = "Pointer";
我不完全确定“数字指针”而不是“字符指针”是什么意思。在 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"
但是char
s 和int
s 一样,需要在堆栈或堆上分配。您需要声明一个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
不同,因为它们可用于指向字符串文字,您不必在堆栈(静态)或堆(动态)上分配内存。我认为这可能是您的实际问题,与内存分配而不是初始化有关。int
double
如果您真的在询问初始化而不是内存分配:就初始化而言,指针变量与任何其他变量没有什么不同。就像未初始化的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!
编译器不知道如何处理它们。程序员没有在堆栈或堆上留出这样的内存来保存这些值。与字符串文字不同,编译器也不会为它们留出任何内存。所以这些说法是非法的。
希望这更清楚。如果没有,请再次发表评论,我会尝试澄清更多。
无论指针指向什么类型,都不需要初始化。唯一的要求是您不得尝试将未初始化的指针(从未分配给)用于任何事情。
但是,出于美观和维护的原因,应始终在可能的情况下进行初始化(即使只是为了NULL
)。
首先,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");
那么代码将通过(第一个示例)将*
运算符应用于无效指针或(第二个示例)将无效指针传递给标准库函数来调用未定义的行为,该函数需要一个指向能够存储正确数字的对象的有效指针的字符。
不需要,但仍建议用于干净的编码风格。此外,您发布的代码完全错误且无法正常工作,但您知道这一点并且只是将其作为一个简单的示例编写,对吧?