我正在阅读斯坦福大学图书馆http://cslibrary.stanford.edu/102/
的文章坏指针示例 具有最常见类型的指针错误的代码将看起来像上面正确的代码,但没有指针被指定为指针的中间步骤。坏代码编译正常,但在运行时,每个带有错误指针的解引用都会以某种方式破坏内存。该程序迟早会崩溃。程序员应该确保在使用之前为每个指针分配一个指针。以下示例显示了错误代码的简单示例以及内存可能如何反应的图...
void BadPointer() {
int* p; // allocate the pointer, but not the pointee
*p = 42; // this dereference is a serious runtime error
}
//在解除引用坏指针时在运行时会发生什么......
但我记得char *应该像这样定义
char *const name_ptr = "Test";
通过这种方式,如果每个人都在考虑这个char *是不是一个错误的定义?
答案 0 :(得分:3)
该行
char *const name_ptr = "Test";
很好;你正在用字符串文字"Test"
的地址初始化指针,这是一个char
数组,以这样一种方式存储,它的内存在程序启动时分配并保持到程序终止。
快速了解const
限定符:
在C中,声明表格
const T foo = expr;
或
T const foo = expr;
表示可能无法写入foo
;它在创建时被分配了 expr 的值,并且在foo
的生命周期的其余部分 1)中可能不会更改该值。使用指针变量,它会变得更复杂:
const T *p = expr;
T const *p = expr;
将p
声明为非const 指向 const 数据的指针; IOW,您可以更改p
(p
可以指向不同对象)的值,但不能更改*p
的值(您无法更改p
点的值到)。
T * const p = expr;
将p
声明为 const 指向非const 数据的指针;您可以更改p
指向(*p = ...
)的值,但不能将p
更改为指向其他对象。
const T * const p = expr;
T const * const p = expr;
都将p
声明为 const 指向 const 数据的指针;您无法更改p
或p
指向的值。
在C中,"Test"
等字符串文字存储为char
的数组,但尝试修改字符串文字的内容是未定义的行为(取决于平台,您可能会获得访问权限)违反)。为安全起见,通常最好将指向字符串文字的指针声明为const char *
或char const *
,而不是如上例所示的char * const
。
至于
void BadPointer() {
int* p; // allocate the pointer, but not the pointee
*p = 42; // this dereference is a serious runtime error
}
,p
是auto
变量,未初始化为任何特定值;它将包含一个随机位字符串,该字符串可能对应于可写地址,也可能不对应。因此,语句*p = 42;
的行为未定义 - 您可能会遇到访问冲突,您可能会覆盖重要的内容并使程序处于错误状态,或者可能似乎“工作”没有问题(写入一些可访问且不重要的随机存储区域)。
通常,单独指针值 2)无法判断给定指针值是有效还是无效。一个例外是特殊指针值NULL,它是一个明确定义的“无处”,保证比较不等于任何有效指针值。在文件范围(在任何函数之外)或使用static
限定符声明的指针变量被隐式初始化为NULL。应始终使用NULL或有效地址显式初始化非静态块范围指针变量。这样,您可以轻松检查指针是否已分配有效值:
int *p = NULL;
...
if (p != NULL) // or simply if (p)
{
*p = 42;
}
else
{
// p was not assigned a valid memory location
}
<小时/> 1)注意,在C中,
foo
不是编译时常量;它是一个常规的运行时变量,你只是无法写入它。您不能在需要编译时常量的上下文中使用它。
2)如果你非常熟悉平台的内存模型,你可以做一些有根据的猜测,但即便如此,也不能保证。
答案 1 :(得分:2)
在第二种情况下:
char *const name_ptr = "Test";
您正在创建放置在只读内存中的字符串文字。因此,您可以拥有一个合法的指针。
在第一种情况下:
void BadPointer() {
int* p; // allocate the pointer, but not the pointee
*p = 42; // this dereference is a serious runtime error
}
您将获得未定义的行为(UB)。
答案 2 :(得分:2)
char *const name_ptr
表示name_ptr
是一个指向char的常量指针(它是指针是常量)。
你可能意味着const char * name_ptr = "Test"
(name_ptr
是指向字符的指针,它是常量)
问题是"Test"
是一个字符串,它是一个字符数组,存储在(可能)常量内存中。由于内存已分配,因此可以将指针初始化为指向它。
int *p;
是一个未初始化的指针。它有一些未定义的值,可能会或可能不会解析为合理的内存位置 - 可能不会,但你永远不会知道。假设*p = 42;
将用42
覆盖该仲裁记忆位置,那么您的程序的所有投注都将关闭。
答案 3 :(得分:2)
在这种情况下,有助于记住指针只不过是一个包含值的正常变量 - 关于它的唯一“神奇”部分是该值代表一个位置内存,您可以取消引用该位置以访问存储在那里的内容。
想象一下像这样的代码:
void BadPrinter() {
int p;
printf("%d\n", p);
}
它会打印什么?谁知道?也许是0,也许是垃圾,也许是由Styx编写的“Come Sail Away”的歌词编码为整数。
现在我们回到你的指针代码:
void BadPointer() {
int* p; // allocate the pointer, but not the pointee
*p = 42; // this dereference is a serious runtime error
}
p
以完全相同的方式未初始化 - 它可以包含任何内容。因此,当您执行*p
时,您要求编译器授予您访问 p
中包含的数字所代表的内存的权限。
因此,如果p
恰好包含0
,那么您现在正在尝试将值42
填充到内存位置0x0
中:您的程序可能会崩溃。如果p
恰好包含可写内存中的位置,则您的程序可能会继续快速继续,因为您将被允许在该位置存储42
。
现在这个案例有点不同了:
char *const name_ptr = "Test";
在这里,您要求编译器分配足够的内存空间来存储字符串"Test"
,并将该内存的位置存储在name_ptr
中。回到我们的第一个例子,它类似于:
void GoodPrinter() {
int p = 4;
printf("%d\n", p);
}