一、取地址运算符&(内存地址)

C++编译的程序占用的内存分为以下几个部分:

1.栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。与其它分区不同,变量的地址是按照申请顺序降序排列的。

2.堆区: 由new指令申请,由delete指令释放。申请后不释放可能会造成内存资源的浪费。需要指出,对于用指针申请的对内存空间,指针本身存入栈,指针指向的空间则存储在堆中。

3.全局(静态)变量区:全局变量和静态(由static修饰)变量的存储是放在一块的。从程序开始运行时写入变量值,运行结束前释放变量。

4.程序代码区:用于存放函数体的二进制代码。

另外,还有专门用于存放常量的区域。

下面编写程序进行测试,证明以上几种变量分区不同。

#include <iostream>
using namespace std;
int x,y,z;
 
void f1(){};
void f2(){};
 
void main()
{
    int a,b,c;
    cout<<"局部变量的地址:"<<endl;
    cout<<&a<<" "<<&b<<" "<<&c<<endl;//地址降序
 
    int *m = new int[3], *n = new int, *l = new int, *k = new int;
    cout<<"指针所指向的内存的地址:"<<endl;
    cout << &(*n) << " " << &(*l) << " " << &(*k) << endl;//指针指向堆空间地址,地址降序
    cout<<"指针指向空间的地址:"<<endl;
    cout <<&(m[0])<< " " <<&(m[1])<< " " <<&(m[2])<< endl;
 
    static int w;
    cout<<"全局(局部)变量的地址:"<<endl;
    cout<<&x<<" "<<&y<<" "<<&z<</*" "<<&w<<*/endl;
 
    cout<<"函数代码的地址:"<<endl;
    cout<<&main<<" "<<&f1<<" "<<&f2<<endl;
}

程序运行结果:

由程序运行结果可以看出:

1. 四种变量的地址相互相差很大,而本身相差很小,说明被分配在了不同的内存空间。

2. 指针和变量的地址是降序排列的,说明被分配在了栈区。

3. 全局变量和局部变量的地址相邻,说明二者被安排在了同一分区。

注意:不要把取地址运算符&和应用运算符&搞混,前者放在变量定义中的变量的前面,而后者放在函数原型定义中的类型名后面。

二、指针变量

我们要想更好的利用上面提到的地址,就必须设定一个变量来保存地址。像用变量来保存int、float等类型的值一样,地址也可以用类似的方法保存。

定义:用来保存地址的变量叫做指针变量(pointer variable)或者简称指针。

如:int* ptr;星号表示指向谁,这个声明定义了一个指向整型数据的指针变量。这个变量中保存整型数据的地址:ptr=&variable;

1) 访问指针指向的变量。

*ptr就表示了ptr中的地址上存储的值。变量名前面的星号表示间接引用运算符(dereference operator)或者叫做取内容运算符(contents operator)。

2)void指针

指针保存的变量必须和指针的类型保持一致,如不能将一个float类型的地址赋给一个指向int的指针。

float flovar=98.6;
int* ptr=&flovar;  //错误:不能将一个float类型的地址赋给一个指向int的指针

但是有一个通用的指针可指向任何数据类型,这类指针成为void:如void* ptr;

三、指针和数组

首先来看看数组表示法和指针表示法访问同一数组元素:

#include <iostream>
using namespace std;
int main()
{
    int intarray[5]={31,54,36,47,38};
    for (int j=0;j<5;j++)
    {
        cout<<intarray[j]<<endl   //数组表示法
            <<*(intarray+j)<<endl;//指针表示法
    }
    return 0;
}

指针表示法:数组名就是数组的首地址,在这里+j就是数组第j+1个元素的地址。再加上取内容运算符*就得到了地址上储存的数据了。

考虑:为什么系统能知道+j就跳到j+1个元素的地址呢,在这里地址明明四个字节四个字节的增长的?

答:还记得前面讨论void指针的时候说的吗,在指针说明的时候必须包含指针所指向的数据类型,因为编译器要知道一个指针是int类型的还是float类型的,这样它才能在访问数组元素的时候执行真确的算术运算。

1) 指针常量和指针变量

表达式intarray是系统分给数组空间的首地址,数组将一直保持着这个地址知道程序结束。intarray是一个指针常量,不能进行++或者--运算,就和不能说7++一样。只有变量才可以进行这样的运算。

四、指针和函数

1)首先,同样进行引用传递参数和指针传递参数的对比:

#include <iostream>
using namespace std;
void funcref(double&);
void funcptr(double*);
int main()
{
    double var=10.0;
    cout<<"前var="<<var<<endl;
    funcref(var);
    cout<<"引用var="<<var<<endl;
    funcptr(&var);
    cout<<"指针var="<<var<<endl;
    return 0;
}
void funcref(double& v)
{
    v *= 3.14;
}
void funcptr(double* ptr)
{
    *ptr*=2;
}

