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

我正在学习C编程语言,并且在理解指针,数组和只读存储器之间的微小差异时遇到了困难。 我正在使用以下示例:

char *myCards = "JQK"; 

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

 *myCards = 'A'; 

我试图使用一组int来实现类似的function; 但是,如果可能的话,我完全不确定如何创建这个数组并将其放在内存的只读部分。

我知道我可以使用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只是一个契约,它表达的意图是事物确实是不变的。 这意味着编译器/链接器可以将数据放在只读部分,因为程序员保证它不会改变。 但它没有必要

其次,字符串文字转换为一个常量的char数组,其中0隐式附加。 请参阅Peter Schneider在这里的注释:它不是正式的 const(因此编译器不会在你使用非const指针时警告你),但它应该是。

结合这个,下面的代码在我的系统上使用gd在Linux amd64上进行段错误,因为gcc确实将数组放在只读部分:

 #include  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; } 

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

顺便说一下,如果用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 。 其他编译器可能表现不同。

我同意菲利克斯帕尔曼。 函数中的数组存储在堆栈中,即使它是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,您仍然可以修改它。