0

我正在阅读斯坦福 CS 图书馆的文章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

4 回答 4

3

线

char *const name_ptr = "Test";

很好;您正在使用字符串字面量的地址初始化指针,该地址"Test"是一个存储数组char,其内存在程序启动时分配并保持到程序终止。

关于const预选赛的快速题外话:

在 C 中,声明形式

const T foo = expr;

或者

T const foo = expr;

表示foo可能无法写入;它在创建时被分配了expr的值,并且该值在1)foo的剩余生命周期内可能不会更改。使用指针变量,它变得有点复杂:

const T *p = expr;
T const *p = expr;

两者都声明p为指向const数据的非 const指针;IOW,您可以更改(可以指向不同的对象) 的值,但不能更改 ( 您不能更改指向的值) 的值。pp*pp

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 *or 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) 如果您非常熟悉您的平台的内存模型,您可以做出一些有根据的猜测,但即使这样也不能保证。

于 2012-07-06T14:36:23.973 回答
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)。

于 2012-07-06T12:26:28.057 回答
2

char *const name_ptr表示这name_ptr是一个指向 char 的常量指针(它是常量指针)。

您可能的意思是const char * name_ptr = "Test" (name_ptr是一个指向常量字符的指针)

问题是这"Test"是一个字符串,它是一个字符数组,存储在(可能)常量内存中的某个位置。由于分配了内存,因此可以初始化指向它的指针。

int *p; 是一个未初始化的指针。它有一些未定义的值,可能会或可能不会解析为合理的内存位置——很可能它不会,但你永远不知道。说*p = 42;将用 覆盖该任意内存位置42,然后您的程序的所有赌注都将关闭。

于 2012-07-06T12:31:01.153 回答
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);
}
于 2012-07-06T12:56:05.947 回答