关于指针类型的问题
本帖最后由 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语言的指针地址是实际的机器地址 还是虚地址
问题1:
这个问题和运行环境有关
例如有一个32位数据 0x12345678
如果这样的代码运行在小端机器上,读取到的是 0x78
如果这样的代码运行在大端机器上,读取到的是 0x12 (这是一个理论上的答案,毕竟我没有大端的机器,没有测试过,不过我认为这个理论上的答案是正确的,你就认为是正确的就好了,^_^)
问题2:
char b
数组名表示数组第0个元素的地址
直接写 b 和写 &b 是一样的
(*b)++ 也就是 (*&b)++
这里先取地址然后立刻解引用,& 和 * 相互抵消,也就是
(*b)++
(*&b)++
(b)++
b++
上面这些是一样的
(*b)++ 和 b++ 是一样的
未完待续。。。
^_^ 继续问题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: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是常数,不能赋值
回到问题2:
#include <stdio.h>
int main(void)
{
char b = {{0}};
(*b)++;
return 0;
}
这个代码提示错误,说 (*b) 不是 lvalue
(*b)++
(*&b)++
(b)++
b++
上面这些相同
(*b) 不是 lvalue
b也不是 lvalue
#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
找规律很重要,找到规律后你就可以以不变应万变,死记硬背是没用的
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:30 编辑
问题2的一半已经解释完了,现在看另一半
”换句话说对于指针,它除了保存地址,还保存什么信息,它又以什么方式保存”
对于指针,除了保存地址信息,还保存类型信息,对于以什么方式保存,这个我不知该如何回答
”c语言的指针地址是实际的机器地址 还是虚地址”
当然是虚拟地址
虚拟地址、逻辑地址、线性地址、物理地址的区别
https://blog.csdn.net/qiuchaoxi/article/details/79616220 #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:21
问题2的一半已经解释完了,现在看另一半
”换句话说对于指针,它除了保存地址,还保存什么信息,它又以什 ...
我的两个小问题咋麻烦您写那么多
我犯糊涂了 (*b)++
我又忘了它是个指针常量
假设b是个数组呗
b+1 就是它行的第二列的地址
这应该就是类型信息了 我是不是可以猜它用4个字节的内存来保存长度信息 毕竟数组长度可以取的很长 一个字节不够的
人造人 发表于 2019-8-8 16:37
对于指针,除了保存地址信息,还保存类型信息
可以看到对于int指针加1,地址值加了4
char指针加1 ...
嗯嗯 我接触指针的时候就是这么想的 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"
编译器直接把 &a + 4 翻译成了对变量a的地址值加16
人造人 发表于 2019-8-8 17:42
编译器直接把 &a + 4 翻译成了对变量a的地址值加16
嗯嗯我懂了谢谢
页:
[1]