如何向初学者解释C指针(声明与一元运算符)?


141

最近,我很高兴向C编程初学者介绍了指针,并偶然发现了以下困难。如果您已经知道如何使用指针,这似乎根本就不是问题,但是请尝试以清晰的心态看下面的示例:

int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);

对于绝对的初学者来说,输出结果可能令人惊讶。在第2行中,他/她刚刚声明* bar为&foo,但是在第4行中,结果表明* bar实际上是foo而不是&foo !!

您可能会说,造成混淆的原因是*符号的含糊之处:在第2行中,它用于声明指针。在第4行中,它用作一元运算符,用于获取指针指向的值。两种不同的东西,对不对?

但是,这种“解释”根本没有帮助初学者。它指出了细微的差异,从而引入了一个新概念。这不是正确的教学方法。

那么,克尼根和里奇如何解释呢?

一元运算符*是间接或取消引用运算符;当应用于指针时,它访问指针指向的对象。[…]

指针ip的声明int *ip旨在用作助记符;它表示该表达式*ip是一个int。变量声明的语法模仿可能出现该变量的表达式的语法

int *ip应该读成“ *ip会返回int”?但是,为什么声明之后的赋值不遵循该模式呢?如果初学者想初始化变量怎么办?int *ip = 1(读取:*ip将返回int和,int则返回1)将无法正常工作。概念模型似乎并不连贯。我在这里想念什么吗?


编辑:它试图在这里总结答案


15
最好的解释是在纸上画图,并用箭头将它们连接起来;)
Maroun 2014年

16
当我不得不解释指针语法时,我总是坚持这样一个事实:*声明中的标记是一个含义为“声明指针”的标记,在表达式中,它是解引用运算符,并且这两个代表着碰巧具有相同符号的不同事物。 (与乘法运算符相同-相同的符号,不同的含义)。这令人困惑,但是与实际情况不同的任何事情都会变得更糟。
Matteo Italia 2014年

40
也许这样写就可以int* bar使它变得更加明显,即星形实际上是类型的一部分,而不是标识符的一部分。当然,这会使您遇到诸如的不直观的问题int* a, b
Niklas B.

9
我一直认为,关于K&R的解释很愚蠢,没有必要。语言在两个不同的事物上使用相同的符号,我们只需要处理它即可。*根据上下文,可以有两种不同的含义。就像同一字母的发音会因其所用的单词而有所不同一样,这使得学习多种语言变得困难。如果每个概念/操作都有其自己的符号,那么我们将需要更大的键盘,因此,在有意义的情况下,这些符号将被回收。
艺术

8
在向其他人教C时,我曾遇到过多次相同的问题,以我的经验,可以按照大多数人的建议来解决。首先,解释没有C语法的指针的概念。然后,教授语法并在类型(int* p)上强调星号,同时警告您的学生在涉及指针时不要在同一行使用多个声明。当学生完全理解了指针的概念后,请向学生说明int *pis语法是等效的,然后使用多个声明说明问题。
Theodoros Chatzigiannakis 2014年

Answers:


43

为了让您的学生理解*符号在不同上下文中的含义,他们必须首先了解上下文的确不同。一旦他们了解了上下文是不同的(即,作业的左手边和一般表达之间的差异),就可以理解差异是不是认知上的飞跃。

首先,说明变量的声明不能包含运算符(通过显示在变量声明中放置-+符号会导致错误来证明这一点)。然后继续说明表达式(即,赋值的右侧)可以包含运算符。确保学生理解表达式和变量声明是两个完全不同的上下文。

当他们了解上下文是不同的时,您可以继续说明,当*符号位于变量标识符前面的变量声明中时,表示“将这个变量声明为指针”。然后,您可以解释说,当在表达式中使用该*符号时(作为一元运算符),该符号是“取消引用运算符”,它的意思是“地址的值”而不是其先前的含义。

