原文链接:http://www.orlion.ga/916/

一、指针的基本操作

例:

int i;
int *pi = &i;
char c;
char *pc = &c;

"&"是取地址符,&i表示取变量i的值,int *pi = &i;表示定义一个指向int型的指针变量pi,并用i的地址来初始化pi。全局变量只能用常量表达式初始化,如果定义int p = i;就错了,因为i不是常量表达式,然后用i的地址来初始化一个指针却没有错,是因为i的地址是在编译链接时能确定的,而不需要到运行时才知道,&i是常量表达式。后面两行定义了一个字符型变量c和一个指向c的字符型指针pc,注意pi与pc虽然是不同类型的指针变量,但他们的内存单元都占4个字节,因为要保存32位的虚拟地址,同理在64位平台上指针变量都占8个字节。如果要让pi指向另外一个整型变量可以重新对pi赋值:pi = &j;如果要改变pi所指向的整型变量的值,比如把变量j的值加10,可以:

*pi = *pi + 10;

指针之间可以相互赋值,也可以用一个指针初始化另一个指针:

int *ptri = pi;

或者:

int *ptri;
ptri = pi;

表示pi指向哪就让ptri指向哪,本质上是把变量pi所保存的地址值赋给变量ptri。

用一个指针给另一个指针赋值时要注意,两个指针必须是同一类型的,pi是int *型,pc是char * 型,pi = pc这样赋值就是错误的。但是可以先强制类型转换:

pi = (int *)pc;

有一种情况需要注意:

int main(void)
{
    int *p;
    ...
    *p = 0;
    ...
}

在堆栈上分配的变量初始值是不确定的,也就是说指针p所指向的内存地址是不确定的,后面用*p访问不确定的地址就会导致不确定的后果,如果导致段错误还比较容易改正,如果意外改写了数据而导致随后的运行中出错就不好调试了。像这种指向不确定地址的指针称为野指针(Unbound Pointer),为避免野指针,在定义指针变量时就应该给它明确的初值,或者把它初始化为NULL:

int main(void)
{
    int *p = NULL;
    ...
    *p = 0;
    ...
}

NULL在C标准库的头文件stddef.h中定义:

#define NULL ((void *)0)

就是把地址0转换为指针类型,称为空指针,它的特殊之处在于操作系统不会把任何数据保存在地址0及其附近,也不会把地址0-0xfff的页面映射到物理内存,所以任何对地址0的访问都会立刻导致段错误。*p = 0会导致段错误。

在编程时经常需要一种通用指针,可以转换为任意其他类型的指针,任意其他类型的指针也可以转换为通用指针。只能定义void *类型的指针而不能定义void型的变量,因为void *指针和别的指针都一样占4个字节,而如果定义void变量(也就是类型不确定的变量)编译器不知道应该分配几个字节给变量。void *指针不能直接Dereference,而必须先转换成别的类型的指针再做Dereference。void *指针常用于函数接口:

void func(void *pv)
{
        /* *pv = 'A' is illegal */
        char *pchar = pv;
        *pchar = 'A';
}
int main(void)
{
        char c;
        func(&c);
...
}

二、指针类型的参数和返回值

#include <stdio.h>

int *swap(int *px, int *py)

{

int temp;

temp = *px;

*px = *py;

*py = temp;

return px;

}

int main(void)

{

int i = 10, j = 20;

int *p = swap(&i, &j);

printf("now i=%d j=%d *p=%d\n", i, j, *p);

return 0;

}

    

尽管在swap函数的作用域中访问不到i和j这两个变量名,却可以通过地址访问它们,最终swap函数将i和j的值做了交换。

三、指针与数组

int a[10];
int *pa = &a[0];
pa++;

对于上边的这个例子,指针pa指向a[0]的地址,后缀运算符的优先级高于单目运算符,所以是取a[0]的地址,而不是取a的地址。然后pa++让pa指向下一个元素(也就是a[1]),由于pa是int *指针,一个int型元素占4个字节,所以pa++使pa指向的地址加4而不是1。

