鱼C论坛

 找回密码
 立即注册
查看: 3222|回复: 15

[已解决]关于指针类型的问题

[复制链接]
发表于 2019-8-8 13:47:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 micolar 于 2019-8-8 13:52 编辑
#include<stdio.h>
int main(){
        char a[3][3] = {
                {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[4][5]
那么(*b)++怎么理解
换句话说对于指针,它除了保存地址,还保存什么信息,它又以什么方式保存
c语言的指针地址是实际的机器地址 还是虚地址
最佳答案
2019-8-8 15:17:00
问题1:
这个问题和运行环境有关

例如有一个32位数据 0x12345678
如果这样的代码运行在小端机器上,读取到的是 0x78
如果这样的代码运行在大端机器上,读取到的是 0x12 (这是一个理论上的答案,毕竟我没有大端的机器,没有测试过,不过我认为这个理论上的答案是正确的,你就认为是正确的就好了,^_^)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-8-8 15:17:00 | 显示全部楼层    本楼为最佳答案   
问题1:
这个问题和运行环境有关

例如有一个32位数据 0x12345678
如果这样的代码运行在小端机器上,读取到的是 0x78
如果这样的代码运行在大端机器上,读取到的是 0x12 (这是一个理论上的答案,毕竟我没有大端的机器,没有测试过,不过我认为这个理论上的答案是正确的,你就认为是正确的就好了,^_^)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 15:27:56 | 显示全部楼层
问题2:
char b[4][5]

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

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

未完待续。。。
^_^
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 15:44:47 | 显示全部楼层
继续问题2:
#include <stdio.h>

int main(void)
{
        char b[4][5] = {{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)++;

1.png

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

未完待续。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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[10];
b[0] = '0';
b[0]出现在了等于号左边,b[0]是 lvalue,因此 b[0]++是正确的

与此相对
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是常数,不能赋值
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 16:04:44 | 显示全部楼层
回到问题2:
#include <stdio.h>

int main(void)
{
        char b[4][5] = {{0}};
        (*b)++;
        return 0;
}

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

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

(*b) 不是 lvalue
b[0]也不是 lvalue

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 16:10:22 | 显示全部楼层
#include <stdio.h>

int main(void)
{
        char b[4][5] = {{0}};
        b[0] = 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[0] = 10;
       ^
main.c:5:7: warning: variable 'b' set but not used [-Wunused-but-set-variable]
  char b[4][5] = {{0}};
       ^

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

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

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

你直接写 b 相当于写 &b[0]
你直接写 b[0] 相当于写 &b[0][0]
你直接写 b[1] 相当于写 &b[1][0]
你直接写 b[2] 相当于写 &b[2][0]

找规律很重要,找到规律后你就可以以不变应万变,死记硬背是没用的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 16:16:21 | 显示全部楼层
b[0]相当于&b[0][0]

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

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

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


b[0]相当于&b[0][0]
一旦对一个东西用了 & 符号,那么这个对象就成了rlvalue
++需要一个lvalue,b[0]是一个rlvalue,b[0]++是错误的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 16:21:05 | 显示全部楼层
本帖最后由 人造人 于 2019-8-8 16:30 编辑

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

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

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

虚拟地址、逻辑地址、线性地址、物理地址的区别
https://blog.csdn.net/qiuchaoxi/article/details/79616220
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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

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


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 16:40:52 | 显示全部楼层
完了
^_^
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-8 16:52:59 | 显示全部楼层
人造人 发表于 2019-8-8 16:21
问题2的一半已经解释完了,现在看另一半

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

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

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

假设b是个数组呗  
b+1 就是它行的第二列的地址
这应该就是类型信息了 我是不是可以猜它用4个字节的内存来保存长度信息 毕竟数组长度可以取的很长 一个字节不够的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-8 16:56:54 | 显示全部楼层
人造人 发表于 2019-8-8 16:37
对于指针,除了保存地址信息,还保存类型信息
可以看到对于int指针加1,地址值加了4
char指针加1 ...

嗯嗯 我接触指针的时候就是这么想的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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"
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-8-8 17:42:43 | 显示全部楼层
编译器直接把 &a + 4 翻译成了对变量a的地址值加16
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-8 18:13:10 | 显示全部楼层
人造人 发表于 2019-8-8 17:42
编译器直接把 &a + 4 翻译成了对变量a的地址值加16

嗯嗯我懂了谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-13 02:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表