为了真正说服您的学生,请说明C的创建者可以使用任何符号来表示解引用运算符(即,他们可以使用该符号@),但是无论出于何种原因,他们都决定使用该设计*

总而言之,无法解释上下文是不同的。如果学生不理解上下文是不同的,他们将不明白为什么*符号可能意味着不同的事物。


80

速记的原因:

int *bar = &foo;

在您的示例中,令人困惑的是,它很容易被误解为等同于:

int *bar;
*bar = &foo;    // error: use of uninitialized pointer bar!

当它实际上意味着:

int *bar;
bar = &foo;

这样写出来,变量声明和赋值分开,不会产生混淆的可能性,并且在K&R引用中描述的使用↔声明并行性非常有效:

  • 第一行声明的变量bar,使得*bar是一个int

  • 第二行指定的地址foobar,使得*bar(一个int)的别名foo(也是一个int)。

在向初学者介绍C指针语法时,最初坚持这种将指针声明与赋值分离的样式可能会有所帮助,并且仅在将指针的基本概念用于以下语言时才引入组合的速记语法(带有适当的警告语,可能引起混淆)。 C已被充分内部化。


4
我很想去typedeftypedef int *p_int;装置,其类型的变量p_int具有这样的属性*p_int是一个int。然后我们有p_int bar = &foo;。鼓励任何人创建未初始化的数据,然后按照默认习惯分配给它,似乎是个坏主意。
Yakk-Adam Nevraumont 2014年

6
这只是C声明的大脑损坏样式;它不特定于指针。考虑一下int a[2] = {47,11};,这不是(不存在)元素的a[2]初始化。
Marc van Leeuwen 2014年

5
@MarcvanLeeuwen同意对大脑的伤害。理想情况下,*应该是类型的一部分,而不是绑定到变量,然后您就可以编写int* foo_ptr, bar_ptr以声明两个指针。但实际上它声明了一个指针和一个整数。
Barmar 2014年

1
这不仅仅是关于“速记”声明/赋值。当您要使用指针作为函数参数时,整个问题再次出现。
阿明2014年

30

声明不足

很高兴知道声明和初始化之间的区别。我们将变量声明为类型,并使用值对其进行初始化。如果我们同时做这两个,我们通常将其称为定义。

1. int a; a = 42;

int a;
a = 42;

我们宣布int一个名为。然后我们通过给它一个值来初始化它42

2. int a = 42;

我们声明int命名为a,并为其赋予值42 42。一个定义。

3. a = 43;

当我们使用变量时,我们说我们它们进行运算a = 43是一项分配操作。我们将数字43分配给变量a。

通过说

int *bar;

我们声明bar为一个指向int的指针。通过说

int *bar = &foo;

我们声明bar并使用foo的地址对其进行初始化。

初始化bar之后,我们可以使用相同的运算符(星号)来访问和操作foo的值。没有操作员,我们将访问指针所指向的地址并对其进行操作。

除此之外,我让图片说话。

什么

关于正在发生的事情的简化的ASCIIMATION。(这里是播放器版本,如果您想暂停等)

          ASCII码化


22

int *bar = &foo;可以在内存中以图形方式查看第二条语句,因为

   bar           foo
  +-----+      +-----+
  |0x100| ---> |  1  |
  +-----+      +-----+ 
   0x200        0x100

现在bar是一个int包含地址&为的类型的指针foo*我们使用一元运算符通过指针来引用以检索'foo'中包含的值bar

编辑:我与初学者的方法是解释memory address变量的

Memory Address:每个变量都有一个由操作系统提供的与之关联的地址。在中int a;&a是变量的地址a

继续以Cas 解释变量的基本类型,

Types of variables: 变量可以保存相应类型的值,但不能保存地址。

int a = 10; float b = 10.8; char ch = 'c'; `a, b, c` are variables. 

Introducing pointers: 如上述变量所示

 int a = 10; // a contains value 10
 int b; 
 b = &a;      // ERROR

