我正在学习C编程语言,并且在理解指针,数组和只读内存之间的微小差异时遇到了困难。我正在使用以下示例:
char *myCards = "JQK";
根据我的理解,这行代码完成的是它在内存的只读段中创建一个空终止字符串,可通过char指针myCards
访问。这意味着我能够编译以下内容,但由于字符串的不变性而会收到错误:
*myCards = 'A';
我正在尝试使用一组int来实现类似的功能;但是,如果可能的话,我完全不确定如何创建这个数组并将其放在内存的只读部分。
我知道我可以使用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"
的内存中。任何见解都有帮助。谢谢。
答案 0 :(得分:3)
好的,首先要明白这一点,知道C中的const
不需要对只读内存做任何事情是很重要的。对于C,没有部分这样的东西。 const
仅仅是一份契约,它表达的意图是事物确实是不变的。这意味着编译器/链接器可以将数据放在只读部分,因为程序员确保它不会发生变化。但是,它并没有 。
其次,字符串文字转换为char
的常量数组,其中0
隐式附加。请参阅Peter Schneider在这里的评论:它不是正式 const(所以编译器在你使用非const指针时不会警告你),但它应该。
结合这个,以下代码在我的系统上使用gd在Linux amd64上进行段错误,因为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;
}
请注意,在您使用非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
。其他编译器可能表现不同。
答案 1 :(得分:1)
我同意菲利克斯帕尔曼。函数中的数组存储在堆栈中,即使它是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,你仍然可以修改它。