在这里,指针作为参数和应用一样,他传递的不是变量本身,而是变量的地址。(引用是原始变量的一个别名,指针是原始变量的地址)

因为ptr中包含的是var的地址,所以任何对*ptr的操作实际上都是对var的操作。

2)排序数组元素

冒泡法排序:

#include<iostream>
using namespace std;
int main()
{
    void order(int*,int);
    const int N=10;
    int array[N]={1,3,234,23,54,656,76,878,87,57};
    cout<<"比较前:";
    for(int j=0;j<N;j++)
        cout<<*(array+j)<<" ";
    order(array,N);
    cout<<"\n比较后:";
    for(int j=0;j<N;j++)
        cout<<*(array+j)<<" ";
    cout<<endl;
    return 0;
}
void order(int* ptr,int n)
{
    void bijiao(int*,int*);
    for (int j=0;j<n-1;j++)
    {
        for (int i=j+1;i<n;i++)
        {
            bijiao(ptr+j,ptr+i);
        }
    }
}
void bijiao(int* m,int* n)
{
    int tem;
    if (*m>*n)
    {
        tem=*m;
        *m=*n;
        *n=tem;
    }
}

五、指针和C类型字符串

1)字符串常量指针:char str1[]=”Defined as an array”;(c类型字符串)

char* str2=”Defined as an array”;(指针类型字符串)

2)看个程序:

#include<iostream>
using namespace std;
int main()
{
    void copystr(char*,const char*);
    char* str1="hah is hahah!";
    char str2[80];
    copystr(str2,str1);
    cout<<str2<<endl;
    return 0;
}
void copystr(char* dest,const char* src)
{
    while (*src)
    {
        *dest++=*src++;
    }
    *dest='\0';
}

※注意※=====const的使用================================================ 
(http://bbs.chinaunix.net/viewthread.php?tid=683333&extra=&page=1) 
关键问题点:const 属于修饰符 ,关键是看const 修饰的位置在那里 
1、const int *a 
这里const 修饰的是int,而int定义的是一个整值 
因此*a 所指向的对象 值 不能通过 *a 来修改,但是 可以重新给 a 来赋值,使其指向不同的对象 
eg: 
  const int *a = 0; 
  const int b = 1; 
  int c = 1; 
  a = &b //ok! 额外:注意不能通过a 来修改 b值 
  a = &c //ok! 额外:虽然c本身不是一个常量 
  *a = 2 //erro! 为题就在这里,不能修改通过 *a 所指向的对象值,最后赋值得对象是c,因此不能通过*a 来修改c值。 
2、int *const a   
这里const修饰的是 a ,a代表的是一个指针地址 
因此不能赋给a其他的地址值,但可以修改a指向的值 
这有点和cont int *a相反的意味,例子就不说了 
3、至于int const *a 和 const int *a 的意义是相同的 他们两个的作用等价 
补充: 
4、const int * const a  
这个代表a所指向的对象的值以及它的地址本身都不能被改变 
5.const int a = 10 和 int const a = 10有什么区别? 
这应该没区别,指针的话有区别  
对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const。 
关于const的点滴补充: 
1、const 对象的地址只能赋值给指向const 对象的指针 
2、指向const 对象的指针可以 被赋 以 一个非const 对象的地址  
3、指向const 的指针常被用作函数的形式参数,保证被传递给函数的实际对象在函数得实际对象在函数中不会被修改 
4、常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误(上面都是在说明const得问题,所以没有赋值,实际语句中要赋值的)

3)指针数组和数组指针

==================指针数组,数组指针================================== 
对于理解指针数组和数组指针也比较有用,如: 
--------------指针数组(首先是个数组)-------------------- 
int *p[10];//指针数组,含有10个指针元素  
也就是说每一个元素都是指针。先是解析[]表示它是一个数组,然后*表示指针,int表示为int型指针,即表示定义一个指针数组,含有10个int类型指针元素。 
--------------数组指针(首先是个指针)-------------------- 
int (*p)[10];//数组指针,这个指针能够用来指向含有10个元素的整数数组。 
先是解析(),括号里表示这个是个指针,然后[]表示数组,即表示定义了一个指向数组的指针。数组指针。