可以分配b = a但不能分配b = &a,因为变量b可以保存值但不能保存地址,因此我们需要Pointers

Pointer or Pointer variables :如果变量包含地址,则称为指针变量。*在声明中使用以通知它是一个指针。

 Pointer can hold address but not value
 Pointer contains the address of an existing variable.
 Pointer points to an existing variable

3
问题在于,读取int *ip“ ip是int类型的指针(*)”时,您在读取类似的内容时会遇到麻烦x = (int) *ip
阿明2014年

2
@abw这是完全不同的东西,因此是括号。我认为人们不会很难理解声明和强制转换之间的区别。
bzeaman 2014年

@abw在中x = (int) *ip;,通过取消引用指针来获取值并将其强制ip转换int为任何类型ip
Sunil Bojanapally 2014年

1
@BennoZeeman你是对的:强制转换和声明是两个不同的东西。我试图暗示星号的不同作用:1st“这不是int,而是指向int的指针” 2nd“这将为您提供int,而不是int的指针”。
阿明2014年

2
@abw:这就是为什么教学int* bar = &foo;使负担更有意义的原因。是的,我知道在单个声明中声明多个指针时会引起问题。不,我认为这根本不重要。
Lightness Races in Orbit

17

查看此处的答案和评论,似乎普遍同意,所讨论的语法可能会使初学者感到困惑。他们中的大多数人都提出了以下思路:

  • 在显示任何代码之前,请使用图表,草图或动画来说明指针的工作方式。
  • 介绍语法时,请解释星号符号的两种不同作用。许多教程缺少或回避了该部分。随之而来的是混乱(“当将初始化的指针声明分解为声明和以后的赋值时,您必须记住删除*” – comp.lang.c FAQ我希望找到一种替代方法,但是我想这是要走的路。

您可以写int* bar而不是int *bar突出区别。这意味着您不会遵循K&R的“声明模仿使用”方法,而是采用Stroustrup C ++方法

我们不声明*bar是整数。我们声明barint*。如果我们要在同一行中初始化一个新创建的变量,那么很显然,我们正在处理bar,而不是*barint* bar = &foo;

缺点:

  • 您必须警告学生有关多指针声明问题(int* foo, barvs int *foo, *bar)。
  • 您必须为他们准备好遭受伤害世界。许多程序员希望看到变量名旁边的星号,并且他们会花很长时间来证明自己的风格。许多样式指南都明确地强制使用此符号(Linux内核编码样式,NASA C样式指南等)。

编辑:建议的另一种方法是采用K&R“模仿”方式,但没有“简写”语法(请参见此处)。一旦您省略在同一行中进行声明和赋值,所有内容将看起来更加连贯。

但是,迟早学生将不得不将指针作为函数参数来处理。和指针作为返回类型。和函数指针。你将不得不解释之间的差异int *func();int (*func)();。我认为事情迟早会崩溃。也许早点比晚点好。


16

K&R风格偏爱int *pStroustrup风格偏爱是有原因的int* p;两种语言在每种语言中都是有效的(并且含义相同),但是正如Stroustrup所说:

在“ int * p;”之间进行选择 和“ int * p;” 不是关于是非,而是关于风格和强调。C强调表达;声明常常被认为仅仅是一种必要的邪恶。另一方面,C ++非常重视类型。

现在,由于您正在尝试在这里教C,这意味着您应该更加强调那种类型的表达式,但是有些人比另一种更容易抓住一个重点,而重点在于它们而不是语言。

因此,有些人会发现更容易从一个想法开始,即an与an int*不同int,然后从那里走了。

如果有人迅速神交看它使用的方式int* barbar,因为这是不是一个int的事情,而是一个指针int,那么他们很快就会看到,*bar正在做一些事情bar,其余的将随之而来。完成此操作后,您可以稍后解释为什么C程序员倾向于使用C语言int *bar

或不。如果有一种方法使每个人都首先理解该概念,那么您一开始就不会有任何问题,并且向一个人解释的最佳方法不一定是向另一个人解释的最佳方法。


1
我喜欢Stroustrup的论点,但我不知道他为什么选择&符号来表示引用-另一个可能的陷阱。
阿明2014年

1
@abw我认为他认为如果我们能做到int* p = &a就可以做到对称int* r = *p。我很确定他在《 C ++的设计和演化》中介绍了它,但是距离我读了很长时间,所以我愚蠢地将我的副本租给了某人。
乔恩·汉娜

3
我想你的意思是int& r = *p。我敢打赌,借款人仍在努力消化这本书。
阿明2014年

@abw,是的,这正是我的意思。注释中的输入错误不会引起编译错误。这本书实际上读起来很轻快。
乔恩·汉娜

4
我偏爱Pascal语法(广为扩展)而不是C的原因之一是,Var A, B: ^Integer;这清楚表明“指向整数的指针”类型适用于Aand B。使用K&R样式int *a, *b也是可行的。但声明一样int* a,b;,但是,看起来好像ab都被声明为int*,但在现实中它声明a作为int*b作为int
supercat 2014年

9

tl; dr:

问:如何向初学者解释C指针(声明与一元运算符)?

答:不要。向初学者讲解指针,然后向他们展示如何用C语法表示其指针概念。


最近,我很高兴向C编程初学者介绍了指针,并偶然发现了以下困难。

IMO C语言的语法不是很糟糕,但是也不是很出色:如果您已经了解了指针,那么它也不是一个很大的障碍,也不会对学习它们有任何帮助。

因此:首先说明指针,并确保它们真正理解它们:

  • 用方框图和箭头图解释它们。您可以在不使用十六进制地址的情况下执行此操作,如果它们不相关,则只需显示指向另一个框或某个nul符号的箭头即可。

  • 用伪代码解释:只写foo的地址存储在bar的值

  • 然后,当您的新手了解了什么是指针,为什么以及如何使用它们时;然后显示到C语法的映射。

我怀疑K&R文本未提供概念模型的原因是他们已经了解了指针,并且可能当时还假设所有其他有能力的程序员也都这样做。助记符仅是提示从一个易于理解的概念到语法的映射。


确实; 首先从理论开始,然后是语法(并不重要)。请注意,内存使用率的理论与语言无关。此框和箭头模型将帮助您完成任何编程语言的任务。
oɔɯǝɹ

请参阅此处的示例(尽管Google也会提供帮助)eskimo.com/~scs/cclass/notes/sx10a.html
2015年

7

开始学习C时,这个问题有些令人困惑。

以下是可以帮助您入门的基本原则:

  1. C中只有几种基本类型:

    • char:一个整数值,大小为1个字节。

    • short:一个2字节大小的整数值。

    • long:一个整数值,大小为4个字节。

    • long long:一个整数值,大小为8个字节。

    • float:非整数值,大小为4个字节。

    • double:一个8字节大小的非整数值。

    注意,每种类型的大小通常由编译器而不是标准定义。

    整数类型shortlong并且long long通常接着int

    但是,这不是必须的,您可以在不使用的情况下使用它们int

    或者,您可以只声明int,但是不同的编译器可能会对此做出不同的解释。

    所以总结一下:

    • short与相同short int但不一定相同int

    • long与相同long int但不一定相同int

    • long long与相同long long int但不一定相同int

    • 在给定的编译器上,intshort intlong intlong long int

  2. 如果声明某种类型的变量,则还可以声明另一个指向它的变量。

    例如:

    int a;

    int* b = &a;

    因此,从本质上讲,对于每个基本类型,我们也有一个对应的指针类型。

    例如:shortshort*

    有两种方法可以“查看”变量b (这可能会使大多数初学者感到困惑)

    • 您可以将其b视为type的变量int*

    • 您可以将其*b视为type的变量int

    因此,有些人会宣布int* b,而另一些人会宣布int *b

    但是,事实是这两个声明是相同的(空格是没有意义的)。

    您可以将其b用作指向整数值的指针,也可以*b用作实际的指向整数值。

    您可以获取(读取)目标值:int c = *b

    您可以设置(写入)指向的值:*b = 5

  3. 指针可以指向任何内存地址,而不仅指向先前声明的某个变量的地址。但是,使用指针时必须小心,以获取或设置位于指向的内存地址的值。

    例如:

    int* a = (int*)0x8000000;

    在这里,我们有a指向内存地址0x8000000的变量。

    如果此内存地址未映射到程序的内存空间内,则使用任何读或写操作*a很可能会由于违反内存访问而导致程序崩溃。

    您可以安全地更改的值a,但是您应该非常小心地更改的值*a

  4. 类型void*是例外,因为它没有可以使用的相应“值类型”(即,您不能声明void a)。此类型仅用作指向内存地址的通用指针,而没有指定驻留在该地址中的数据类型。


7

也许稍微多一点就可以使它更容易:

#include <stdio.h>

int main()
{
    int foo = 1;
    int *bar = &foo;
    printf("%i\n", foo);
    printf("%p\n", &foo);
    printf("%p\n", (void *)&foo);
    printf("%p\n", &bar);
    printf("%p\n", bar);
    printf("%i\n", *bar);
    return 0;
}

让他们告诉您他们期望每行输出什么,然后让他们运行程序并查看结果。解释他们的问题(其中的裸露版本肯定会提示一些问题-但稍后您可以担心样式,严格性和可移植性)。然后,在他们的思想因过度思考而陷入混乱或成为午餐后僵尸之前,编写一个具有值的函数和一个具有指针的函数。

以我的经验,它克服了“为什么这样打印?” 驼峰,然后立即通过动手玩法(作为一些基本的K&R资料(如字符串解析/数组处理)的前奏)展示这对函数参数有用的原因,这不仅使本课变得有意义,而且使您牢牢掌握。

下一步是让他们解释如何i[0]涉及到&i。如果他们能够做到这一点,他们将不会忘记它,而您甚至可以提前一点就开始谈论结构,以免陷入困境。

上面有关框和箭的建议也不错,但也可能导致讨论关于内存如何工作的全面讨论-这是必须在某个时候进行的演讲,但可能会分散您的注意力:如何在C中解释指针符号


这是一个很好的练习。但是我想提出的问题是一个特定的句法问题,可能会对学生建立的心理模型产生影响。考虑一下:int foo = 1;。现在,这是确定:int *bar; *bar = foo;。这不行:int *bar = foo;
armin

1
@abw唯一有意义的事情是学生们最终告诉自己的一切。那意味着“看见一个,做一个,教一个”。您无法防止或预测他们会在丛林中看到什么语法或样式(甚至是您的旧存储库!),因此您必须显示足够的排列以使基本概念独立于样式而被理解-并且然后开始教他们为什么要确定某些样式。就像教英语一样:基本表达,习语,样式,特定上下文中的特定样式。不幸的是,这并不容易。无论如何,祝你好运!
zxq9 2014年

6

表达式 的类型*barint; 因此,变量(和表达式) 的类型barint *。由于变量具有指针类型,因此其初始化程序也必须具有指针类型。

指针变量的初始化和赋值之间存在不一致。那只是必须要努力学习的东西。


3
在这里查看答案,我感到许多经验丰富的程序员甚至再也看不到问题了。我猜这是“学习与矛盾生活”的副产品。
阿明2014年

3
@abw:初始化规则与分配规则不同;对于标量算术类型,差异可以忽略不计,但对于指针和聚合类型却很重要。这是您需要与其他所有内容一起解释的内容。
约翰·博德2014年

5

我宁愿将其作为第一个*适用于的int内容阅读bar

int  foo = 1;           // foo is an integer (int) with the value 1
int* bar = &foo;        // bar is a pointer on an integer (int*). it points on foo. 
                        // bar value is foo address
                        // *bar value is foo value = 1

printf("%p\n", &foo);   // print the address of foo
printf("%p\n", bar);    // print the address of foo
printf("%i\n", foo);    // print foo value
printf("%i\n", *bar);   // print foo value

2
然后,您必须解释为什么int* a, b不按照他们的想法去做。
法老

4
是的,但我认为根本不int* a,b应该使用它。为了提高可靠性,进行更新等,每行应该只有一个变量声明,并且永远不要更多。即使编译器也可以处理,这也可以向初学者解释。
grorel 2014年

那是一个人的意见。那里有数百万程序员完全可以声明每行一个以上的变量,并将其作为工作的一部分每天进行。您不能让学生远离其他的做事方式,最好向他们展示所有其他选择,并让他们决定他们想做的事情,因为一旦被雇用,他们将遵循某种风格他们可能会或可能会不满意。对于程序员而言,多功能性是一个非常好的特质。
法拉普2014年

1
我同意@grorel。认为它是*类型的一部分,并且不鼓励它是容易的int* a, b。除非您更喜欢说那*a是类型int而不是a指向int…… 的指针
凯文·乌什

@grorel是正确的:int *a, b;不应使用。在同一条语句中声明两个具有不同类型的变量是非常差的做法,并且很可能成为维护问题的可靠选择。对于在嵌入式领域工作的人来说,也许不同,后者的an int*和an int通常大小不同,有时存储在完全不同的内存位置。它是C语言众多方面之一,最好将其教导为“允许,但不要这样做”。
邪恶的狗馅饼

5
int *bar = &foo;

Question 1:什么bar

Ans:它是一个指针变量(输入int)。指针应指向某个有效的内存位置,以后应使用一元运算符将其取消引用(* bar)*,以便读取存储在该位置的值。

Question 2:什么&foo

Ans:foo是类型为。的变量,int它存储在某个有效的内存位置中,而该位置我们可以从操作员那里获取,&因此现在我们拥有的是某个有效的内存位置&foo

因此,将两者放在一起,即指针所需的是一个有效的内存位置,并且该位置就是这样获得的,&foo因此初始化很好。

现在,指针bar指向有效的内存位置,并且可以将其解引用来获取存储在其中的值,即*bar


5

您应该指出一个初学者,*在声明和表达式中具有不同的含义。如您所知,表达式中的*是一元运算符,而声明中的*不是运算符,而只是一种与类型组合的语法,以使编译器知道它是指针类型。最好对初学者说:“ *的含义不同。为了理解*的含义,您应该找到使用*的位置”


4

我认为魔鬼在太空中。

我会写(不仅是初学者,也是我自己):int * bar =&foo; 而不是int * bar =&foo;

这应该表明语法和语义之间的关系是什么


4

已经注意到*具有多个角色。

还有一个简单的想法可以帮助初学者掌握事物:

认为“ =”也具有多种作用。

当在声明的同一行上使用赋值时,请将其视为构造函数调用,而不是任意赋值。

当你看到:

int *bar = &foo;

认为它几乎等同于:

int *bar(&foo);

括号优先于星号,因此“&foo”更容易直观地归因于“ bar”而不是“ * bar”。


4

我几天前看到了这个问题,然后碰巧正在阅读Go Blog上关于Go的类型声明的解释。它从介绍C类型声明开始,尽管我认为已经给出了更完整的答案,但这似乎是添加到此线程的有用资源。

C对声明语法采取了一种不寻常且巧妙的方法。无需使用特殊语法描述类型,而是编写涉及要声明的项的表达式,并声明该表达式将具有的类型。从而

int x;

声明x为int:表达式'x'的类型为int。通常,要弄清楚如何编写新变量的类型,请编写一个包含该变量的表达式,该表达式的结果为基本类型,然后将基本类型放在左侧,将表达式放在右侧。

因此,声明

int *p;
int a[3];

声明p是指向int的指针,因为'* p'具有int类型,并且a是一个int数组,因为a [3](忽略特定的索引值,该值被冲压为数组的大小)具有类型诠释

(接着描述如何将这种理解扩展到函数指针等)

这是我以前从未考虑过的方法,但是它似乎是解决语法过载的一种非常简单的方法。


3

如果问题是语法,则使用模板/用法显示等效代码可能会有所帮助。

template<typename T>
using ptr = T*;

然后可以用作

ptr<int> bar = &foo;

之后,将普通/ C语法与此仅C ++方法进行比较。这对于解释const指针也很有用。


2
对于初学者来说,这将更加令人困惑。
Karsten 2014年

我的意思是,您不会显示ptr的定义。只需将其用于指针声明即可。
MI3Guy 2014年

3

混淆的根源是这样的事实,即*符号在C中可能具有不同的含义,这取决于使用该符号的事实。为了说明初学者的指针,*应说明不同上下文中符号的含义。

在声明中

int *bar = &foo;  

*符号不是间接运算符。相反,它有助于指定bar通知编译器的类型,该类型bar指向的指针int。另一方面,当它出现在语句中时,*符号(当用作一元运算符时)将执行间接操作。因此,声明

*bar = &foo;

这是错误的,因为它将地址分配给foo指向的对象,bar而不是bar自身。


3

“也许将其写为int * bar使得星号实际上是类型的一部分,而不是标识符的一部分,这更加明显。” 所以我知道 我说,它有点像Type,但仅用于一个指针名称。

“当然,您会遇到int * a,b等不直观的问题。”


2

在这里,您必须使用,理解和解释编译器逻辑,而不是人工逻辑(我知道,是人工的,但是这里您必须模仿计算机...)。

当你写

int *bar = &foo;

编译器将其分组为

{ int * } bar = &foo;

也就是说:这是一个新变量,其名称为bar,其类型为int的指针,其初始值为&foo

并且您必须添加:=上面的内容表示初始化不是影响,而在以下表达式中*bar = 2;影响

按评论编辑:

当心:如果有多个声明,*则仅与以下变量有关:

int *bar = &foo, b = 2;

bar是指向由foo地址初始化的int的指针,b是初始化为2的int,并且in

int *bar=&foo, **p = &bar;

bar是指向int的静态指针,p是指向已初始化为地址或bar的int指针的指针。


2
实际上,编译器不会像这样对它进行分组:int* a, b;将a声明为指向an的指针int,而将b声明为an int。该*符号仅具有两个不同的含义:在声明中,它表示指针类型,在表达式中,它是一元解引用运算符。
tmlen 2014年

@tmlen:我的意思是,在初始化中,*in转换为类型,以便初始化指针,而在情感操作中,指向的值会受到影响。但是至少您给我戴了顶漂亮的帽子:-)
Serge Ballesta 2014年

