简体   繁体   English

两次对指针变量进行类型转换,为什么?

[英]Typecasting a pointer variable twice, why?

I have found the following code in my lecture book, it works as intended, there is no error, i am just trying to understand it.我在我的课本中找到了以下代码,它按预期工作,没有错误,我只是想了解它。 In the code, we create a char array and an int_pointer.在代码中,我们创建了一个 char 数组和一个 int_pointer。 We are able to traverse the char array with the int pointer by typecasting it to a char pointer, such that everytime it gets incremented we move exactly 1 byte forward, instead of 4.我们可以通过将 int 指针类型转换为 char 指针来遍历带有 int 指针的 char 数组,这样每次递增时,我们都会向前移动 1 个字节,而不是 4 个字节。

Now my question is why we typecast 2 times.现在我的问题是为什么我们要打 2 次。 In line 13 (where the comment is) there is the "inner" typecast of (char *) and additionally the "outer" typecast (int *).在第 13 行(注释所在的位置)有 (char *) 的“内部”类型转换和另外的“外部”类型转换 (int *)。 I understand why the inner typecast is necessary, but why do we need the outer one?我明白为什么内部类型转换是必要的,但为什么我们需要外部类型? I have removed it and yet everything stays the same.我已经删除了它,但一切都保持不变。

What amazes me even more is that by typecasting it back into an int pointer, the expected length of the referenced data becomes 4 again and yet, when it gets dereferenced it prints out char after char, which indicates that only 1 byte is actually read out, even though we use a pointer that is of type int , which usually reads 4 bytes from where the pointer points to.更让我惊讶的是,通过将其类型转换回 int指针,引用数据的预期长度再次变为 4,然而,当它被取消引用时,它会在一个字符后打印出一个字符,这表明实际上只读取了 1 个字节,即使我们使用 int 类型的指针,它通常从指针指向的位置读取 4 个字节。 How can that be?怎么可能?

#include <stdio.h>

int main(){
    int i;

    char char_array[5] = {'a','b','c','d','e'};
    int int_array[5] = {1,2,3,4,5};

    char *char_pointer;
    int *int_pointer;

    char_pointer = (char *) int_array; // Typecast into the
    int_pointer = (int *) char_array; // pointer's data type

    for (int i=0; i < 5;i++){ // Iterate through the char array with the int_pointer
        printf("[integer pointer] points to %p, which contains the char '%c'\n",
               int_pointer, *int_pointer);
  /* Line 13 */      int_pointer = (int *) ((char *) int_pointer + 1); 

    }

    for (int i=0; i < 5;i++){ // Iterate through the int array with the char_pointer
        printf("[char pointer] points to %p, which contains the integer '%d'\n",
               char_pointer, *char_pointer);
        char_pointer = (char *) ((int *) char_pointer + 1);

    } } 

OUTPUT输出

[integer pointer] points to 000000000061FE03, which contains the char 'a'
[integer pointer] points to 000000000061FE04, which contains the char 'b'
[integer pointer] points to 000000000061FE05, which contains the char 'c'
[integer pointer] points to 000000000061FE06, which contains the char 'd'
[integer pointer] points to 000000000061FE07, which contains the char 'e'
[char pointer] points to 000000000061FDE0, which contains the integer '1'
[char pointer] points to 000000000061FDE4, which contains the integer '2'
[char pointer] points to 000000000061FDE8, which contains the integer '3'
[char pointer] points to 000000000061FDEC, which contains the integer '4'
[char pointer] points to 000000000061FDF0, which contains the integer '5'

You would be very unlikely to see code like that outside an environment where they're just trying to teach you how things may work under the covers :-)你很可能不看,他们只是试图教给你的东西可能会如何在幕后工作的环境之外一样,代码:-)

But I'll answer one question at least:但我至少会回答一个问题:

the expected length of the referenced data becomes 4 again and yet, when it gets dereferenced it prints out char after char, which indicates that only 1 byte is actually read out ...引用数据的预期长度再次变为 4,然而,当它被取消引用时,它会在一个字符后打印出一个字符,这表明实际上只读取了 1 个字节......

