作者:Mark学编程
说明:本文,首发于LOFTER,然后在我的微信公众号–Mark学编程 发布。如果需要参考以前的文章,请关注公众号,或者在本博客寻找,但本博客可能不全。
刚才看了一位美女的指针授课,是一家有名的公司培训,但指针讲解其实可以更好,这里面有几个技巧或者说,理论知识。先说变量,一定要把变量名,变量值,类型,内存地址等区别和联系起来,尤其是变量名,这个仅仅是个名字,通过他操作变量值而已,经过编译后,变量名就消失了。指针也是如此,把指针简单的说成是地址,那位美女说的,我就不附图了。估计好多书也是这么说的,其实是容易误导的,并且也不正确,下面的英文因为是大师写的,很精确并容易理解。
好了,请看下面的内容,还会有更多的指针内容在这几天发出来。希望有反馈,以便修改,力争做成通俗的指针教程。
为了准确学习指针,我们这里主要以C语言两位大师撰写的书名为 The C Programming language 第二版教程为主学习,大师们表达的更清晰,更准确,反而更容易懂。为了帮助大家理解,我简单改写或删除一些,程序举例我会改用更易懂的我这边的代码,并在必要的地方加上中文解释和我的评论。有需要交流的,可以联系我,或在微信公众号(Mark学编程)联系。因为讨论指针需要长篇大论,所以,今天先写一,写到指针基础为止。
A pointer is a variable that contains the address of a variable. Pointers are much used in C, partly because they are sometimes the only way to express a computation, and partly because they usually lead to more compact and efficient code than can be obtained in other ways. Pointers and arrays are closely related; So we will explores this relationship later on.
A typical machine has an array of consecutively numbered or addressed memory cells that may be manipulated individually or in contiguous groups. One common situation is that any byte can be a char, a pair of one-byte cells can be treated as a short integer, and four adjacent bytes form a long. A pointer is a group of cells (often two or four) that can hold an address. So if c is a char and p is a pointer that points ot it, we could represent the situation this way:
The unary operator & gives the address of an object, so the statement
p = &c;
assigns the address of c to the variable p, and p is said to "point to" c. The & operator only applies to objects in memory: variables and array elements. It cannot be applied to expressions, constants, or register variables.
简要解释上面的内容:指针就是一个变量,用于包含另一个变量的地址。上一次我们已经把内存,地址,变量之间的关系深入探讨了,有问题的话,参照微信公众号-Mark学编程的上一篇。接着, 大师讲了C语言用指针的好处及被诟病的内容。指针被诟病的英文等我没有写。作者接着说,一个典型的计算机内存有连续的有地址(或编号的)的单元,可以操作(估计是指读写)单个的或连续的单元。这里的array不是数组的意思,是一系列,连续的意思。一个典型的例子是任何一个字节内存空间都可以表示一个“字符”,两个字节可以用来存储short类型的数字,4个临近的连续字节空间形成一个long类型空间。一个指针一般是两个或四个单元,用于存放指针(也就是另一个变量的地址),这里表达内存空间用了不同的词,其实是一回事。再次强调,内存空间等的概念一定搞清楚。
上面的图表明了,当c是个char类型字符,p 是指针并指向c的示意图。
& 这个符号在上几节的代码中都已经使用过,它能够给你一个内存中object的地址,这里object代表变量等等。以后你学了java, python等就明白object是什么了。目前就理解成“物体吧”接着下面列出了 p = &c 。这个语句将c变量所在的内存地址赋给了指针(指针也是变量),p 就被看作 “指向” c; 这个一元运算符(一元的意思是只能操作一个操作数,是不是叫单目运算符更准确,不知道,名称好像不统一),只能应用到内存中的变量,数组的元素。 不能用于表达式,常量,或属于register的变量。register 以后会讨论;
The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to. Suppose that x and y are integers and ip is a pointer to int. This artificail sequence shows how to declare a pointer and how to use & and *;
* 一元运算符是一个间接运算符或叫“解引用“运算符;如果你英语好,你就以英文dereferencing来记忆吧。中文的那个词实在有问题。当这个符号和指针连在一起时,他就能够访问(读出或写入)指针指向的变量值呀什么的。注意,有的书上这么强调,当定义指针变量时,用的*符号是表明变量是指针型变量,想想看,没有*会怎样?就成了定义普通变量了。而当访问指针所指向的变量等的值时,*加上指针变量名是正确的书写方式,这个*加上指针就能直接操作指针指向的内存内容-值。举例, 当我们定义了变量
int a = 10;
当我们使用变量时,比如 a + 20; 其实是用10 + 20,同理,当我们用指针(变量)时,我们用的是指针(通过地址)指向的那个值。但需要在指针前面放上*这个符号,这个是规定,否则可能不容易辨识。看大师举的例子:
int x = 1, y = 2, z[10]; // 定义变量并赋值,最后那个是数组元素。
int *ip // 定义了一个指向 int 的指针 ip。
ip = &x; //给指针(变量)赋值,就是把 int 类型的变量 x 的地址赋给 ip,注意前面没有 * ;
y = *ip; //将ip指向的值(1)赋值给 y, 因为ip是指向int类型 x 的指针,所以就是把 x 的值赋给 y ; y 赋值后是 1;
*ip = 0; //x 变量代表的值已经变成 0 了。给ip指向的内存地址赋值就是给x赋值;
ip = &z[0]; //ip now points to z[0];
The declaration of x, y, and z are what we've seen all along. The declaration of the pointer ip,
int * ip; 定义指针(指向int类型的指针,为什么表明类型?因为不同类型要求空间不一样,所以,指针移动一步,指向的类型不同,跨打的步伐不一样大)
is intended as a mnemonic; it says that the expression *ip is an int. The syntax of the declaration for a variable mimics the sysntax of expressions in which the variable might appear. This reasoning applies to function declarations as well. For example, 这里主要说,这么定义,目的是提醒,并且这种格式是简洁的。
double *dp, atof(char *);
says that in an expression *dp and atof(s) have values of double, and that the argument of atof is a pointer to char. 这个表示,*dp 和 函数atof 的值是double 类型的。括号内表明,函数实际调用时,参数是指向char类型的指针。
You should also note the implication that a pointer is constrained to point to a particular kind of object: every pointer points to a specific data type. (one exception) 除了一个例外,每个指针被限制为只能指向一个特定的类型。
if ip points to the integer x, then *ip can occure in any context where x could be, so 如果 ip 是指向整形 x 的指针,那么 *ip 就和 x 一样使用。
*ip = *ip + 10;
incrments *ip by 10. 加了10;
The unary operators * and & bind more tightly than arithmetic operators, so the assignment 这两个单目运算符比算数运算符更紧密的团结在指针周围,所以先算指针指向的数据。
y = *ip + 1;
takes whatever ip points at, then adds 1, and assigns the result to y, 省略大师的其他例子,然后看 (*ip)++;在这种情况下,必须有括号,否则先算加加就惨了,因为加加先对保存的地址加了1,就错了。是指针指向的值加1;下面的解释是 * ++ 都是右结合符。就是把最右边先向左运算。比如 a + b + c 是从左向右结合的,先计算 a + b , 再计算+ c;
如果从右到左,就是先c+b了。而++和是从左到右的,加了括号,因为优先,所以先运算;++这个没有问题,* 这个解释有点问题,还是我没有明白?
The parentheses are necessary in this last example; without them, the expression would increment ip instead of what it points to, because unary operators like * and ++ associate right to left.
Finally, since pointers are variables, they can be used without dereferencing. For example, if pp is another pointer to int, 因为指针就是变量,所以直接赋值给另一个变量,那么新的变量就和指针一样,指向并访问指向的数据了。
好了,就到这里,顺便布置一个家庭作业:你能准确定义什么叫访问吗?
pp = ip; 把ip的值赋值给pp。
copies the contents of ip into pp, thus making pp points to whatever ip pointed to.