0

指针基本上不是数组指示。初学者容易认为指针看起来像数组。大部分使用

“ char * pstr”类似,看起来像

“ char str [80]”

但是,重要的是,在较低级别的编译器中,指针仅被视为整数。

让我们看例子:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv, char **env)
{
    char str[] = "This is Pointer examples!"; // if we assume str[] is located in 0x80001000 address

    char *pstr0 = str;   // or this will be using with
    // or
    char *pstr1 = &str[0];

    unsigned int straddr = (unsigned int)pstr0;

    printf("Pointer examples: pstr0 = %08x\n", pstr0);
    printf("Pointer examples: &str[0] = %08x\n", &str[0]);
    printf("Pointer examples: str = %08x\n", str);
    printf("Pointer examples: straddr = %08x\n", straddr);
    printf("Pointer examples: str[0] = %c\n", str[0]);

    return 0;
}

结果将如下所示:0x2a6b7ed0是str []的地址

~/work/test_c_code$ ./testptr
Pointer examples: pstr0 = 2a6b7ed0
Pointer examples: &str[0] = 2a6b7ed0
Pointer examples: str = 2a6b7ed0
Pointer examples: straddr = 2a6b7ed0
Pointer examples: str[0] = T

因此,基本上,记住Pointer是某种整数。出示地址。


