声明指针类型?

Cod*_*ter 42 c c++ pointers

我刚刚读到我们需要在C(或C++)中声明它们时给出指针类型,即:

int *point ;
Run Code Online (Sandbox Code Playgroud)

据我所知,指针存储变量的地址,并且地址占用相同数量的内存,无论哪种类型.那么,为什么我们需要声明它的类型?

Mik*_*our 70

类型安全.如果你不知道p应该指出什么,那就没有什么可以阻止类别错误了

*p = "Nonsense";
int i = *p;
Run Code Online (Sandbox Code Playgroud)

静态类型检查是一种非常强大的工具,可以防止各种类型的错误.

C和C++也支持指针算法,只有在目标类型的大小已知时才有效.

无论我的类型如何,地址都占用相同数量的内存

对于今天流行的平台来说,情况确实如此.但是有些平台并非如此.例如,指向多字节字的指针可能小于指向单个字节的指针,因为它不需要表示字中字节的偏移量.


Kni*_*nug 35

因为:

  1. 不同类型的地址不需要具有相同的大小.该标准明确规定了(C 2011标准(在线草案),6.2.5/28).
  2. type-safety:这允许编译器检测何时提供指向函数或赋值的不兼容指针.这反过来可以防止将参数顺序搞乱到函数的丑陋情况.
  3. 指针解除引用时,编译器需要知道类型.
  4. 要做指针运算,需要知道指向的对象的大小,从而知道它的类型.

最后两点不适用于void指针,这就是为什么它们不能通过解引用而不能对它们进行指针运算的原因.该标准指定一个void指针必须足够大以容纳任何类型的指针(除了函数指针,这是一个完全不同的故事),并且void可以在没有强制转换的情况下对指针进行赋值(至少在C中,在C++中强制转换是总是需要的).


Mih*_*eac 13

一个原因是指针算术.p+1除非你知道元素的大小,否则你无法做到这p一点 - 这p是指针类型的大小.如果你尝试p+1一个void *p你可能会得到一个错误的答案(它与在a上完成相同,char *但也许你不想要那个;它被-pedantic作为警告和-pedantic-errors错误捕获).

另一个原因是类型安全.如果函数作为参数接收,int *则无法将指针传递给char(字符串).你会收到一个警告(-Werror/ 的错误-pedantic-errors).考虑这个(虚拟)代码:

void test(int *x)
{
}

int main()
{
    char *x = "xyz";
    test(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译(使用gcc (GCC) 4.8.2 20131017 (Red Hat 4.8.2-1))给出:

1.c: In function ‘main’:
1.c:8:2: warning: passing argument 1 of ‘test’ from incompatible pointer type [enabled by default]
  test(x);
  ^
1.c:1:6: note: expected ‘int *’ but argument is of type ‘char *’
 void test(int *x)
      ^
Run Code Online (Sandbox Code Playgroud)

  • @Guido [这个答案](http://stackoverflow.com/a/3524270/249237)说`void*`上的指针算法不是**与`char*`和非法相同(在C和C++中) ). (2认同)

Sha*_*our 11

那么,为什么我们需要声明它的类型?

您想知道指针的类型,以便进行静态类型检查.

我们还需要知道类型以便指针算法工作,例如当我们索引到不同大小类型的数组(相当于指针算术)时,指针将根据类型相关的数量进行调整.如果我们看看草案C99标准部分,6.5.6 Additive运营商说(强调我的):

另外,两个操作数都应具有算术类型,或者一个操作数应是指向对象类型的指针 [...]

所以指针需要是一个对象类型,这意味着不完整或无效.

你还说:

无论什么类型,地址占用相同数量的内存.那么,为什么我们需要声明它的类型?

这在C++中并不总是如此,成员函数指针的大小可以根据类类型而改变,其中一个很好的文章就是指向成员函数的指针是非常奇怪的动物.

此外,我们可以看到,无论是C99标准草案部分章节6.2.5 类型27其中说:

[...]指向其他类型的指针不需要具有相同的表示或对齐要求.

和C++标准草案部分3.9.2 复合类型3段说:

[...]指针类型的值表示是实现定义的.符合cv资格和cv不合格版本(3.9.3)的布局兼容类型的指针应具有相同的值表示和对齐要求(3.11).[...]

除特定情况外,不要求指针具有相同的表示.


Juz*_*ing 8

您需要将类型指定为标准要求.此外,当您尝试执行加法或减法等指针运算时,没有任何问题.


APa*_*Pan 5

在解除引用和指针算术时,指针的类型起作用.例如

int x=10;     //Lets suppose the address of x=100
int *ptr=&x;   //ptr has value 100
printf("The value of x is %d", *ptr);
ptr++;  // ptr has value 104(if int is 4bytes)
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,指针类型是int,因此编译器将从内存地址100开始查找存储在接下来的4个字节中的值(如果int是4bytes).因此指针的类型告诉编译器它应该有多少字节在解除引用时寻找.如果指针类型不存在,编译器将如何知道在解除引用时要查看多少字节.当我们做ptr ++时,指针的类型告诉ptr应该增加多少.这里ptr增加4.

char c='a';   //Lets suppose the address of c = 200
char* ptr=&c;   //ptr has value 200
ptr++;   //ptr has value 201(char assumed to be 1 byte) 
Run Code Online (Sandbox Code Playgroud)

指针类型告诉ptr增加1个字节.


sup*_*cat 5

虽然处理器通常有不同的指令"从地址加载一个字节","从地址加载一个16位半字","从一个地址加载一个32位字",同样用于"存储"操作,C使用相同的语法从地址加载一个字节,以加载任何其他大小的值.鉴于声明:

int n = *p;
Run Code Online (Sandbox Code Playgroud)

编译器可以生成从p中的地址加载字节,半字或字的代码并将其存储到n中; 如果p是一个*float,它可能会生成一个更复杂的代码序列来加载c中的浮点值,截断它,转换为int,并将转换后的值存储到n中.在不知道p的类型的情况下,编译器无法知道哪个操作是合适的.

同样,该语句p++可以将地址增加p一,二,四或其他数字.地址增加的数量将取决于声明的p类型.如果编译器不知道p的类型,它将不知道如何调整地址.

可以声明指针而不指定它指向的事物的类型.这种指针的类型是void*.void*在对它做任何有用的事情之前,必须将a转换为真实的指针类型; 主要的用处void*在于如果指针被转换为void*,它可以作为一个void*代码传递,该代码对指针的实际类型一无所知.如果指针最终给代码知道它的类型,而代码注塑指针回到那个类型,结果会是一样的已被转换为指针void*.

必须处理指向它所知道的事物的指针的代码通常可以有效地void*用于这样的目的,但是知道指针所指向的事物的代码通常应该声明适当类型的指针.