No, the expression *int_pointer will read out four bytes (or as many bytes as is needed to make an int ).不,表达式*int_pointer读出四个字节(或创建int所需的尽可能多的字节)。 What you're seeing there is the undefined behaviour of mismatching a format specifier with a different data type to what's expected.您看到的是未定义的行为,即使用不同数据类型的格式说明符与预期不匹配。 The standard is quite explicit (ISO C11 in this case):该标准非常明确(在这种情况下为 ISO C11):

If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.如果任何参数不是相应转换规范的正确类型,则行为未定义。

What's probably happening with your code in this case is that it's just using the least significant eight bits of the int .在这种情况下,您的代码可能发生的情况是它仅使用int的最低八位。


And, just as an aside, it doesn't actually make any difference what the original type of a pointer is when you cast it to another type and perform arithmetic on it (expect for the possibility of having non-aligned pointers which is fatal in some environments).而且,顺便说一句,当您将指针强制转换为另一种类型并对其执行算术运算时,指针的原始类型实际上并没有任何区别(期望有可能具有非对齐的指针,这在某些环境)。

The arithmetic is performed on the cast type, not the original:算术上类型,而不是原来的执行:

int_pointer =  (int  *) ((char *) int_pointer  + 1);
char_pointer = (char *) ((int *)  char_pointer + 1);
//                       \___________________/
//                                 |
//                This is what you're adding one to,
//              the pointer as if it was another type.

The first of those advances the pointer by one memory location regardless of the fact it uses an int pointer, because the additice expression is being given a char pointer due to the cast.其中的第一个将指针推进一个内存位置,而不管它使用int指针的事实,因为由于强制转换,additice 表达式被赋予了一个char指针。

Similarly, the second advances by sizeof(int) , even though it's adjusting (eventually) a char pointer.类似地,第二个提前sizeof(int) ,即使它正在调整(最终)一个char指针。

Your question is answered by @paxdiablo, with a very well conceptual explanation but I'll add a couple of lines to make it easier to visualize. @paxdiablo 回答了您的问题,并提供了很好的概念解释,但我会添加几行以使其更易于可视化。

Breaking down the line int_pointer = (int *)((char *) int_pointer + 1);分解线int_pointer = (int *)((char *) int_pointer + 1); :

// set the int_pointer to point to the starting address of the char_array
int_pointer = (int *) char_array;
// treat the int_pointer as if it's a character pointer
(char *) int_pointer
// add 1 to that pointer *** adds 1 byte only ***
(char *) int_pointer + 1
// then, switch back and treat that pointer as if it's an integer pointer again
int_pointer = (int *) ((char *) int_pointer + 1);

And, breaking down the line char_pointer = (char *)((int *) char_pointer + 1);并且,分解线char_pointer = (char *)((int *) char_pointer + 1); :

// set the char_pointer to point to the starting address of the int_array
char_pointer = (char *) int_array;
// treat the char_pointer as if it's an integer pointer
(int *) char_pointer
// add 1 to that pointer *** adds 4 bytes ***
(int *) char_pointer + 1
// then, switch back and treat that pointer as if it's a character pointer again
char_pointer = (char *)((int *) char_pointer + 1);

Then the magic of the printf function comes into play... If a printf function is to print the value of a dereferenced pointer, printf treats that same address according to the corresponding format specifier:那么printf函数的神奇之处就开始发挥作用了……如果printf函数要打印解除引用的指针的值,则printf会根据相应的格式说明符处理相同的地址:

printf("dereferencing same address as int %d and as char %c\n", *char_pointer, *char_pointer);
// printf takes the same memory address, namely the value of "char_pointer"
// first, treats the 4 bytes as if it's an integer
// then, treats the single byte as if it's a character

and same as above;和上面一样;

printf("dereferencing same address as int %d and as char %c\n", *int_pointer, *int_pointer);
// printf takes the same memory address, namely the value of "int_pointer"
// first, treats the 4 bytes as if it's an integer
// then, treats the single byte as if it's a character

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM