micolar 发表于 2019-8-8 13:47:21

关于指针类型的问题

本帖最后由 micolar 于 2019-8-8 13:52 编辑

#include<stdio.h>
int main(){
        char a = {
                {48,49,50},
                {51,52,53},
                {54,55,56}
        };
        printf("%c",*(char*)a);
        return 0;
}

上面我定义了一个二维数组
因为数组名a 本身应该可以看成一个指针
一开始下意识输出格式是直接*a
然后我意识到指针也有类型
所以又改成了*(char*)a

但是又有个两个问题
1.int 假如是4个字节 char 1个字节 如果把一个指向int类型的指针强制换位char类型的指针
它读取是32位的前八位还是后八位

2.各个数组的行列可能是不同的
如果又有一个数组 char b
那么(*b)++怎么理解
换句话说对于指针,它除了保存地址,还保存什么信息,它又以什么方式保存
c语言的指针地址是实际的机器地址 还是虚地址

人造人 发表于 2019-8-8 15:17:00

问题1:
这个问题和运行环境有关

例如有一个32位数据 0x12345678
如果这样的代码运行在小端机器上,读取到的是 0x78
如果这样的代码运行在大端机器上,读取到的是 0x12 (这是一个理论上的答案,毕竟我没有大端的机器,没有测试过,不过我认为这个理论上的答案是正确的,你就认为是正确的就好了,^_^)

人造人 发表于 2019-8-8 15:27:56

问题2:
char b

数组名表示数组第0个元素的地址
直接写 b 和写 &b 是一样的

(*b)++ 也就是 (*&b)++
这里先取地址然后立刻解引用,& 和 * 相互抵消,也就是
(*b)++
(*&b)++
(b)++
b++
上面这些是一样的
(*b)++ 和 b++ 是一样的

未完待续。。。
^_^

人造人 发表于 2019-8-8 15:44:47

继续问题2:

#include <stdio.h>

int main(void)
{
        char b = {{0}};
        (*b)++;
        return 0;
}


这个代码是错的,编译会提示
E:\tmp>gcc -g -Wall -o main main.c
main.c: In function 'main':
main.c:6:6: error: lvalue required as increment operand
(*b)++;



因为 (*b) 不是 lvalue,所以不能 ++

未完待续。。。

人造人 发表于 2019-8-8 15:56:28

本帖最后由 人造人 于 2019-8-8 15:59 编辑

既然 (*b) 不是 lvalue,不能 ++
那什么是 lvalue ?

就我个人的理解
能放在等于号左边的一切,都是 lvalue

例如
int a = 100;
a = 50;
a出现在了等于号左边,a是 lvalue,因此 a++是正确的

char b;
b = '0';
b出现在了等于号左边,b是 lvalue,因此 b++是正确的

与此相对
int c = 10;
100 = c;
100是一个常数,不能出现在等于号左边,因此100不是lvalue,100++不正确的

还有
const int d = 100;
d = 10;
d是const的,d不能出现在等于号左边,d不是lvalue,因此 d++不正确的
#include <stdio.h>

int main(void)
{
        const int d = 100;
        d = 10;
        d++;
        return 0;
}

E:\tmp>gcc -g -Wall -o main main.c
main.c: In function 'main':
main.c:6:4: error: assignment of read-only variable 'd'
d = 10;
    ^
main.c:7:3: error: increment of read-only variable 'd'
d++;
   ^~


这个代码编译提示错误

有lvalue就有rlvalue
是lvalue就一定是rlvalue,因为能出现在等于号左边的东西,一定能出现在等于号右边
是rlvalue就不一定是lvalue,因为100能出现在等于号右边,作为一个常数,但是100不能出现在等于号左边,100是常数,不能赋值

人造人 发表于 2019-8-8 16:04:44

回到问题2:
#include <stdio.h>

int main(void)
{
      char b = {{0}};
      (*b)++;
      return 0;
}


这个代码提示错误,说 (*b) 不是 lvalue

(*b)++
(*&b)++
(b)++
b++
上面这些相同

(*b) 不是 lvalue
b也不是 lvalue

人造人 发表于 2019-8-8 16:10:22

#include <stdio.h>

int main(void)
{
        char b = {{0}};
        b = 10;
        return 0;
}


E:\tmp>gcc -g -Wall -o main main.c
main.c: In function 'main':
main.c:6:7: error: assignment to expression with array type
b = 10;
       ^
main.c:5:7: warning: variable 'b' set but not used [-Wunused-but-set-variable]
char b = {{0}};
       ^

b是一个二维数组,或者说数组的数组

如果你把 b 看成一个整体 X,那么
char b 就是 char X

b是一个数组的数组,就是数组中存储的是数组

