简体   繁体   English

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

[英]How to explain C pointers (declaration vs. unary operators) to a beginner?

I have had the recent pleasure to explain pointers to a C programming beginner and stumbled upon the following difficulty.我最近有幸向 C 编程初学者解释指针,并偶然发现了以下困难。 It might not seem like an issue at all if you already know how to use pointers, but try to look at the following example with a clear mind:如果您已经知道如何使用指针,这似乎根本不是问题,但请尝试以清醒的头脑查看以下示例:

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

To the absolute beginner the output might be surprising.对于绝对的初学者来说,输出可能会令人惊讶。 In line 2 he/she had just declared *bar to be &foo, but in line 4 it turns out *bar is actually foo instead of &foo!在第 2 行中,他/她刚刚将 *bar 声明为 &foo,但在第 4 行中,结果证明 *bar 实际上是 foo 而不是 &foo!

The confusion, you might say, stems from the ambiguity of the * symbol: In line 2 it is used to declare a pointer.您可能会说,这种混淆源于 * 符号的歧义:在第 2 行它用于声明一个指针。 In line 4 it is used as an unary operator which fetches the value the pointer points at.在第 4 行中,它用作一元运算符,用于获取指针指向的值。 Two different things, right?两种不同的东西,对吧?

However, this "explanation" doesn't help a beginner at all.然而,这种“解释”对初学者根本没有帮助。 It introduces a new concept by pointing out a subtle discrepancy.它通过指出一个微妙的差异来引入一个新概念。 This can't be the right way to teach it.这不是教它的正确方法。

So, how did Kernighan and Ritchie explain it?那么,Kernighan 和 Ritchie 是如何解释的呢?

The unary operator * is the indirection or dereferencing operator;一元运算符 * 是间接或解引用运算符; when applied to a pointer, it accesses the object the pointer points to.当应用于指针时,它访问指针指向的对象。 […] […]

The declaration of the pointer ip, int *ip is intended as a mnemonic;指针 ip 的声明int *ip旨在作为助记符; it says that the expression *ip is an int.它说表达式*ip是一个int。 The syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear .变量声明的语法模仿可能出现该变量的表达式的语法

int *ip should be read like " *ip will return an int "? int *ip应该读作“ *ip will return an int ”? But why then doesn't the assignment after the declaration follow that pattern?但是为什么声明之后的赋值不遵循这种模式呢? What if a beginner wants to initialize the variable?如果初学者想初始化变量怎么办? int *ip = 1 (read: *ip will return an int and the int is 1 ) won't work as expected. int *ip = 1 (阅读: *ip将返回一个int并且int1 )不会按预期工作。 The conceptual model just doesn't seem coherent.概念模型似乎并不连贯。 Am I missing something here?我在这里错过了什么吗?


Edit: It tried to summarize the answers here .编辑:它试图在这里总结答案

The reason why the shorthand:简写的原因:

int *bar = &foo;

in your example can be confusing is that it's easy to misread it as being equivalent to:在您的示例中可能会令人困惑的是,很容易将其误读为等效于:

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

when it actually means:当它实际上意味着:

int *bar;
bar = &foo;

Written out like this, with the variable declaration and assignment separated, there is no such potential for confusion, and the use ↔ declaration parallelism described in your K&R quote works perfectly:像这样写,变量声明和赋值分开,没有这种混淆的可能性,并且在你的 K&R 引用中描述的 use ↔ 声明并行非常有效:

  • The first line declares a variable bar , such that *bar is an int .第一行声明了一个变量bar ,这样*bar是一个int

  • The second line assigns the address of foo to bar , making *bar (an int ) an alias for foo (also an int ).第二行将foo的地址分配给bar ,使*bar (一个int )成为foo (也是一个int )的别名。

When introducing C pointer syntax to beginners, it may be helpful to initially stick to this style of separating pointer declarations from assignments, and only introduce the combined shorthand syntax (with appropriate warnings about its potential for confusion) once the basic concepts of pointer use in C have been adequately internalized.在向初学者介绍 C 指针语法时,最初坚持这种将指针声明与赋值分开的风格可能会有所帮助,并且只有在指针使用的基本概念在C已被充分内化。

For your student to understand the meaning of the * symbol in different contexts, they must first understand that the contexts are indeed different.为了让您的学生在不同的上下文中理解*符号的含义,他们必须首先了解上下文确实是不同的。 Once they understand that the contexts are different (ie the difference between the left hand side of an assignment and a general expression) it isn't too much of a cognitive leap to understand what the differences are.一旦他们理解上下文是不同的(即作业的左侧和一般表达之间的差异),理解差异是什么并不是太多的认知飞跃。

Firstly explain that the declaration of a variable cannot contain operators (demonstrate this by showing that putting a - or + symbol in a variable declaration simply causes an error).首先解释变量的声明不能包含运算符(通过证明在变量声明中放置-+符号只会导致错误来证明这一点)。 Then go on to show that an expression (ie on the right hand side of an assignment) can contain operators.然后继续证明表达式(即在赋值的右侧)可以包含运算符。 Make sure the student understands that an expression and a variable declaration are two completely different contexts.确保学生理解表达式和变量声明是两个完全不同的上下文。

