为什么int指针可以存储字符串?
Why is int pointer able to store string?
我已经尝试了以下方法,但我无法得到它背后的工作。
char *s;
s="hello";
printf("%s",s);
显然,这完美地打印了 hello。现在,如果我们使用下面的代码,它也可以完美运行。
int *s="hello";
printf("%s", s);
这个输出也是hello。
如果我们这样做 double *s="hello";
,那也行得通。
任何人都可以通过正确的解释帮助我解决这个问题吗?
因为在 C 中没有强类型转换,你可以将任何你想要的数据赋值给一个指针。使用 printf()
中的 %s
,您告诉编译器使用 int
指针作为指向 char
数组的指针,这非常有效,因为您 do 将 char
数组分配给 int
指针。
首先,字符串没有存储在指针中。相反,指针指向字符串的开头。诸如 "hello"
之类的字符串文字通常位于 运行 进程的只读部分中的代码旁边。
此外,char *
可以 cast 转换为 int *
,反之亦然:
char *foo = "hello";
int *bar = (int*)foo;
foo = (char*)bar;
然而,在没有转换的情况下在这些指针类型之间进行转换是不符合标准的;所以给出
char *foo;
int *bar;
这些陈述都不是
foo = bar;
bar = foo;
符合标准。
您的程序不严格符合;这不能以符合标准的方式完成。
C11 draft n1570 Appendix J.2 声明行为未定义如果:
— Two pointer types that are required to be compatible are not identically qualified, or are not pointers to compatible types (6.7.6.1).
和6.5.4.1 Cast运算符表示
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
6.5.16.1 然后说,如果没有强制转换,您可以对兼容指针进行赋值,或者将指向 void 的指针赋值给指向对象的指针,或者将指向对象的指针赋值给指向 void 的指针。
现在,int *
和 char *
不是指向兼容类型的指针,因此行为未定义。现在,未定义的行为定义为
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
2 NOTE
Possible undefined behavior ranges from ignoring the situation completely with unpredictable
results, to behaving during translation or program execution in a documented manner characteristic of the
environment (with or without the issuance of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).
(强调我的)。
使用默认设置,GCC 决定以记录的方式运行,发出诊断消息,但不终止翻译(编译):
test.c:1:8: warning: initialization from incompatible pointer type
[-Wincompatible-pointer-types]
int *s = "hello";
^
可以使用 -Werror
开关将警告转换为致命错误,它会在发出诊断消息的同时终止翻译:
% gcc test.c -Werror
test.c:1:8: error: initialization from incompatible pointer type
[-Werror=incompatible-pointer-types]
int *s = "hello";
^
cc1: all warnings being treated as errors
即使在使用默认设置编译时 GCC 仍然可以成功编译您的程序,您的程序并不严格符合要求,因此无法最大限度地移植。其他编译器和 GCC 的未来版本可以使用默认开关终止转换,或者无法生成工作代码。
您的程序有两个严重错误,一个是符合规范的编译器必须诊断的,另一个是符合规范的编译器不需要诊断的,但在 运行 时间会导致未定义的行为。
char *s;
s="hello";
printf("%s",s);
这是完全正确的。您定义了一个指向 char
对象的指针 s
,并将其初始化为指向字符串 "hello"
。然后将其传递给 printf
。 "%s"
格式告诉 printf
期待一个指向字符串的 char*
指针,而这正是您传递的内容。
int *s="hello";
您已将 s
定义为指向 int
的指针。并且您已尝试将其初始化为指向一个字符串。这是一个 约束违规 ,这意味着编译器必须对其进行诊断,至少要发出警告。如果您没有收到警告,那么您的编译器不符合标准,您应该了解如何使其正常工作。如果您收到警告并忽略它,不要那样做。许多 C 编译器(包括 gcc)会针对某些违反约束的情况发出非致命警告,尤其是那些涉及指针转换的情况。您应该密切注意编译时警告,并且应该考虑找出如何让您的编译器将此类违规视为致命错误。
gcc 的默认行为是发出所需的诊断(允许警告;恕我直言,致命错误会更好,但不是必需的),然后生成代码 如同 初始化导致从 char*
到 int*
的转换。你绝对不应该依赖这种行为。其他编译器的行为可能不同。
printf("%s", s);
正如我在上面所写的,"%s"
格式使 printf
假定它将接收一个指向字符串的 char*
参数。相反,您传递了一个 int*
参数。由于 printf
的特殊性(它是一个 variadic 函数,所需参数的数量和类型由格式字符串指定,而不是由 printf
本身指定), 编译器不一定能诊断出错误。例如,格式字符串可以是变量而不是字符串文字。
在许多系统上,char*
和 int*
指针具有相同的大小和表示形式,并以相同的方式传递给函数——并将 char*
转换为 int*
产生一个具有相同表示的指针,就好像它首先被正确定义为 char*
指针一样。因此,您调用 "works" 的方式可能与您正确编写代码的方式相同。这并不意味着代码是正确的;这意味着它有一个严重的错误,该错误没有出现在您的程序行为中。
编写遵循规则的代码仍然是您的责任,即使编译器不能始终执行这些规则,即使程序 出现 到 "work".
我已经尝试了以下方法,但我无法得到它背后的工作。
char *s;
s="hello";
printf("%s",s);
显然,这完美地打印了 hello。现在,如果我们使用下面的代码,它也可以完美运行。
int *s="hello";
printf("%s", s);
这个输出也是hello。
如果我们这样做 double *s="hello";
,那也行得通。
任何人都可以通过正确的解释帮助我解决这个问题吗?
因为在 C 中没有强类型转换,你可以将任何你想要的数据赋值给一个指针。使用 printf()
中的 %s
,您告诉编译器使用 int
指针作为指向 char
数组的指针,这非常有效,因为您 do 将 char
数组分配给 int
指针。
首先,字符串没有存储在指针中。相反,指针指向字符串的开头。诸如 "hello"
之类的字符串文字通常位于 运行 进程的只读部分中的代码旁边。
此外,char *
可以 cast 转换为 int *
,反之亦然:
char *foo = "hello";
int *bar = (int*)foo;
foo = (char*)bar;
然而,在没有转换的情况下在这些指针类型之间进行转换是不符合标准的;所以给出
char *foo;
int *bar;
这些陈述都不是
foo = bar;
bar = foo;
符合标准。
您的程序不严格符合;这不能以符合标准的方式完成。
C11 draft n1570 Appendix J.2 声明行为未定义如果:
— Two pointer types that are required to be compatible are not identically qualified, or are not pointers to compatible types (6.7.6.1).
和6.5.4.1 Cast运算符表示
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
6.5.16.1 然后说,如果没有强制转换,您可以对兼容指针进行赋值,或者将指向 void 的指针赋值给指向对象的指针,或者将指向对象的指针赋值给指向 void 的指针。
现在,int *
和 char *
不是指向兼容类型的指针,因此行为未定义。现在,未定义的行为定义为
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
2 NOTE
Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
(强调我的)。
使用默认设置,GCC 决定以记录的方式运行,发出诊断消息,但不终止翻译(编译):
test.c:1:8: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types] int *s = "hello"; ^
可以使用 -Werror
开关将警告转换为致命错误,它会在发出诊断消息的同时终止翻译:
% gcc test.c -Werror test.c:1:8: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] int *s = "hello"; ^ cc1: all warnings being treated as errors
即使在使用默认设置编译时 GCC 仍然可以成功编译您的程序,您的程序并不严格符合要求,因此无法最大限度地移植。其他编译器和 GCC 的未来版本可以使用默认开关终止转换,或者无法生成工作代码。
您的程序有两个严重错误,一个是符合规范的编译器必须诊断的,另一个是符合规范的编译器不需要诊断的,但在 运行 时间会导致未定义的行为。
char *s;
s="hello";
printf("%s",s);
这是完全正确的。您定义了一个指向 char
对象的指针 s
,并将其初始化为指向字符串 "hello"
。然后将其传递给 printf
。 "%s"
格式告诉 printf
期待一个指向字符串的 char*
指针,而这正是您传递的内容。
int *s="hello";
您已将 s
定义为指向 int
的指针。并且您已尝试将其初始化为指向一个字符串。这是一个 约束违规 ,这意味着编译器必须对其进行诊断,至少要发出警告。如果您没有收到警告,那么您的编译器不符合标准,您应该了解如何使其正常工作。如果您收到警告并忽略它,不要那样做。许多 C 编译器(包括 gcc)会针对某些违反约束的情况发出非致命警告,尤其是那些涉及指针转换的情况。您应该密切注意编译时警告,并且应该考虑找出如何让您的编译器将此类违规视为致命错误。
gcc 的默认行为是发出所需的诊断(允许警告;恕我直言,致命错误会更好,但不是必需的),然后生成代码 如同 初始化导致从 char*
到 int*
的转换。你绝对不应该依赖这种行为。其他编译器的行为可能不同。
printf("%s", s);
正如我在上面所写的,"%s"
格式使 printf
假定它将接收一个指向字符串的 char*
参数。相反,您传递了一个 int*
参数。由于 printf
的特殊性(它是一个 variadic 函数,所需参数的数量和类型由格式字符串指定,而不是由 printf
本身指定), 编译器不一定能诊断出错误。例如,格式字符串可以是变量而不是字符串文字。
在许多系统上,char*
和 int*
指针具有相同的大小和表示形式,并以相同的方式传递给函数——并将 char*
转换为 int*
产生一个具有相同表示的指针,就好像它首先被正确定义为 char*
指针一样。因此,您调用 "works" 的方式可能与您正确编写代码的方式相同。这并不意味着代码是正确的;这意味着它有一个严重的错误,该错误没有出现在您的程序行为中。
编写遵循规则的代码仍然是您的责任,即使编译器不能始终执行这些规则,即使程序 出现 到 "work".