为什么int指针能够存储字符串?

时间:2016-05-14 07:19:11

标签: c pointers c-strings

我已尝试过以下内容,但我无法完成它的工作。

char *s;
s="hello";
printf("%s",s);

显然,这打印你好。现在,如果我们使用以下代码,它也可以完美运行。

int *s="hello";
printf("%s", s);

这个输出也是你好。 如果我们double *s="hello";,那也有效。 任何人都可以通过正确的解释帮助我解决这个问题吗?

3 个答案:

答案 0 :(得分:1)

首先,字符串不存储在指针中。相反,指针指向字符串的开头。字符串文字(例如"hello")通常位于正在运行的进程的只读部分中的代码旁边。 此外,char *可以强制转换int *,反之亦然,可以使用强制转换操作符:

char *foo = "hello";
int *bar = (int*)foo;
foo = (char*)bar;

但是,在没有强制转换的情况下在这些指针类型之间进行转换不符合标准;所以给出了

char *foo;
int *bar;

这些陈述都不是

foo = bar;
bar = foo;

符合标准。

您的计划并非严格遵守;这不能以符合标准的方式完成。

C11草案n1570 附录J.2 声明在以下情况下行为未定义:

  

- 需要兼容的两种指针类型不具有相同的限定条件,或者不是兼容类型的指针(6.7.6.1)。

6.5.4.1演员表示

  

涉及指针的转换,除了6.5.16.1的约束允许的范围外,应通过显式转换来指定。

6.5.2.1然后说没有强制转换你可以对兼容的指针进行赋值,或者将指向void的指针赋给指向对象的指针,或者将指向对象的指针赋给指向void的指针。

现在,int *char *不是指向兼容类型的指针,因此行为未定义。现在,未定义的行为定义为

  

1未定义的行为

     

行为,在使用不可移植或错误的程序结构或错误数据时,   本国际标准没有要求

     

2注意

     

可能的未定义行为包括完全忽略不可预测的情况   结果, 在翻译或程序执行过程中以记录的方式表现   环境(有或没有发出诊断消息) ,终止翻译或   执行(发出诊断信息)。

(enphasis mine)

使用默认设置,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可以使用默认开关终止转换,或者无法生成工作代码。

答案 1 :(得分:0)

因为在C中没有强类型转换,所以可以为指针分配所需的数据。使用%s中的printf(),您告诉编译器使用int指针作为指向char数组的指针,这完全可以正常工作,因为您char数组分配给int指针。

答案 2 :(得分:0)

您的程序有两个严重错误,一个是符合要求的编译器必须诊断的错误,另一个是符合编译器不需要诊断但是在运行时导致未定义行为的另一个错误。

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的特殊性(它是可变参数函数,并且所需参数的数量和类型由格式字符串指定,而不是由printf本身指定),编译器不一定能诊断出错误。例如,格式字符串可以是变量而不是字符串文字。

在许多系统上,char*int*指针具有相同的大小和表示,并以相同的方式传递给函数 - 并将char*转换为{{1产生一个具有相同表示的指针,就好像它首先被正确定义为int*指针一样。因此,您的调用“工作”可能与您正确编写代码的方式相同。这并不意味着代码是正确的;这意味着它有一个严重的错误,没有出现在你的程序的行为中。

编写遵循规则的代码仍然是您的责任,即使编译器不能始终强制执行这些规则,即使程序出现以“工作”。