-1

我将解释int是对象,float等也是对象。指针是一种对象,其值表示内存中的地址(因此,为什么指针默认为NULL)。

首次声明指针时,将使用类型指针名称语法。它被读为“称为名称的整数指针,可以指向任何整数对象的地址”。我们仅在清算期间使用此语法,类似于将int声明为“ int num1”的方式,但仅在要使用该变量而不是“ int num1”时才使用“ num1”。

int x = 5; //一个值为5的整数对象

int * ptr; //默认情况下为NULL的整数

为了使指针指向对象的地址,我们使用“&”符号,该符号可以读作“的地址”。

ptr =&x; //现在值是'x'的地址

由于指针只是对象的地址,因此要获取该地址处的实际值,我们必须使用“ *”符号,该符号在指针之前使用时表示“指向的地址所指向的值”。

std :: cout << * ptr; //在地址处打印出值

您可以简要地说明“ ”是一个“运算符”,它针对不同类型的对象返回不同的结果。与指针一起使用时,' '运算符不再表示“乘以”。

它有助于绘制一个图表,以显示变量如何具有名称和值,而指针如何具有地址(名称)和值,并表明指针的值将是int的地址。


-1

指针只是用于存储地址的变量。

计算机中的内存由按顺序排列的字节(一个字节由8位组成)组成。每个字节都有一个与之关联的数字,就像数组中的索引或下标一样,这称为字节的地址。字节的地址从0开始到小于内存大小的1。例如,在64MB的RAM中,有64 * 2 ^ 20 = 67108864字节。因此,这些字节的地址将从0到67108863开始。

