斯坦福的指针和记忆

时间:2012-07-06 12:22:37

标签: c pointers

我正在阅读斯坦福大学图书馆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 *是不是一个错误的定义?

4 个答案:

答案 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,您可以更改pp可以指向不同对象)的值,但不能更改*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 数据的指针;您无法更改pp指向的值。

在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
}

pauto变量,未初始化为任何特定值;它将包含一个随机位字符串,该字符串可能对应于可写地址,也可能不对应。因此,语句*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);
}