*(pa+2)等价于pa[2],pa就像数组名一样。a[2]之所以能取数组下标为2的元素是因为它等价于*(a+2),数组名做右值时自动转为指向首元素的指针,所以a[2]与pa[2]本质上是一样的。由于(*((E1) + (E2)))等价于(*((E2) + (E1))),所以a[2]可以写成2[a]。另外由于a做右值使用时和&a[0]是一个意思,所以int *pa = &a[0]通常会写成int *pa = a;

C语言规定只有指向同一个数组中元素的指针之间相互比较才有意义,否则没有意义。那么两个指针相减表示什么?pa – a等于几?因为pa – 1 == a,所以pa – a显然应该等于1,指针相减表示两个指针之间相差的元素个数,同样只有指向同一个数组中元素的指针之间相减才有意义。两个指针相加表示什么?想不出来它能有什么意义,因此C语言也规定两个指针不能相加。

C指针(一)的更多相关文章

  1. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  2. enote笔记法使用范例(2)——指针(1)智能指针

    要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...

  3. C++虚函数和函数指针一起使用

    C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...

  4. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  5. c 数组与指针的使用注意事项

    数组变量和指针变量有一点小小的区别 所以把数组指针赋值给指针变量的时候千万要小心 加入把数组赋值给指针变量,指针变量只会包含数组的地址信息 而对数组的长度一无所知 相当于指针丢失了一部分信息,我们把这 ...

  6. Marshal.Copy将指针拷贝给数组

    lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...

  7. C++智能指针

    引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...

  8. EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针

    一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...

  9. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  10. 智能指针unique_ptr的用法

    unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...

随机推荐

  1. Oracle分区

    可以参考文档:http://docs.oracle.com/cd/E18283_01/server.112/e16541/part_admin001.htm#insertedID0 (支持11g和12 ...

  2. 简述HTML DOM及其节点分类

    在JavaScript中,document这个对象大家一定很熟悉,哪怕是刚刚开始学习的新人,也会很快接触到这个对象.而document对象不仅仅是一个普通的JavaScript内置对象,它还是一个巨大 ...

  3. 【WEB】一个简单的WEB服务器

    WEB 服务器如何工作的?   HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则.计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从H ...

  4. 关于js单线程(转载)

    进程和线程都是操作系统的概念.进程是应用程序的执行实例,每一个进程都是由私有的虚拟地址空间.代码.数据和其它系统资源所组成:进程在运行过程中能够申请创建和使用系统资源(如独立的内存区域等),这些资源也 ...

  5. css3 filter的十种特效

    filter默认值是none,他不具备继承性,其中filter-function具有一下属性: grayscale灰度 sepia褐色(求专业指点翻译) saturate饱和度 hue-rotate色 ...

  6. 把C#程序(含多个Dll)合并成一个Exe的超简单方法

    开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. 但是,很多时候我们本想开发一款只需要一个exe就能完美运行的小工具.那该怎么办呢? 下文介绍一种超 ...

  7. ubuntu环境下vmware取消自动启动服务

    概述其实vmware这个服务取不取消,影响不大,主要是我有强迫症,在不用虚拟机的时候,看着vmware占着进程真心不爽,想要解决这个问题,在用虚拟机的时候启动服务,反之,则停.接下来,我说一下实现吧. ...

  8. CSS外边距叠加问题

    CSS外边距叠加就是margin-collapse,边距合并指的是,当两个垂直外边距相遇时,它们将形成一个外边距,水平边 距永远不会重合.重叠结果计算规则:①.两个相邻的外边距都是正数时,折叠结果是它 ...

  9. HOJ 1797 Red and Black

    传送门  http://acm.hit.edu.cn/hoj/problem/view?id=1797 总体的思路是遍历可以到达的' . ',将其对应的vis数组化为1,然后统计所有为1的vis项; ...

  10. unity3D学习—坦克大战(一)

    背景介绍 本人一名C#程序员,从事C#开发已经有四年有余了,目前在一家大型公司上班.鉴于公司的业务需要,现在需要学习unity3D游戏开发,好在unity支持C#脚本开发,无形中省下了许多重新学习新语 ...