int *p=123;
char *s="hi";
我试着在谷歌上搜索这个问题,但没有得到确切的答案。所以,问题是为什么编译器抛出错误时,我们分配的地址的地方的数字到一个整数指针,但编译完美时,一个字符指针被分配字符串或字符串,并将它们存储在内存中的每个字符由不同的内存地址。就像我在开始时写的代码一样,当我声明int指针并为其分配整数时,它会抛出警告"warning: initialization of 'int *' from 'int' makes pointer from integer without a cast"
,但当我声明char指针并不分配地址而是字符时,它编译起来没有问题。正如char指针在内存中存储每个字符及其自己的地址,int指针不应该在内存中存储每个数字作为每个元素的不同地址的数组吗?
4条答案
按热度按时间vwkv1x7d1#
这种差异是由于两个独立的事实,这两个事实都是C的“怪癖”,一开始可能会令人困惑。
第一个奇怪之处是,当你有一个指针变量 * initialization * 时,它与赋值不同。如你所知当你说
这意味着“
p
是指向int
的指针,初始值为123”。但是,如果我们在一行中定义变量p
,并在第二行中使用普通的 * assignment * 语句给它一个值,会怎么样呢?很容易想象它看起来像这样:但实际上它看起来像这样:
在带有初始化的指针声明/定义中,
*
意味着声明了一个指针变量,但并不意味着初始化指针指向的内容。另一方面,在正则赋值表达式中,*
是一个运算符,表示“我正在谈论这个指针指向的内存位置”。所以当你说这意味着,“指针
p
指向某个内存位置,我想将值123存储到该内存位置。”这意味着“使指针
p
指向某个内存位置123”。在这种情况下,可以更清楚地说但是,当然,除非我们正在进行某种专门的嵌入式编程,否则我们很少为想要访问的内存位置选择精确的数值。
但让我们回到你问的问题。为了避免混淆,让我们使用您的两个带初始化器的声明
并将它们更改为单独的声明:
和分配:
正如我们刚刚看到的,整数指针赋值
p = 123
并没有达到我们的目的,甚至可能根本没有做任何有用的事情。那么为什么字符指针赋值工作?
这与C的第二个怪癖有关,它分为两部分。第一部分是,正如你可能知道的,C中的字符串表示为字符数组。(这很好,这甚至算不上"怪癖"。)但是第二部分是,* 每当你在表达式中提到一个数组并试图获取它的值时,你得到的值是指向数组第一个元素的指针。
首先,当你在程序中提到字符串常量
"hi"
时,编译器会自动为你创建一个小数组。效果就像你说的(也许这是第三个怪癖)。
然后当你说
就好像你写的
这是一个很长的答案,但如果你仍然和我在一起,底线是,当你使用指针时,你必须清楚地知道 * 指针 * 和 * 它指向什么 * 之间的区别。当你说
你两样都兼顾了你让指针
p
指向某个地方,* 并且 * 你安排,在它指向的位置,有一个字符串"hi"
。但当你说你让指针
p
指向位置123,但你没有做任何事情来影响内存位置123的值-你当然没有让p
指向整数值123。说了这么多,你可能仍然想知道,有没有一种一步到位的方法来设置一个整数指针指向一个整数数组,就像有
s =
"hi"`的字符数组一样?传统上,没有。但是C99引入了一种叫做array initializer的东西,这正是你想要的。它看起来像这样:
这与字符串字面量的情况相同。编译器创建一个数组 * 并 * 设置指针指向它,就像你说的那样
所以,总的来说,你可以说
而且,一旦你这样做了,int指针和char指针之间就没有太大的区别了。
补遗:在你的问题或这个答案中并没有真正提到它,但是还有一个与C密切相关的怪癖值得一提。我说,"每当你在一个表达式中提到一个数组,并试图取它的值,你得到的值是一个指向数组第一个元素的指针。"我说当你写
就好像你写了
但如果你写上
这看起来是错误的-你怎么能把一个像
string_constant
这样的数组,并把它赋值给一个像s
这样的指针呢?这是因为,正如我所说的,当你在表达式中提到一个数组并试图获取它的值时,你得到的是一个指向数组第一个元素的指针。或者换句话说5sxhfpxr2#
这两个声明
有语义上的区别。
要使它们在语义上相同,您需要编写
在这种情况下,指针由标量对象初始化。在第一个声明中,指针
p
由整数常量123
初始化,在第二个声明中,指针s
由整数字符常量'h'
初始化。所以编译器应该对这两个声明发出警告。
或者你可以写例如
在这种情况下,两个指针都由数组的第一个字符的地址初始化。
请注意,字符串文字
"hi"
的数组类型为char[3]
,并存储在内存中,如{ 'h', 'i', '\0' }
。作为初始化器,数组a
和字符串文字"hi"
被隐式转换为指向其第一个元素的指针,对应的类型为int *
和char *
。因此,int *
和char *
类型的指针由相同类型的表达式int *
和char *
初始化。dddzy1tm3#
C 2018 6.5.16.1 1指定分配的约束。关于赋值给指针的第一个约束是:
在
int *p=123;
中,p
被初始化,而不是赋值。然而,初始化规则表明赋值规则适用于6.7.9 11。p
是一个指针,但123
是一个int
,不是一个指针,所以它不满足约束,编译器会抱怨。在
char *s="hi";
中,"hi"
是一个字符数组。它被用作提供s
的初始值的表达式。C自动将用作表达式的数组转换为指向其第一个元素的指针。1因此,对于"hi"
,我们有一个指向字符的指针,该字符是char *
,并且上面的约束允许将char *
赋值给char *
,因此编译器不会抱怨。请注意,
p = 123;
不会将123
的地址分配给指针。它会尝试将123
的值赋给指针。除非在特殊的情况下,即已知特殊的内存地址,例如硬件接口的地址或操作系统设置的特殊地址,否则不应这样做。在这些情况下,我们将使用强制转换将整数地址转换为指针,如p = (void *) 0x1000;
。相比之下,s = "hi";
将"hi"
的地址分配给s
而不是分配其值的原因是因为数组自动转换为指针。为了完整起见,关于赋值给指针的第二个约束是:
void
的限定或非限定版本的指针,并且左操作数指向的类型具有右操作数指向的类型的所有限定符;这不适用,因为不涉及
void *
。关于赋值给指针的第三个约束是:
此规则允许您将整数赋给指针,因为一种空指针常量是值为0的整数常量表达式。因此,允许
int *p = 0;
或int *p = 4-4;
。int *p = x-x;
将不满足此约束,因为它不是常量表达式。脚注
1当数组是
sizeof
的操作数、一元&
的操作数或用于初始化数组的字符串文字时,不执行此转换。这方面的规则在6.3.2.1 3.xkftehaa4#
首先,你试图将一个原始类型存储到一个指针。因为指针需要一个变量地址或者数组的起始点,所以它们不能真正直接保存一个变量。
当你这样声明变量“hi”时:
char *s = "hi";
,你实际上创建了一个characters的数组,如果它不是全局的,则存储在堆栈段中。然而,当你试图存储123时,它是一个原始类型,正如我所提到的,所以你试图将一个变量存储到需要地址的指针。如果您尝试执行类似于
char s1[3] = {'h', 'i', '\0'};
的操作,这将与“hi”声明类似。你对代码所做的就像char* s = 'h'
一样,它也会给予一个错误。因此,指针需要存储地址,因此不能在赋值的右侧使用基元类型。