C 中的指针数组和只读存储器

Pointers Arrays and Read Only Memory in C

我正在学习 C 编程语言并且 运行 难以理解指针、数组和只读存储器之间的细微差别。我正在使用以下示例:

char *myCards = "JQK";

根据我的理解,这行代码完成的是它在内存的只读部分创建一个以空字符结尾的字符串,该部分可通过字符指针 myCards 访问。这意味着我能够编译以下内容,但由于字符串的不可变性而会收到错误消息:

*myCards = 'A';

我正在尝试使用整数数组实现类似的功能;但是,我完全不确定如何创建此数组并将其放置在内存的只读部分(如果可能)。

我知道我可以使用 const 关键字并创建一个数组,如下所示:

const int myInts[] = {3, 6, 1, 2, 3, 8, 4, 1, 7, 2};

但是,我可以在这个数组初始化之后直接执行以下操作:

printf("First element of array: %i\n", myInts[0]);    
int *myIntsPtr = myInts;
*myIntsPtr = 15;
printf("First element of array: %i\n", myInts[0]);

我能够通过创建一个指向它的指针来改变数组的第一个元素,这意味着这个数组从未被放入只读内存。

基本上我一直在试图弄清楚如何声明这个 int 数组,以便它在只读内存中类似于 "JQK"。任何见解都是有帮助的。谢谢。

好的,首先要了解这一点,重要的是要知道 C 中的 const 不必对只读内存做任何事情。对于 C,没有节这样的东西。 const 只是一个契约,它表达了某种东西确实是不变的意图。这意味着 compiler/linker 可以 将数据放在只读部分中,因为程序员保证它不会改变。不过,没有

其次,字符串文字转换为 char 的常量数组,并隐式附加 0。在此处查看 Peter Schneider 的评论:它不是 正式 const(因此当您使用非常量指针指向它时编译器不会警告您),但它 应该 是。

结合这一点,以下代码在我的系统上使用 Linux amd64 上的 gcc 出现段错误,因为 gcc 确实将数组放在只读部分中:

#include <stdio.h>

const int myInts[] = {3, 6, 1, 2, 3, 8, 4, 1, 7, 2};

int main(void)
{
    printf("First element of array: %i\n", myInts[0]);    
    int *myIntsPtr = myInts;
    *myIntsPtr = *(myIntsPtr + 1);
    printf("First element of array: %i\n", myInts[0]);
    return 0;
}

请注意,在您使用指向常量数组的非常量指针的行中还有一个编译器警告。

顺便说一句,如果您在函数中使用 gcc 声明数组,则相同的代码将起作用,这是因为数组本身是在堆栈上创建的。你仍然收到警告,代码仍然是错误的。这是关于如何在此处实现 C 的技术细节。与字符串文字的不同之处在于它是一个匿名对象(char 数组没有标识符)并且在任何情况下都具有静态存储持续时间。


编辑 解释字符串文字的作用:以下代码是等价的:

int main(void)
{
    const char *foo = "bar";
}

const char ihavenoname_1[] = {'b', 'a', 'r', 0};

int main(void)
{
    const char *foo = ihavenoname_1;
}

所以,简而言之,如果您希望 gcc 将数据放入只读部分,请将其声明为 const 静态存储持续时间(在函数之外)。其他编译器的行为可能不同。

我同意 Felix Palmen 的观点。函数内的数组存储在堆栈中,即使它是 const,您也可以使用适当的强制转换对其进行修改。这是我用 MS-VC++ 得到的(ebp 是堆栈指针):

const int test [ 5 ] = { 0, 1, 2, 3, 4 };
00309598  mov         dword ptr [ebp-1Ch],0 
0030959F  mov         dword ptr [ebp-18h],1 
003095A6  mov         dword ptr [ebp-14h],2 
003095AD  mov         dword ptr [ebp-10h],3 
003095B4  mov         dword ptr [ebp-0Ch],4 
( ( int* ) test ) [ 1 ] = 0;
003095BB  mov         dword ptr [ebp-18h],0

现在,在函数中定义数组,但这次定义为 static const,或者将其定义为 const 全局变量...两个测试的结果相同:数组现在在数据段(使用地址而不是ebp)但仍然可以修改:

static const int test [ 5 ] = { 0, 1, 2, 3, 4 };
( ( int* ) test ) [ 1 ] = 0;
01449598  mov         dword ptr [test+4 (145ECACh)],0

使用 gcc,您将在只读内存中拥有数据,但正如 Felix 所说,它是不保证的:例如,如果您使用 MS-VC,您仍然可以修改它。