指针的目的是保存特定变量的地址.那么下面代码的内存结构应如下所示:
int a = 5;
int *b = &a;
Run Code Online (Sandbox Code Playgroud)
......内存地址......值 a ... 0x000002
................... 5
b ... 0x000010 ..... .............. 0x000002
好的.然后假设现在我想保存指针*b的地址.然后我们通常定义一个双指针,**c,as
int a = 5;
int *b = &a;
int **c = &b;
Run Code Online (Sandbox Code Playgroud)
然后内存结构看起来像:
......内存地址......值 a ... 0x000002
................... 5
b ... 0x000010 ..... ............... 0x000002
c ... 0x000020 ................... 0x000010
所以**c指的是*b的地址.
现在我的问题是,为什么这种类型的代码,
int a = 5;
int *b = &a;
int *c = &b;
Run Code Online (Sandbox Code Playgroud)
产生警告?
如果指针的目的只是为了保存内存地址,我认为如果我们要保存的地址是指变量,指针,双指针等,那么应该没有层次结构,所以下面的代码类型应该是有效.
int a = 5;
int *b = &a;
int *c = &b;
int *d = &c;
int *e = &d;
int *f = &e;
Run Code Online (Sandbox Code Playgroud)
Som*_*ude 90
在
int a = 5;
int *b = &a;
int *c = &b;
Run Code Online (Sandbox Code Playgroud)
您收到警告,因为&b它是类型int **,并且您尝试初始化类型的变量int *.这两种类型之间没有隐式转换,导致警告.
要采用你想要工作的更长的例子,如果我们尝试取消引用f,编译器会给我们一个int,而不是一个我们可以进一步解引用的指针.
还要注意,在许多系统上int并且int*大小不同(例如,指针可能是64位长,int32位长).如果取消引用f并获得一个int,则会丢失一半的值,然后您甚至无法将其转换为有效指针.
ala*_*ain 53
如果指针的目的只是为了保存内存地址,我认为如果我们要保存的地址是变量,指针,双指针等等,应该没有层次结构.
在运行时,是的,指针只保存一个地址.但是在编译时,还有一个与每个变量相关联的类型.正如其他人所说,int*并且int**是两种不同的,不兼容的类型.
有一种类型,void*可以做你想要的:它只存储一个地址,你可以为它分配任何地址:
int a = 5;
int *b = &a;
void *c = &b;
Run Code Online (Sandbox Code Playgroud)
但是当你想要取消引用时void*,你需要自己提供"缺失"的类型信息:
int a2 = **((int**)c);
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 23
现在我的问题是,为什么这种类型的代码,
Run Code Online (Sandbox Code Playgroud)int a = 5; int *b = &a; int *c = &b;产生警告?
你需要回到基础.
p是指针值,那么它*p是一个变量v是变量则&v是指针现在我们可以找到你发布的所有错误.
然后假设现在我想保存指针的地址
*b
No. *b是int类型的变量.它不是指针.b是一个变量,其值是一个指针. *b是一个变量,其值是一个整数.
**c是指的地址*b.
不不不.绝对不.如果你要理解指针,你必须正确理解这一点.
*b是一个变量; 它是变量的别名a.变量的地址是变量a的值b. **c没有提到的地址a.相反,它是一个变量,其为别名为变量a.(等等*b.)
正确的说法是:值变量c是地址的b.或者,等效地:值c是指向的指针b.
我们怎么知道呢?回到基础.你这么说c = &b.那么价值是c多少?一个指针.要什么?b.
确保您完全理解基本规则.
现在您希望了解变量和指针之间的正确关系,您应该能够回答有关代码为什么会出错的问题.
250*_*501 20
如果你想获得正确的警告并且你想要编译代码,那么C的类型系统需要这个.只有一个指针深度,你不知道指针是指向指针还是指向实际整数.
如果你取消引用一种类型,int**你知道你得到的类型int*,如果你取消引用,类似int*的是int.根据您的提案,类型将是模棱两可的.
从你的例子中,不可能知道是否c指向a int或int*:
c = rand() % 2 == 0 ? &a : &b;
Run Code Online (Sandbox Code Playgroud)
c指向什么类型?编译器不知道,所以下一行是不可能执行的:
*c;
Run Code Online (Sandbox Code Playgroud)
在C中,所有类型的信息在编译后都会丢失,因为每个类型都在编译时检查,不再需要.您的提议实际上会浪费内存和时间,因为每个指针都必须有关于指针中包含的类型的其他运行时信息.
Joh*_*ode 17
指针是具有附加类型语义的内存地址的抽象,并且在诸如C类型的语言中是重要的.
首先,不能保证int *并且int **具有相同的大小或表示(在他们现代的桌面架构上,但你不能依赖它是普遍真实的).
其次,类型对指针算法很重要.给定p类型的指针T *,表达式p + 1产生下一个类型对象的地址T.因此,假设以下声明:
char *cp = 0x1000;
short *sp = 0x1000; // assume 16-bit short
int *ip = 0x1000; // assume 32-bit int
long *lp = 0x1000; // assume 64-bit long
Run Code Online (Sandbox Code Playgroud)
表达式cp + 1为我们提供了下一个char对象的地址0x1001.表达式sp + 1为我们提供了下一个short对象的地址0x1002. ip + 1给了我们0x1004,并lp + 1给了我们0x1008.
所以,给定
int a = 5;
int *b = &a;
int **c = &b;
Run Code Online (Sandbox Code Playgroud)
b + 1让我们接下来的地址int,并c + 1给我们的下一个地址指针来int.
如果希望函数写入指针类型的参数,则需要指针指针.请使用以下代码:
void foo( T *p )
{
*p = new_value(); // write new value to whatever p points to
}
void bar( void )
{
T val;
foo( &val ); // update contents of val
}
Run Code Online (Sandbox Code Playgroud)
这适用于任何类型T.如果我们T用指针类型替换P *,代码就变成了
void foo( P **p )
{
*p = new_value(); // write new value to whatever p points to
}
void bar( void )
{
P *val;
foo( &val ); // update contents of val
}
Run Code Online (Sandbox Code Playgroud)
语义完全相同,它只是不同的类型; 形式参数p总是比变量更多的间接级别val.
438*_*427 11
如果我们要保存的地址是变量,指针,双指针,我认为应该没有层次结构
没有"层次结构",在没有任何警告的情况下生成UB将非常容易 - 这将是非常可怕的.
考虑一下:
char c = 'a';
char* pc = &c;
char** ppc = &pc;
printf("%c\n", **ppc); // compiles ok and is valid
printf("%c\n", **pc); // error: invalid type argument of unary ‘*’
Run Code Online (Sandbox Code Playgroud)
编译器给了我一个错误,因此它帮助我知道我做错了什么,我可以纠正错误.
但没有"等级",如:
char c = 'a';
char* pc = &c;
char* ppc = &pc;
printf("%c\n", **ppc); // compiles ok and is valid
printf("%c\n", **pc); // compiles ok but is invalid
Run Code Online (Sandbox Code Playgroud)
由于没有"层次结构",编译器不能给出任何错误.
但当行:
printf("%c\n", **pc);
Run Code Online (Sandbox Code Playgroud)
执行,它是UB(未定义的行为).
首先*pc读取char它就好像它是一个指针,即使我们只保留1个字节,也可能读取4或8个字节.那是UB.
如果程序由于上面的UB没有崩溃但只返回了一些垃圾值,那么第二步就是取消引用垃圾值.再一次UB.
结论
类型系统通过将int*,int**,int***等视为不同类型来帮助我们检测错误.
glg*_*lgl 10
如果指针的目的只是为了保存内存地址,我认为如果我们要保存的地址是变量,指针,双指针等等,那么应该没有层次结构,所以下面的代码类型应该是有效的.
我认为这是你的误解:指针本身的目的是存储内存地址,但指针通常也有一个类型,以便我们知道在它指向的位置会发生什么.
特别是,与你不同,其他人真的想拥有这种层次结构,以便知道如何处理指针所指向的内存内容.
C指针系统的特点是附加了类型信息.
如果你这样做
int a = 5;
Run Code Online (Sandbox Code Playgroud)
&a意味着你得到的是一个int *如果你取消引用它是一个int又一个.
把它带到下一个层次,
int *b = &a;
int **c = &b;
Run Code Online (Sandbox Code Playgroud)
&b也是一个指针.但是不知道背后隐藏着什么,分别是.它指向什么,它是无用的.重要的是要知道解除引用指针会显示原始类型的类型,因此它*(&b)是一个int *,并且**(&b)是int我们使用的原始值.
如果您认为在您的情况下应该没有类型的层次结构,您可以随时使用void *,尽管直接可用性非常有限.
如果指针的目的只是为了保存内存地址,我认为如果我们要保存的地址是变量,指针,双指针等等,那么应该没有层次结构,所以下面的代码类型应该是有效的.
那对于机器来说是真的(毕竟大概都是一个数字).但是在许多语言中键入变量,意味着编译器可以确保您正确使用它们(类型对变量强加正确的上下文)
确实,指向指针和指针(可能)的指针使用相同数量的内存来存储它们的值(请注意,对于int和指向int的指针不是这样,地址的大小与a的大小无关)屋).
因此,如果您有一个地址的地址,您应该按原样使用而不是简单的地址,因为如果您将指针作为一个简单的指针访问指针,那么您将能够操作int的地址,就好像它是一个int ,这不是(没有任何其他东西替换int,你应该看到危险).你可能会感到困惑,因为所有这些都是数字,但在日常生活中你不会:我个人在1美元和1只狗身上有很大的不同.狗和$是类型,你知道你可以用它们做什么.
你可以在装配中编程并制作你想要的东西,但你会发现它有多危险,因为你几乎可以做你想做的事情,特别是奇怪的事情.是的,修改一个地址值是危险的,假设你有一辆自动驾驶汽车应该以距离表示的地址提供东西:1200内存街道(地址),并假设街道房屋相隔100英尺(1221是无效地址),如果你能够像整数那样操纵地址,你就可以尝试在1223处交付并让数据包在人行道中间.
另一个例子可以是房屋,房屋的地址,该地址的地址簿中的入口号.所有这三个都是不同的概念,不同的类型......
有不同的类型.并且有充分的理由:
有......
int a = 5;
int *b = &a;
int **c = &b;
Run Code Online (Sandbox Code Playgroud)
… 表达方式 …
*b * 5
Run Code Online (Sandbox Code Playgroud)
......是有效的,而表达......
*c * 5
Run Code Online (Sandbox Code Playgroud)
没有意义.
大不了就是没有,怎么指针或指针到指针存储,但什么他们参考.
C语言是强类型的.这意味着,对于每个地址,都有一个类型,它告诉编译器如何解释该地址的值.
在你的例子中:
int a = 5;
int *b = &a;
Run Code Online (Sandbox Code Playgroud)
的类型的aIS int,和类型b是int *(读作"指针int").使用您的示例,内存将包含:
..... memory address ...... value ........ type
a ... 0x00000002 .......... 5 ............ int
b ... 0x00000010 .......... 0x00000002 ... int*
Run Code Online (Sandbox Code Playgroud)
该类型实际上并不存储在内存中,只是编译器知道,当您读到时,a您会找到一个int,当您阅读时,b您将找到一个可以找到的地方的地址int.
在你的第二个例子中:
int a = 5;
int *b = &a;
int **c = &b;
Run Code Online (Sandbox Code Playgroud)
类型c为int **,读作"指向指针的指针int".这意味着,对于编译器:
c 是一个指针;c,你得到另一个指针的地址;int.那是,
c是一个指针(int **);*c也是一个指针(int *);**c是一个int.内存将包含:
..... memory address ...... value ........ type
a ... 0x00000002 .......... 5 ............ int
b ... 0x00000010 .......... 0x00000002 ... int*
c ... 0x00000020 .......... 0x00000010 ... int**
Run Code Online (Sandbox Code Playgroud)
由于"类型"不与值一起存储,并且指针可以指向任何内存地址,因此编译器知道地址值的类型的方式基本上是通过获取指针的类型,并移除最右边的*.
顺便说一下,这是一个普通的32位架构.对于大多数64位体系结构,您将拥有:
..... memory address .............. value ................ type
a ... 0x0000000000000002 .......... 5 .................... int
b ... 0x0000000000000010 .......... 0x0000000000000002 ... int*
c ... 0x0000000000000020 .......... 0x0000000000000010 ... int**
Run Code Online (Sandbox Code Playgroud)
地址现在每个8个字节,而a int仍然只有4个字节.由于编译器知道每个变量的类型,因此它可以轻松处理这种差异,并为指针读取8个字节,为4个字节读取int.
为什么这种类型的代码会产生警告?
Run Code Online (Sandbox Code Playgroud)int a = 5; int *b = &a; int *c = &b;
的&操作者产生一个指向对象,即&a是类型int *(通过初始化),以便在分配给b这也是类型int *是有效的.&b产生一个指向对象的指针b,它是&b指向的类型的指针int *,即int **.
C在赋值运算符(用于初始化)的约束中表示(C11,6.5.16.1p1):"两个操作数都是指向兼容类型的限定或非限定版本的指针".但是在C定义什么是兼容类型int **并且int *不兼容类型.
因此int *c = &b;初始化中存在约束违规,这意味着编译器需要进行诊断.
一个规则的理由这里是没有保障的标准,这两个不同的指针类型的大小相同(除了void *和字符指针类型),也就是sizeof (int *)和sizeof (int **)可以是不同的值.
| 归档时间: |
|
| 查看次数: |
6688 次 |
| 最近记录: |