When they understand that the contexts are different, you can go on to explain that when the * symbol is in a variable declaration in front of the variable identifier, it means 'declare this variable as a pointer'.当他们理解上下文不同时,您可以继续解释当*符号位于变量标识符前面的变量声明中时,它的意思是“将此变量声明为指针”。 Then you can explain that when used in an expression (as a unary operator) the * symbol is the 'dereference operator' and it means 'the value at the address of' rather than its earlier meaning.然后你可以解释当在表达式中使用时(作为一元运算符) *符号是“解引用运算符”,它的意思是“地址处的值”而不是它的早期含义。

To truly convince your student, explain that the creators of C could have used any symbol to mean the dereference operator (ie they could have used @ instead) but for whatever reason they made the design decision to use * .为了真正说服你的学生,解释一下 C 的创建者可以使用任何符号来表示取消引用运算符(即他们可以使用@代替),但无论出于何种原因,他们做出了使用*的设计决定。

All in all, there's no way around explaining that the contexts are different.总而言之,没有办法解释上下文不同。 If the student doesn't understand the contexts are different, they can't understand why the * symbol can mean different things.如果学生不理解上下文不同,他们就无法理解为什么*符号可以表示不同的含义。

Short on declarations短声明

It is nice to know the difference between declaration and initialization.很高兴知道声明和初始化之间的区别。 We declare variables as types and initialize them with values.我们将变量声明为类型并用值初始化它们。 If we do both at the same time we often call it a definition.如果我们同时进行这两项工作,我们通常称其为定义。

1. int a; a = 42; 1. int a; a = 42; int a; a = 42;

int a;
a = 42;

We declare an int named a .我们声明一个名为aint Then we initialize it by giving it a value 42 .然后我们通过给它一个值42初始化它。

2. int a = 42; 2. int a = 42;

We declare and int named a and give it the value 42. It is initialized with 42 .我们声明一个名为a 的int并赋予它值 42。它被初始化为42 A definition.一个定义。

3. a = 43; 3. a = 43;

When we use the variables we say we operate on them.当我们使用变量时,我们说我们它们进行操作 a = 43 is an assignment operation. a = 43是赋值操作。 We assign the number 43 to the variable a.我们将数字 43 分配给变量 a。

By saying通过说

int *bar;

we declare bar to be a pointer to an int.我们将bar声明为一个指向 int 的指针。 By saying通过说

int *bar = &foo;

we declare bar and initialize it with the address of foo .我们声明bar并用foo的地址初始化它。

After we have initialized bar we can use the same operator, the asterisk, to access and operate on the value of foo .初始化bar 后,我们可以使用相同的运算符星号来访问和操作foo的值。 Without the operator we access and operate on the address the pointer is pointing to.没有操作符,我们访问和操作指针指向的地址。

Besides that I let the picture speak.除此之外,我让图片说话。

What什么