你直接写 b 相当于写 &b
你直接写 b 相当于写 &b
你直接写 b 相当于写 &b
你直接写 b 相当于写 &b

找规律很重要,找到规律后你就可以以不变应万变,死记硬背是没用的

人造人 发表于 2019-8-8 16:16:21

b相当于&b

一旦对一个东西用了 & 符号,那么这个东西就成了rlvalue

例如
int a = 100;
int *pa = NULL;
pa = &a;
&a = 0;

a是int,&a是 rlvalue,&a = 0;是错误的


b相当于&b
一旦对一个东西用了 & 符号,那么这个对象就成了rlvalue
++需要一个lvalue,b是一个rlvalue,b++是错误的

人造人 发表于 2019-8-8 16:21:05

本帖最后由 人造人 于 2019-8-8 16:30 编辑

问题2的一半已经解释完了,现在看另一半

”换句话说对于指针,它除了保存地址,还保存什么信息,它又以什么方式保存”
对于指针,除了保存地址信息,还保存类型信息,对于以什么方式保存,这个我不知该如何回答

”c语言的指针地址是实际的机器地址 还是虚地址”
当然是虚拟地址

虚拟地址、逻辑地址、线性地址、物理地址的区别
https://blog.csdn.net/qiuchaoxi/article/details/79616220

人造人 发表于 2019-8-8 16:37:10

#include <stdio.h>

int main(void)
{
        int a = 100;
        char b = '\n';

        printf("&a = %p\n", &a);
        printf("&a + 1 = %p\n", &a + 1);
        printf("\n");
        printf("&b = %p\n", &b);
        printf("&b + 1 = %p\n", &b + 1);
        return 0;
}


E:\tmp>gcc -g -Wall -o main main.c

E:\tmp>main
&a = 0xffffcbfc
&a + 1 = 0xffffcc00

&b = 0xffffcbfb
&b + 1 = 0xffffcbfc

对于指针,除了保存地址信息,还保存类型信息
可以看到对于int指针加1,地址值加了4
char指针加1,地址值加了1

如果一个指针只知道地址信息,不知道类型信息,就不能进行指针运算,因为没办法知道如何计算地址值


人造人 发表于 2019-8-8 16:40:52

完了
^_^

micolar 发表于 2019-8-8 16:52:59

人造人 发表于 2019-8-8 16:21
问题2的一半已经解释完了,现在看另一半

”换句话说对于指针,它除了保存地址,还保存什么信息,它又以什 ...

我的两个小问题咋麻烦您写那么多

我犯糊涂了 (*b)++
我又忘了它是个指针常量

假设b是个数组呗
b+1 就是它行的第二列的地址
这应该就是类型信息了 我是不是可以猜它用4个字节的内存来保存长度信息 毕竟数组长度可以取的很长 一个字节不够的

micolar 发表于 2019-8-8 16:56:54

人造人 发表于 2019-8-8 16:37
对于指针,除了保存地址信息,还保存类型信息
可以看到对于int指针加1,地址值加了4
char指针加1 ...

嗯嗯 我接触指针的时候就是这么想的

人造人 发表于 2019-8-8 17:41:19

micolar 发表于 2019-8-8 16:52
我的两个小问题咋麻烦您写那么多

我犯糊涂了 (*b)++


编译器并没有把指针的类型信息保存在编译后的程序中,编译器直接在编译程序时完成了这个类型信息相关的处理

#include <stdio.h>

int main(void)
{
        int a = 100;
        int *pa = &a + 4;
        return 0;
}


        .file        "main.c"
        .text
        .def        __main;        .scl        2;        .type        32;        .endef
        .globl        main
        .def        main;        .scl        2;        .type        32;        .endef
        .seh_proc        main
main:
        pushq        %rbp
        .seh_pushreg        %rbp
        movq        %rsp, %rbp
        .seh_setframe        %rbp, 0
        subq        $48, %rsp
        .seh_stackalloc        48
        .seh_endprologue
        call        __main
        movl        $100, -12(%rbp)
        leaq        -12(%rbp), %rax
        addq        $16, %rax
        movq        %rax, -8(%rbp)
        movl        $0, %eax
        addq        $48, %rsp
        popq        %rbp
        ret
        .seh_endproc
        .ident        "GCC: (GNU) 7.4.0"

人造人 发表于 2019-8-8 17:42:43

编译器直接把 &a + 4 翻译成了对变量a的地址值加16

micolar 发表于 2019-8-8 18:13:10

人造人 发表于 2019-8-8 17:42
编译器直接把 &a + 4 翻译成了对变量a的地址值加16

嗯嗯我懂了谢谢
页: [1]
查看完整版本: 关于指针类型的问题