在此处输入图片说明

让我们看看声明变量时会发生什么。

整数标记;

我们知道一个int占用4个字节的数据(假设我们使用的是32位编译器),因此编译器从内存中保留4个连续字节来存储一个整数值。分配的4个字节中的第一个字节的地址称为可变标记的地址。假设连续4个字节的地址为5004,5005,5006和5007,那么可变标记的地址将为5004。 在此处输入图片说明

声明指针变量

如前所述,指针是存储内存地址的变量。与其他变量一样,您需要先声明一个指针变量,然后才能使用它。这是如何声明指针变量的方法。

句法: data_type *pointer_name;

data_type是指针的类型(也称为指针的基本类型)。pointer_name是变量的名称,可以是任何有效的C标识符。

让我们举一些例子:

int *ip;

float *fp;

int * ip表示ip是一个指针变量,能够指向int类型的变量。换句话说,指针变量ip只能存储int类型的变量的地址。同样,指针变量fp只能存储float类型的变量的地址。变量的类型(也称为基本类型)ip是指向int的指针,而fp是指向float的指针。指向int类型的指针的指针变量可以用符号表示为(int *)。同样,类型为float的指针变量可以表示为(float *)

声明指针变量后,下一步是为其分配一些有效的内存地址。在没有给指针变量分配任何有效内存地址的情况下,切勿使用指针变量,因为在声明之后,该指针变量包含垃圾值,并且可能指向内存中的任何位置。使用未分配的指针可能会产生不可预测的结果。甚至可能导致程序崩溃。

int *ip, i = 10;
float *fp, f = 12.2;

ip = &i;
fp = &f;

资料来源:Thecguru是迄今为止我所发现的最简单但最详细的解释。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.