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

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. Spring集成JPA提示Not an managed type

    在做Spring与JPA集成时,出现问题如下: Caused by: java.lang.IllegalArgumentException: Not an managed type: class co ...

  2. Java集合的Stack、Queue、Map的遍历

    Java集合的Stack.Queue.Map的遍历   在集合操作中,常常离不开对集合的遍历,对集合遍历一般来说一个foreach就搞定了,但是,对于Stack.Queue.Map类型的遍历,还是有一 ...

  3. Python设计模式之单例模式

    1.由于语言的特性不同,设计模式的实现方式和实现难度也会不同 2.有的模式已经在语言内置了,比如迭代器模式. 3.单例模式可以直接用模块级变量来实现 4.普通工厂模式可以直接通过传入"类名& ...

  4. OpenCV3编程入门笔记(2)计时函数、感兴趣区域RIO、分离/混合通道

    11     绘制直线的line函数 DrawLine(Mat img, Pont start, Point end); 绘制椭圆的ellipse函数 DrawEllipse(Mat img, dou ...

  5. a、b交换与比较

    1.有两个变量a,b,不用if.?: .switch 或其他判断语句,找出两个数中 较大的: int max = ((a+b)+abs(a-b))/2 较小的: int min = ((a+b)-ab ...

  6. Java 创建xml文件和操作xml数据

    java中的代码 import java.io.File; import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; ...

  7. golang作为server向android提供数据服务

    中间交换的数据是json ,后台数据库服务器是sqlserver2012 android通过post或者get方式访问 如get方式http://192.168.255.13:7080/tblFile ...

  8. 能源项目xml文件 -- app-datasource.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  9. Win7下Maven的安装与配置

    简介  官网:https://maven.apache.org/ Apache Maven,是一个(特别是Java软件)项目管理及自动构建工具,由Apache软件基金会所提供.基于项目对象模型(Pro ...

  10. [redis] Redis 常用命令

    redis命令文档:http://doc.redisfans.com/index.html 1. redis查看当前所有的key KEYS * 模糊匹配keykeys 模糊字符串*   2. 查看当前 ...