C++指针(部分有误需修改)的更多相关文章

  1. 在当前Server上找某某object,注意只需修改"要找的object"就可以使用

    ---在当前Server上找某某object,注意只需修改"要找的object"就可以使用EXEC sp_MSforeachdb 'use ? ;IF EXISTS(SELECT ...

  2. centos lamp/lnmp阶段复习 以后搬迁discuz论坛不需要重新安装,只需修改配置文件即可 安装wordpress 安装phpmyadmin 定时备份mysql两种方法 第二十五节课

    centos  lamp/lnmp阶段复习 以后搬迁discuz论坛不需要重新安装,只需修改配置文件即可 安装wordpress  安装phpmyadmin  定时备份mysql两种方法  第二十五节 ...

  3. 从零开始学Python第七周:面向对象进阶(需修改)

    一,类的属性 (1)示例 通过属性获取已经创建对象的个数 class Plane: pCount = 0 #类属性 def __init__(self,name,category): self.nam ...

  4. 从零开始学Python第六周:面向对象基础(需修改)

    标签(空格分隔): 面向对象 一,面向对象基础 (1)面向对象概述 面向过程:根据业务逻辑从上到下写代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类 ...

  5. nvmw install 失败. 需修改"Msxml2.XMLHTTP"为"Msxml2.ServerXMLHTTP"

    准备在windows下学习nodejs. 下载了nvmw . 但没法安装node的任何版本. 都是报错如下: C:\Users\WXG>nvmw install v0.12.0 x86 Star ...

  6. Dynamics 365 for CRM:CRM与ADFS安装到同一台服务器,需修改ADFS服务端口号

    CRM与ADFS安装到同一台服务器时,出现PluginRegistrationTool 及 CRM Outlook Client连接不上,需要修改ADFS的服务端口号,由默认的808修改为809: P ...

  7. 前端页面汉子显示为问号,需修改 linux下面修改mysql 数据库的字符编码为utf8

    设置MySQL数据库编码为UTF-8 登陆后查看数据库当前编码:SHOW VARIABLES LIKE 'char%'; 修改/etc/mysql/my.cnf (默认安装路径下) (标签下没有的添加 ...

  8. AccessHelper 需修改

    using System; using System.Collections.Generic; using System.Configuration; using System.Data; using ...

  9. 编写通用shell脚本启动java项目,适用于多数服务,只需修改服务名即可

    文件名:service-user.sh 文件内容: ##shell脚本的头文件必须有#!/bin/sh ##再次配置java环境变量以防报其他错误## java env#jdk安装目录export J ...

随机推荐

  1. 每一个可以移动的棋子都要移动——Every-SG 游戏

    先看一个问题 HDU 3595 GG and MM (Every_SG博弈) 题目有N个游戏同时进行,每个游戏有两堆石子,每次从个数多的堆中取走数量小的数量的整数倍的石子.取最后一次的获胜.并且N个游 ...

  2. eclipse如何调试(Debug)程序(zhuan)

    http://jingyan.baidu.com/article/e6c8503c7e46b6e54f1a18c5.html ************************************* ...

  3. hiho_1070_RMQ

    题目 区间最小值查询,但是支持对数组中的任意数字进行修改. 分析 采用RMQ_ST算法的O(1)算法不支持修改,因为每次修改都要重新设置动归数组.因此采用线段树解决,修改和查询的复杂度均为O(logN ...

  4. gdb 调试学习

    gdb 是unix/linux 系统下的程序调试工具,和IDE(如VS, Eclipse等)的图形化调试工具相比,gdb在断点,跟踪显示方面有着不足,但是它在某些方面比图形化调试工具更加丰富的功能. ...

  5. WAP调用微信支付https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1

    公司做的一个购物网站 之前微信版的网站要搬在webView上   可是微信支付是个问题 , 在外部浏览器怎么都发不起微信请求 , 原因是因为页面调用的微信浏览器自带JSAPI 在外部浏览器无法调用,但 ...

  6. semantic-ui使用gulp执行build-css报错

    1.执行gulp build-css报错 [09:40:49] Starting 'build-css'... Building CSS Potentially unhandled rejection ...

  7. media query ie8- 兼容实现总结

    虽然说响应式设计的理想状态是,需对pc/移动各种终端进行响应:但是现实是高分辨率的pc端与手机终端屏幕相差太大,像电商这样有大量图片和文字信息的同时排版要求精准的页面,设计一个同时适应高分辨率pc又适 ...

  8. GitHub学习资料

    GitHub账户注册注册了有一年多了(Joined on 13 Apr 2015),一直以来都是本地命令行上传到内网的Git服务器Gitlab.最近正好在学习新的编程语言,所以当初荒废的GitHub想 ...

  9. [JS] 面向对象的5种写法和拓展JS对象的写法

    面向对象的JAVA  最开始当然是对象的定义了 收集了以下的五种写法 //第1种写法 function Circle(r) { this.r = r; } Circle.PI = 3.14159; C ...

  10. webservice方法内用了session,asp.net调用时注意问题

    可搜索相关:ASP.NET WebService 中使用 ASP.NET_SessionId 当前问题是:我们写了WebService给客户调用,第一个方法是登陆,传入用户名和密码,成功后在Webse ...