A simplified ASCIIMATION on what is going on.关于正在发生的事情的简化 ASCIIMATION。 (And here a player version if you want to pause etc.) (如果你想暂停等,这里有一个播放器版本

ASCII化

The 2nd statement int *bar = &foo;第二条语句int *bar = &foo; can be viewed pictorially in memory as,可以在内存中以图形方式查看,

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

Now bar is a pointer of type int containing address & of foo .现在bar是一个包含地址&int类型的指针foo Using the unary operator * we deference to retrieve the value contained in 'foo' by using the pointer bar .使用一元运算符*我们尊重使用指针bar检索包含在 'foo' 中的值。

EDIT : My approach with beginners is to explain the memory address of a variable ie编辑:我对初学者的方法是解释变量的memory address ,即

Memory Address: Every variable has an address associated with it provided by the OS. Memory Address:每个变量都有一个与之相关的地址,由操作系统提供。 In int a;int a; , &a is address of variable a . , &a是变量a地址。

Continue explaining basic types of variables in C as,继续解释C中变量的基本类型,

Types of variables: Variables can hold values of respective types but not addresses. Types of variables:变量可以保存各自类型的值,但不能保存地址。

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

Introducing pointers: As said above variables, for example Introducing pointers:如上面所说的变量,例如

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

It is possible assigning b = a but not b = &a , since variable b can hold value but not address, Hence we require Pointers .可以分配b = a但不能分配b = a b = &a ,因为变量b可以保存值但不能保存地址,因此我们需要Pointers

Pointer or Pointer variables : If a variable contains an address it is known as a pointer variable. Pointer or Pointer variables :如果变量包含地址,则称为指针变量。 Use * in the declaration to inform that it is a pointer.在声明中使用*来通知它是一个指针。

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

Looking at the answers and comments here, there seems to be a general agreement that the syntax in question can be confusing for a beginner.看看这里的答案和评论,似乎有一个普遍的共识,即所讨论的语法可能会让初学者感到困惑。 Most of them propose something along these lines:他们中的大多数人提出了一些类似的建议:

  • Before showing any code, use diagrams, sketches or animations to illustrate how pointers work.在显示任何代码之前,使用图表、草图或动画来说明指针的工作原理。
  • When presenting the syntax, explain the two different roles of the asterisk symbol .在呈现语法时,解释星号符号的两种不同作用 Many tutorials are missing or evading that part.许多教程都缺少或回避了该部分。 Confusion ensues ("When you break an initialized pointer declaration up into a declaration and a later assignment, you have to remember to remove the *" – comp.lang.c FAQ ) I hoped to find an alternative approach, but I guess this is the way to go.混乱随之而来(“当您将初始化的指针声明分解为声明和稍后的赋值时,您必须记住删除 *” – comp.lang.c FAQ我希望找到一种替代方法,但我想这是要走的路。

You may write int* bar instead of int *bar to highlight the difference.你可以写int* bar而不是int *bar来突出差异。 This means you won't follow the K&R "declaration mimics use" approach, but the Stroustrup C++ approach :这意味着您不会遵循 K&R“声明模拟使用”方法,而是使用Stroustrup C++ 方法

We don't declare *bar to be an integer.我们不将*bar声明为整数。 We declare bar to be an int* .我们将bar声明为int* If we want to initialize a newly created variable in the same line, it is clear that we are dealing with bar , not *bar .如果我们想在同一行初始化一个新创建的变量,很明显我们正在处理bar ,而不是*bar int* bar = &foo;

The drawbacks:缺点:

  • You have to warn your student about the multiple pointer declaration issue ( int* foo, bar vs int *foo, *bar ).你必须警告你的学生关于多指针声明问题( int* foo, bar vs int *foo, *bar )。
  • You have to prepare them for a world of hurt .你必须让他们为受伤世界做好准备。 Many programmers want to see the asterisk adjacent to the name of the variable, and they will take great lengths to justify their style.许多程序员希望看到变量名旁边的星号,他们会不遗余力地证明自己的风格。 And many style guides enforce this notation explicitly (Linux kernel coding style, NASA C Style Guide, etc.).并且许多样式指南明确地强制执行此表示法(Linux 内核编码样式、NASA C 样式指南等)。

Edit: A different approach that has been suggested, is to go the K&R "mimic" way, but without the "shorthand" syntax (see here ).编辑:建议的另一种方法是采用 K&R“模仿”方式,但没有“速记”语法(请参阅此处)。 As soon as you omit doing a declaration and an assignment in the same line , everything will look much more coherent.一旦您省略在同一行中进行声明和赋值,一切都会看起来更加连贯。

However, sooner or later the student will have to deal with pointers as function arguments.然而,学生迟早将不得不将指针作为函数参数来处理。 And pointers as return types.和指针作为返回类型。 And pointers to functions.和指向函数的指针。 You will have to explain the difference between int *func();您将不得不解释int *func();之间的区别int *func(); and int (*func)();int (*func)(); . . I think sooner or later things will fall apart.我想事情迟早会分崩离析。 And maybe sooner is better than later.也许越早越好。

There's a reason why K&R style favours int *p and Stroustrup style favours int* p ; K&R 风格偏爱int *p和 Stroustrup 风格偏爱int* p both are valid (and mean the same thing) in each language, but as Stroustrup put it:两者在每种语言中都是有效的(并且意思相同),但正如 Stroustrup 所说:

The choice between "int* p;" "int* p;" 之间的选择and "int *p;"和“int * p;” is not about right and wrong, but about style and emphasis.不是关于对与错,而是关于风格和重点。 C emphasized expressions; C 强调表达式; declarations were often considered little more than a necessary evil.声明通常被认为只不过是一种必要的罪恶。 C++, on the other hand, has a heavy emphasis on types.另一方面,C++ 非常重视类型。

Now, since you're trying to teach C here, that would suggest you should be emphasising expressions more that types, but some people can more readily grok one emphasis quicker than the other, and that's about them rather than the language.现在,既然你试图在这里教 C,那意味着你应该更多地强调表达式,而不是类型,但有些人可以更容易地理解一个重点而不是另一个重点,这是关于它们而不是语言。

Therefore some people will find it easier to start with the idea that an int* is a different thing than an int and go from there.因此,有些人会发现很容易开始的想法是一个int*是不同的东西比int从那里走。

If someone does quickly grok the way of looking at it that uses int* bar to have bar as a thing that is not an int, but a pointer to int , then they'll quickly see that *bar is doing something to bar , and the rest will follow.如果有人确实很快理解了使用int* barbar作为不是 int 而是指向int的指针的查看方式,那么他们很快就会看到*bar正在对bar做一些事情,并且其余的将随之而来。 Once you've that done you can later explain why C coders tend to prefer int *bar .完成后,您可以稍后解释为什么 C 编码人员倾向于更喜欢int *bar

Or not.或不。 If there was one way that everybody first understood the concept you wouldn't have had any problems in the first place, and the best way to explain it to one person will not necessarily be the best way to explain it to another.如果有一种方法让每个人都首先理解这个概念,那么你一开始就不会有任何问题,向一个人解释它的最佳方式不一定是向另一个人解释它的最佳方式。

tl;dr: tl;博士:

Q: How to explain C pointers (declaration vs. unary operators) to a beginner?问:如何向初学者解释 C 指针(声明与一元运算符)?

A: don't.答:不要。 Explain pointers to the beginner, and show them how to represent their pointer concepts in C syntax after.向初学者解释指针,然后向他们展示如何用 C 语法表示他们的指针概念。


I have had the recent pleasure to explain pointers to a C programming beginner and stumbled upon the following difficulty.我最近有幸向 C 编程初学者解释指针,并偶然发现了以下困难。

IMO the C syntax isn't awful, but isn't wonderful either: it's neither a great hindrance if you already understand pointers, nor any help in learning them. IMO 的 C 语法并不糟糕,但也不是很好:如果您已经理解指针,它既不是一个很大的障碍,也不是学习它们的任何帮助。

Therefore: start by explaining pointers, and make sure they really understand them:因此:从解释指针开始,并确保他们真正理解它们:

  • Explain them with box-and-arrow diagrams.用方框和箭头图解释它们。 You can do it without hex addresses, if they're not relevant, just show the arrows pointing either to another box, or to some nul symbol.您可以在没有十六进制地址的情况下执行此操作,如果它们不相关,只需显示指向另一个框或某个空符号的箭头即可。

  • Explain with pseudocode: just write address of foo and value stored at bar .用伪代码解释:只写foo 的地址存储在 bar 中的值

  • Then, when your novice understands what pointers are, and why, and how to use them;然后,当您的新手了解什么是指针,为什么以及如何使用它们时; then show the mapping onto C syntax.然后显示到 C 语法的映射。

I suspect the reason the K&R text doesn't provide a conceptual model is that they already understood pointers , and probably assumed every other competent programmer at the time did too.我怀疑 K&R 文本没有提供概念模型的原因是他们已经理解了指针,并且可能假设当时所有其他有能力的程序员也理解了。 The mnemonic is just a reminder of the mapping from the well-understood concept, to the syntax.助记符只是提醒人们从易于理解的概念到语法的映射。

This issue is somewhat confusing when starting to learn C.这个问题在开始学习 C 的时候有些混乱。

Here are the basic principles that might help you get started:以下是可能有助于您入门的基本原则:

  1. There are only a few basic types in C: C中只有几种基本类型:

    • char : an integer value with the size of 1 byte. char : 一个大小为 1 字节的整数值。

    • short : an integer value with the size of 2 bytes. short : 一个大小为 2 个字节的整数值。

    • long : an integer value with the size of 4 bytes. long : 一个 4 字节大小的整数值。

    • long long : an integer value with the size of 8 bytes. long long :一个8字节大小的整数值。

    • float : a non-integer value with the size of 4 bytes. float :大小为 4 个字节的非整数值。

    • double : a non-integer value with the size of 8 bytes. double :大小为 8 字节的非整数值。

    Note that the size of each type is generally defined by the compiler and not by the standard.请注意,每种类型的大小通常由编译器定义,而不是由标准定义。

    The integer types short , long and long long are usually followed by int .整数类型shortlonglong long通常后跟int

    It is not a must, however, and you can use them without the int .但是,这不是必须的,您可以在没有int情况下使用它们。

    Alternatively, you can just state int , but that might be interpreted differently by different compilers.或者,您可以只声明int ,但这可能会被不同的编译器以不同的方式解释。

    So to summarize this:所以总结一下:

    • short is the same as short int but not necessarily the same as int . shortshort int相同,但不一定与int相同。

    • long is the same as long int but not necessarily the same as int . longlong int相同,但不一定与int相同。

    • long long is the same as long long int but not necessarily the same as int . long longlong long int相同,但不一定与int相同。

    • On a given compiler, int is either short int or long int or long long int .在给定的编译器上, int要么是short int要么是long int要么是long long int

  2. If you declare a variable of some type, then you can also declare another variable pointing to it.如果你声明了某个类型的变量,那么你也可以声明另一个指向它的变量。

    For example:例如:

    int a;

    int* b = &a;

    So in essence, for each basic type, we also have a corresponding pointer type.所以本质上,对于每个基本类型,我们也有一个对应的指针类型。

    For example: short and short* .例如: shortshort*

    There are two ways to "look at" variable b (that's what probably confuses most beginners) :有两种方法可以“查看”变量b (这可能会让大多数初学者感到困惑)

    • You can consider b as a variable of type int* .您可以将b视为int*类型的变量。

    • You can consider *b as a variable of type int .您可以将*b视为int类型的变量。

    Hence, some people would declare int* b , whereas others would declare int *b .因此,有些人会声明int* b ,而其他人会声明int *b

    But the fact of the matter is that these two declarations are identical (the spaces are meaningless).但事实是这两个声明是相同的(空格没有意义)。

    You can use either b as a pointer to an integer value, or *b as the actual pointed integer value.您可以使用b作为指向整数值的指针,或使用*b作为实际指向的整数值。

    You can get (read) the pointed value: int c = *b .您可以获取(读取)指向的值: int c = *b

    And you can set (write) the pointed value: *b = 5 .您可以设置(写入)指向值: *b = 5

  3. A pointer can point to any memory address, and not only to the address of some variable that you have previously declared.指针可以指向任何内存地址,而不仅仅是指向您之前声明的某个变量的地址。 However, you must be careful when using pointers in order to get or set the value located at the pointed memory address.但是,在使用指针以获取或设置位于指向的内存地址的值时必须小心。

    For example:例如:

    int* a = (int*)0x8000000;

    Here, we have variable a pointing to memory address 0x8000000.在这里,我们有a指向内存地址 0x8000000 的变量a

    If this memory address is not mapped within the memory space of your program, then any read or write operation using *a will most likely cause your program to crash, due to a memory access violation.如果这个内存地址没有映射到你程序的内存空间中,那么任何使用*a读或写操作很可能会导致你的程序崩溃,因为内存访问冲突。

    You can safely change the value of a , but you should be very careful changing the value of *a .您可以安全地修改的值a ,但你应该非常小心改变的值*a

  4. Type void* is exceptional in the fact that it doesn't have a corresponding "value type" which can be used (ie, you cannot declare void a ).类型void*是个例外,因为它没有可以使用的相应“值类型”(即,您不能声明void a )。 This type is used only as a general pointer to a memory address, without specifying the type of data that resides in that address.此类型仅用作指向内存地址的通用指针,而不指定驻留在该地址中的数据类型。

Perhaps stepping through it just a bit more makes it easier:也许多走一步会让它更容易:

#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;
}

Have them tell you what they expect the output to be on each line, then have them run the program and see what turns up.让他们告诉您他们期望每一行的输出是什么,然后让他们运行程序并查看结果。 Explain their questions (the naked version in there will certainly prompt a few -- but you can worry about style, strictness and portability later).解释他们的问题(那里的裸版肯定会提示一些——但你可以在以后担心样式、严格性和可移植性)。 Then, before their mind turns to mush from overthinking or they become an after-lunch-zombie, write a function that takes a value, and the same one that takes a pointer.然后,在他们的头脑因过度思考而变得糊涂或成为午餐后僵尸之前,编写一个接受值的函数,以及接受指针的同一个函数。

In my experience its getting over that "why does this print that way?"根据我的经验,它克服了“为什么这种打印方式?” hump, and then immediately showing why this is useful in function parameters by hands-on toying (as a prelude to some basic K&R material like string parsing/array processing) that makes the lesson not just make sense but stick. hump,然后通过动手玩弄(作为一些基本 K&R 材料的前奏,如字符串解析/数组处理)立即展示为什么这在函数参数中很有用,这使课程不仅有意义而且坚持。

The next step is to get them to explain to you how i[0] relates to &i .下一步是让他们向解释i[0]&i If they can do that, they won't forget it and you can start talking about structs, even a little ahead of time, just so it sinks in.如果他们能做到这一点,他们就不会忘记它,你可以开始谈论结构,甚至提前一点,这样它就会深入人心。

The recommendations above about boxes and arrows is good also, but it can also wind up digressing into a full-blown discussion about how memory works -- which is a talk that must happen at some point, but can distract from the point immediately at hand: how to interpret pointer notation in C.上面关于盒子和箭头的建议也很好,但它也可能最终离题,进入关于内存如何工作的全面讨论——这是一个必须在某个时候发生的谈话,但可能会分散手头的注意力:如何解释 C 中的指针符号。

The type of the expression *bar is int ;表达式*bar的类型是int thus, the type of the variable (and expression) bar is int * .因此,变量(和表达式) barint * Since the variable has pointer type, its initializer must also have pointer type.由于变量具有指针类型,因此其初始值设定项也必须具有指针类型。

There is an inconsistency between pointer variable initialization and assignment;指针变量初始化和赋值存在不一致; that's just something that has to be learned the hard way.这只是必须通过艰苦的方式学习的东西。

int *bar = &foo;

Question 1 : What is bar ? Question 1 :什么是bar

Ans : It is a pointer variable(to type int ). Ans :它是一个指针变量(输入int )。 A pointer should point to some valid memory location and later should be dereferenced(*bar) using a unary operator * in order to read the value stored in that location.一个指针应该指向某个有效的内存位置,然后应该使用一元运算符*取消引用 (*bar) 以读取存储在该位置的值。

Question 2 : What is &foo ? Question 2 :什么是&foo

Ans : foo is a variable of type int .which is stored in some valid memory location and that location we get it from the operator & so now what we have is some valid memory location &foo . Ans :foo是类型的变量int和。其中存储在一些有效的存储位置,该位置我们从运营商处获得它&现在这样,我们所拥有的是一些有效的内存位置&foo

So both put together ie what the pointer needed was a valid memory location and that is got by &foo so the initialization is good.所以两者放在一起,即指针需要的是一个有效的内存位置,这是由&foo获得的&foo所以初始化很好。

Now pointer bar is pointing to valid memory location and the value stored in it can be got be dereferencing it ie *bar现在指针bar指向有效的内存位置,存储在其中的值可以取消引用它,即*bar

I'd rather read it as the first * apply to int more than bar .我宁愿将其阅读为第一个*适用于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

You should point out a beginner that * has different meaning in the declaration and the expression.您应该指出*在声明和表达式中具有不同含义的初学者。 As you know, * in the expression is a unary operator, and * In the declaration is not an operator and just a kind of syntax combining with type to let compiler know that it is a pointer type.如你所知,表达式中的*是一元运算符,而声明中的*不是运算符,只是一种与类型结合的语法,让编译器知道它是一个指针类型。 it is better to say a beginner, "* has different meaning. For understanding the meaning of *, you should find where * is used"初学者最好说“*有不同的含义。要理解*的含义,您应该找到*的使用位置”

I think the devil is in the space.我认为魔鬼在太空中。

I would write (not only for the beginner, but for myself as well): int* bar = &foo;我会写(不仅为初学者,也为我自己): int* bar = &foo; instead of int *bar = &foo;而不是 int *bar = &foo;

this should make evident what is the relationship between syntax and semantics这应该表明句法和语义之间的关系是什么

It was already noted that * has multiple roles.已经注意到 * 有多个角色。

There's another simple idea that may help a beginner to grasp things:还有一个简单的想法可以帮助初学者掌握事物:

Think that "=" has multiple roles as well.认为“=”也有多个角色。

When assignment is used on the same line with declaration, think of it as a constructor call, not an arbitrary assignment.当赋值与声明在同一行使用时,将其视为构造函数调用,而不是任意赋值。

When you see:当你看到:

int *bar = &foo;

Think that it's nearly equivalent to:认为它几乎相当于:

int *bar(&foo);

Parentheses take precendence over asterisk, so "&foo" is much more easily intuitively attributed to "bar" rather than "*bar".括号优先于星号,因此“&foo”更容易直观地归因于“bar”而不是“*bar”。

If the problem is the syntax, it may be helpful to show equivalent code with template/using.如果问题出在语法上,使用模板/使用显示等效代码可能会有所帮助。

template<typename T>
using ptr = T*;

This can then be used as这可以用作

ptr<int> bar = &foo;

After that, compare the normal/C syntax with this C++ only approach.之后,将普通/C 语法与这种仅限 C++ 的方法进行比较。 This is also useful for explaining const pointers.这对于解释常量指针也很有用。

The source of confusion arises from the fact that * symbol can have different meanings in C, depending upon the fact in which it is used.混淆的根源在于*符号在 C 中可能具有不同的含义,具体取决于使用它的事实。 To explain the pointer to a beginner, the meaning of * symbol in different context should be explained.为了向初学者解释指针,应该解释*符号在不同上下文中的含义。

In the declaration在声明中

int *bar = &foo;  

the * symbol is not the indirection operator . *符号不是间接运算符 Instead, it helps to specify the type of bar informing the compiler that bar is a pointer to an int .相反,它有助于指定bar的类型,通知编译器bar指向int指针 On the other hand, when it appears in a statement the * symbol (when used as a unary operator ) performs indirection.另一方面,当它出现在语句中时, *符号(当用作一元运算符时)执行间接操作。 Therefore, the statement因此,声明

*bar = &foo;

would be wrong as it assigns the address of foo to the object that bar points to, not to bar itself.将是错误的,因为它将foo的地址分配给bar指向的对象,而不是bar本身。

"maybe writing it as int* bar makes it more obvious that the star is actually part of the type, not part of the identifier." “也许把它写成 int* bar 会更明显地表明星号实际上是类型的一部分,而不是标识符的一部分。” So I do.所以我愿意。 And I say, that it is somesing like Type, but only for one pointer name.我说,它有点像 Type,但只针对一个指针名称。

" Of course this runs you into different problems with unintuitive stuff like int* a, b." “当然,这会让你遇到像 int* a, b 这样不直观的东西的不同问题。”

I saw this question a few days ago, and then happened to be reading the explanation of Go's type declaration on the Go Blog .前几天看到这个问题,后来正好在看Go Blog上对Go的类型声明的解释。 It starts off by giving an account of C type declarations, which seems like a useful resource to add to this thread, even though I think that there are more complete answers already given.它首先介绍了 C 类型声明,这似乎是添加到此线程的有用资源,尽管我认为已经给出了更完整的答案。

C took an unusual and clever approach to declaration syntax. C 对声明语法采用了一种不同寻常且巧妙的方法。 Instead of describing the types with special syntax, one writes an expression involving the item being declared, and states what type that expression will have.不是用特殊的语法描述类型,而是编写一个涉及被声明项的表达式,并说明该表达式将具有什么类型。 Thus因此

int x;

declares x to be an int: the expression 'x' will have type int.将 x 声明为 int:表达式 'x' 将具有 int 类型。 In general, to figure out how to write the type of a new variable, write an expression involving that variable that evaluates to a basic type, then put the basic type on the left and the expression on the right.通常,要弄清楚如何编写新变量的类型,请编写一个涉及该变量的表达式,该表达式的计算结果为基本类型,然后将基本类型放在左侧,将表达式放在右侧。

Thus, the declarations因此,声明

int *p; int a[3];

state that p is a pointer to int because '*p' has type int, and that a is an array of ints because a[3] (ignoring the particular index value, which is punned to be the size of the array) has type int.声明 p 是一个指向 int 的指针,因为 '*p' 的类型是 int,而 a 是一个 int 数组,因为 a[3](忽略特定的索引值,它被认为是数组的大小)具有类型内部

(It goes on to describe how to extend this understanding to function pointers etc) (它继续描述如何将这种理解扩展到函数指针等)

This is a way that I've not thought about it before, but it seems like a pretty straightforward way of accounting for the overloading of the syntax.这是一种我以前没有考虑过的方法,但它似乎是一种非常直接的解释语法重载的方法。

Here you have to use, understand and explain the compiler logic, not the human logic (I know, you are a human, but here you must mimic the computer ...).在这里你要使用、理解和解释编译器逻辑,而不是人的逻辑(我知道,是人,但在这里你必须模仿计算机......)。

When you write当你写

int *bar = &foo;

the compiler groups that as编译器将其分组为

{ int * } bar = &foo;

That is : here is a new variable, its name is bar , its type is pointer to int, and its initial value is &foo .即:这是一个新变量,它的名字是bar ,它的类型是指向 int 的指针,它的初始值是&foo

And you must add : the = above denotes an initialization not an affectation, whereas in following expressions *bar = 2;并且您必须添加:上面的=表示初始化而不是做作,而在以下表达式中*bar = 2; it is an affectation做作

Edit per comment:每条评论编辑:

Beware : in case of multiple declaration the * is only related to the following variable :注意:在多重声明的情况下, *仅与以下变量相关:

int *bar = &foo, b = 2;

bar is a pointer to int initialized by the address of foo, b is an int initialized to 2, and in bar 是一个指向 int 的指针,由 foo 的地址初始化,b 是一个初始化为 2 的 int,而 in

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

bar in still pointer to int, and p is a pointer to a pointer to an int initialized to the address or bar. bar in 仍然是指向 int 的指针,而 p 是指向初始化为地址或 bar 的 int 指针的指针。

Basically Pointer is not a array indication.基本上指针不是数组指示。 Beginner easily thinks that pointer looks like array.初学者很容易认为指针看起来像数组。 most of string examples using the大多数字符串示例使用

"char *pstr" it's similar looks like "char *pstr" 它看起来很相似

"char str[80]" “字符 str[80]”

But, Important things , Pointer is treated as just integer in the lower level of compiler.但是,重要的是,指针在编译器的较低级别中仅被视为整数。

Let's look examples::让我们看看例子::

#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;
}

Results will like this 0x2a6b7ed0 is address of str[]结果会像这样 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

So, Basically, Keep in mind Pointer is some kind of Integer.所以,基本上,请记住指针是某种整数。 presenting the Address.介绍地址。

I would explain that ints are objects, as are floats etc. A pointer is a type of object whose value represents an address in memory ( hence why a pointer defaults to NULL ).我将解释 int 是对象,浮点数等也是。指针是一种对象类型,其值表示内存中的地址(因此指针默认为 NULL )。

When you first declare a pointer you use the type-pointer-name syntax.当你第一次声明一个指针时,你使用 type-pointer-name 语法。 It's read as an "integer-pointer called name that can point to the address of any integer object".它被读作“名为 name 的整数指针,可以指向任何整数对象的地址”。 We only use this syntax during decleration, similar to how we declare an int as 'int num1' but we only use 'num1' when we want to use that variable, not 'int num1'.我们只在声明期间使用这种语法,类似于我们如何将 int 声明为“int num1”,但我们只在想要使用该变量时使用“num1”,而不是“int num1”。

int x = 5;整数 x = 5; // an integer object with a value of 5 // 一个值为 5 的整数对象

int * ptr; int * ptr; // an integer with a value of NULL by default // 默认值为 NULL 的整数

To make a pointer point to an address of an object we use the '&' symbol which can be read as "the address of".为了使指针指向对象的地址,我们使用“&”符号,该符号可以读作“的地址”。

ptr = &x; ptr = &x; // now value is the address of 'x' // 现在值是 'x' 的地址

As the pointer is only the address of the object, to get the actual value held at that address we must use the '*' symbol which when used before a pointer means "the value at the address pointed to by".由于指针只是对象的地址,要获得该地址的实际值,我们必须使用“*”符号,当在指针之前使用时,它表示“指向的地址处的值”。

std::cout << *ptr; std::cout << *ptr; // print out the value at the address // 打印出地址处的值

You can explain briefly that ' ' is an 'operator' that returns different results with different types of objects.您可以简要说明“ ”是一个“运算符”,它对不同类型的对象返回不同的结果。 When used with a pointer, the ' ' operator doesn't mean "multiplied by" anymore.当与指针一起使用时,' ' 运算符不再意味着“乘以”。

It helps to draw a diagram showing how a variable has a name and a value and a pointer has an address (the name) and a value and show that the value of the pointer will be the address of the int.它有助于绘制一个图表,显示变量如何具有名称和值以及指针如何具有地址(名称)和值,并表明指针的值将是 int 的地址。

A pointer is just a variable used to store addresses.指针只是用于存储地址的变量。

Memory in a computer is made up of bytes (A byte consists of 8 bits) arranged in a sequential manner.计算机中的内存由按顺序排列的字节(一个字节由 8 位组成)组成。 Each byte has a number associated with it just like index or subscript in an array, which is called the address of the byte.每个字节都有一个与之关联的数字,就像数组中的索引或下标一样,称为字节的地址。 The address of byte starts from 0 to one less than size of memory.字节地址从 0 开始,比内存大小小 1。 For example, say in a 64MB of RAM, there are 64 * 2^20 = 67108864 bytes .例如,假设在 64MB 的 RAM 中,有 64 * 2^20 = 67108864 bytes 。 Therefore the address of these bytes will start from 0 to 67108863 .因此,这些字节的地址将从 0 开始到 67108863 。

在此处输入图片说明

Let's see what happens when you declare a variable.让我们看看当你声明一个变量时会发生什么。

int marks;整数标记;

As we know an int occupies 4 bytes of data (assuming we are using a 32-bit compiler) , so compiler reserves 4 consecutive bytes from memory to store an integer value.我们知道一个 int 占用 4 个字节的数据(假设我们使用的是 32 位编译器),因此编译器从内存中保留 4 个连续字节来存储整数值。 The address of the first byte of the 4 allocated bytes is known as the address of the variable marks . 4 个分配字节的第一个字节的地址称为变量标记的地址。 Let's say that address of 4 consecutive bytes are 5004 , 5005 , 5006 and 5007 then the address of the variable marks will be 5004 .假设连续 4 个字节的地址是 5004 、 5005 、 5006 和 5007 ,那么变量标记的地址将是 5004 。 在此处输入图片说明

Declaring pointer variables声明指针变量

As already said a pointer is a variable that stores a memory address.如前所述,指针是一个存储内存地址的变量。 Just like any other variables you need to first declare a pointer variable before you can use it.就像任何其他变量一样,您需要先声明一个指针变量,然后才能使用它。 Here is how you can declare a pointer variable.以下是声明指针变量的方法。

Syntax: data_type *pointer_name;语法: data_type *pointer_name;

data_type is the type of the pointer (also known as the base type of the pointer). data_type 是指针的类型(也称为指针的基类型)。 pointer_name is the name of the variable, which can be any valid C identifier. pointer_name 是变量的名称,它可以是任何有效的 C 标识符。

Let's take some examples:让我们举一些例子:

int *ip;

float *fp;

int *ip means that ip is a pointer variable capable of pointing to variables of type int . int *ip 表示 ip 是一个能够指向 int 类型变量的指针变量。 In other words, a pointer variable ip can store the address of variables of type int only .换句话说,指针变量 ip 只能存储 int 类型变量的地址。 Similarly, the pointer variable fp can only store the address of a variable of type float .同样,指针变量 fp 只能存储 float 类型变量的地址。 The type of variable (also known as base type) ip is a pointer to int and type of fp is a pointer to float .变量的类型(也称为基类型) ip 是一个指向 int 的指针,而 fp 的类型是一个指向 float 的指针。 A pointer variable of type pointer to int can be symbolically represented as ( int * ) .指向 int 的指针类型的指针变量可以象征性地表示为 (int *)。 Similarly, a pointer variable of type pointer to float can be represented as ( float * )类似地,指向 float 的指针类型的指针变量可以表示为 ( float * )

After declaring a pointer variable the next step is to assign some valid memory address to it.声明指针变量后,下一步是为其分配一些有效的内存地址。 You should never use a pointer variable without assigning some valid memory address to it, because just after declaration it contains garbage value and it may be pointing to anywhere in the memory.你不应该在没有为其分配一些有效内存地址的情况下使用指针变量,因为在声明之后它包含垃圾值并且它可能指向内存中的任何地方。 The use of an unassigned pointer may give an unpredictable result.使用未分配的指针可能会产生不可预测的结果。 It may even cause the program to crash.它甚至可能导致程序崩溃。

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

ip = &i;
fp = &f;

Source: thecguru is by far the simplest yet detailed explanation I have ever found.资料来源: thecguru是迄今为止我发现的最简单但详细的解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM