当我从书本中学习时,在使用指针之前必须对其进行初始化,因此我们通常会像这样使用
int a = 12;
int * p = &a;
所以我理解为什么int* p = 12
是错的,因为它没有地址;
然后我在编码的时候找到了一些东西,就是这样:
char * months[12] = {"Jan", "Feb", "Mar", "April", "May" , "Jun", "Jul"
,"Aug","Sep","Oct","Nov","Dec"};
然后又出现了另一个常用的情况,那就是:
char *p = "string"; (this is ok , why int * a = 12 can't be allowed ?)
我很困惑。什么时候初始化,如何?为什么int * a = 12
无法自动初始化?也许是关于记忆安排的事情。
答案 0 :(得分:8)
首先关闭:
int a = 12;
int* p = &a;
这是有效的,因为&a
是一个内存地址。
int* p = 12;
这主要是因为12不是内存地址。同样地,12本身没有地址,但这会更好地反映在int* p = &12;
之类的片段中(正如您所正确指出的那样,它将不起作用)。
指针的一个有趣属性是它们通常用于指定值列表的开头。例如,取这个整数数组:
int a[] = {1, 3, 7, 13};
它可以简单地变成一个整数指针。
int* p = a; // magic!
指针对象是a
的第一个元素,因此*p == 1
。现在,您也可以p[0]
(也是1),p[1] == 3
,p[3] == 7
和p[4] == 13
。
char* foo = "bar"
的作用原因是“bar”不是单个值:它是伪装的字符数组。单个字符用单引号表示。事实上:
"bar"[0] == 'b'
"bar"[1] == 'a'
"bar"[2] == 'r'
编译器对字符串文字(引用字符串)有特殊支持,可以直接将它们分配给指针。例如,char* foo = "bar"
有效。
符合C99的编译器也支持数组文字。例如,int* p = (int [3]){1, 2, 3};
有效。字符数组和int数组将被赋予一个全局地址,因为制作C的人认为这是一件有用的事情。
答案 1 :(得分:3)
归结为类型。
在C和C ++中,像12
这样的普通整数文字的类型是int
。没有从类型int
到类型int*
的隐式转换,这是有道理的:从概念上讲,指针和整数是完全不同的东西。因此int *p = 12;
无效。
在C中,像"abc"
这样的普通字符串文字被翻译成一个 static 字符数组(大小足以存储abc
加上一个终止空字符)。类型"字符数组"可以隐式转换为类型char*
(指向char的指针) - 数组被称为衰变为指针。因此作业char *p = "abc";
有效。
但是有一个问题:它是修改该数组的未定义行为(在C和C ++中)。事实上,这种转换在C ++中被弃用(甚至是非法的),你应该使用const char *
代替。
答案 2 :(得分:2)
int* p = 12
错误,因为指定的值可能属于也可能不属于内存地址。您迫使p
指向该位置
允许char *p = "string"
,因为编译器已经为字符串设置了空间,p
指向该字符串的第一个字符。
答案 3 :(得分:2)
实际上gcc编译器会警告你:
char* p = "hello";
这是因为"你好"现在被视为const char *的等价物。
所以这会更好:
const char* p = "hello";
但是正如其他人所描述的那样,"你好"有一个地址,指向一个固定的字符序列的开始。
答案 4 :(得分:0)
该语言已异常,并允许使用字符串文字来初始化char const*
。有些编译器不太严格,并且允许使用字符串文字来初始化char*
。
更新,以回应Pascal Cuoq的评论
在C和C ++中,以下是使用字符串文字初始化变量的有效方法:
char carr[] = "abc";
char carr[10] = "abc";
char const* cp = "abc";
以下是在初始化列表中使用整数文字初始化变量的有效方法:
int iarr[] = {1, 2, 3};
int iarr[10] = {1, 2, 3};
但是,以下内容不是在初始化列表中使用整数文字初始化变量的有效方法:
int const* ip = {1, 2, 3};
当我说语言异常并允许字符串文字用于初始化char const*
时,这就是我的意思。
答案 5 :(得分:0)
int* p = 12
是错误的,因为它做的事情几乎肯定不是你认为的那样。假设您的编译器没有抱怨您试图隐式地将int
强制转换为int *
,这不是非法的。你所做的是,在内存位置p
指向12
,这几乎肯定是你不应该阅读的内容。赋值是合法的,但如果您取消引用该指针,则您处于未定义的行为区域。如果您处于用户模式,则*(int*)12
可能是分段错误。
答案 6 :(得分:0)
使用C术语,这两种情况之间的区别在于字符串文字存在,它们是一个char数组(因此是一个左值,所以你可以把它们的地址或指向它们);但在C90中没有其他文字。 12
是一个整数常量,而不是文字。你不能&(12)
,因为语言是这样说的。括号封闭的初始化列表不是值。常数是右值;文字是左值。
在C ++中,行为是相同的,但是C ++对同一件事使用不同的术语。在C ++中,常量都称为“文字”,但它们也都是rvalues(字符串文字除外),所以你不能拿它们的地址。
C99添加了其他类型的数组文字。
答案 7 :(得分:0)
许多程序员对指定指针的语法感到困惑。
int* p;
int *p;
int * p;
以上所有声明都是一样的:一个指针p,它可以是NULL,也可以是内存中整数大小的存储单元的地址。
因此
int * p = 12;
声明一个指针p,并为其赋值12.
在C和C ++中,指针只是对编译器具有特殊含义的变量,因此您可以使用特殊语法来访问它们所拥有的值的内存位置。
暂时考虑一下这种方式。
想想数字“90210”。这可能是您的银行余额。这可能是你出生后的小时数。
这些都是数字的简单“整数”解释。如果我告诉你它是一个邮政编码 - 突然它描述了地方。 90210 不是加利福尼亚州的比佛利山庄,它是加州比佛利山庄的[邮政] 地址。
同样,当你写
int * p = 12;
你是说“12”是内存中整数的地址,你将在指针变量p
中记住这个事实。
你可以写
int * p = &12;
这将强制编译器在包含12的本机整数表示的程序可执行文件中生成整数存储单元,然后它将生成将该整数的地址加载到变量p
中的代码。
char* p = "hello";
非常不同。
12; // evaluates to an integer value of 12
"hello"; // evaluates to an integer value which specifies the location of the characters { 'h', 'e', 'l', 'l', 'o', 0 } in memory, with a pointer cast.
int i = 12; // legal
char h = 'h'; // legal
const char* p = "hello"; // legal
uintptr_t p = "hello"; // legal
C和C ++中的双引号具有特殊含义,它们计算指向包含在其中的字符串的指针,而不是评估字符串本身。
这是因为
"The quick brown fox jumped over the lazy dog"
不适合CPU寄存器(32位或64位,具体取决于您的机器)。相反,文本被写入可执行文件,而程序则加载它的地址(将适合寄存器)。这就是我们称之为C / C ++的指针。
答案 8 :(得分:0)
我在stackoverflow上发现了一个很好的提示:总是用NULL初始化声明的指针。 这将有助于理解新创建的指针必须经过进一步的操作才能使用。
因此应执行的操作必须为指针分配适当的地址(初始化该指针)。
使用原始代码执行您可能打算做的事情
int *p = 12;
您必须做的事:
int *p;
p = malloc(sizeof(p)); /* pointer p holds address of allocated memory */
*p = 12;
或另一个示例:
int *p;
const int a = 12;
p = &a; /* pointer p holds address of variable a */
上面的其他人回答了char *p = "string";
为什么正确。这只是指针初始化的另一种方式。
同样,在stackoverflow上的answers之一中,我发现了一种非常适合您的情况的选择:
int *p = &(int){12};