C++指针(部分有误需修改)
一、取地址运算符&(内存地址)
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++指针(部分有误需修改)的更多相关文章
- 在当前Server上找某某object,注意只需修改"要找的object"就可以使用
---在当前Server上找某某object,注意只需修改"要找的object"就可以使用EXEC sp_MSforeachdb 'use ? ;IF EXISTS(SELECT ...
- centos lamp/lnmp阶段复习 以后搬迁discuz论坛不需要重新安装,只需修改配置文件即可 安装wordpress 安装phpmyadmin 定时备份mysql两种方法 第二十五节课
centos lamp/lnmp阶段复习 以后搬迁discuz论坛不需要重新安装,只需修改配置文件即可 安装wordpress 安装phpmyadmin 定时备份mysql两种方法 第二十五节 ...
- 从零开始学Python第七周:面向对象进阶(需修改)
一,类的属性 (1)示例 通过属性获取已经创建对象的个数 class Plane: pCount = 0 #类属性 def __init__(self,name,category): self.nam ...
- 从零开始学Python第六周:面向对象基础(需修改)
标签(空格分隔): 面向对象 一,面向对象基础 (1)面向对象概述 面向过程:根据业务逻辑从上到下写代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类 ...
- nvmw install 失败. 需修改"Msxml2.XMLHTTP"为"Msxml2.ServerXMLHTTP"
准备在windows下学习nodejs. 下载了nvmw . 但没法安装node的任何版本. 都是报错如下: C:\Users\WXG>nvmw install v0.12.0 x86 Star ...
- Dynamics 365 for CRM:CRM与ADFS安装到同一台服务器,需修改ADFS服务端口号
CRM与ADFS安装到同一台服务器时,出现PluginRegistrationTool 及 CRM Outlook Client连接不上,需要修改ADFS的服务端口号,由默认的808修改为809: P ...
- 前端页面汉子显示为问号,需修改 linux下面修改mysql 数据库的字符编码为utf8
设置MySQL数据库编码为UTF-8 登陆后查看数据库当前编码:SHOW VARIABLES LIKE 'char%'; 修改/etc/mysql/my.cnf (默认安装路径下) (标签下没有的添加 ...
- AccessHelper 需修改
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using ...
- 编写通用shell脚本启动java项目,适用于多数服务,只需修改服务名即可
文件名:service-user.sh 文件内容: ##shell脚本的头文件必须有#!/bin/sh ##再次配置java环境变量以防报其他错误## java env#jdk安装目录export J ...
随机推荐
- control file sequential read 等待事件
可能的原因 control file sequential read Reading from the control file. This happens in many cases. For ex ...
- ubuntu sudo apt-get update 失败 解决方法
sudo apt-get update 报了一堆错误: Err http://cn.archive.ubuntu.com trusty InRelease Err http://cn.archive. ...
- CSS 关于IE6 margin 为负数 负值的时候 正常显示的方法
一定要加position: relative; 有时候比如margin-left的负数,还需要加上如 float:left 属性.
- seleniumAccessors
assertErrorOnNext(message) 告诉Selenium在下一个命令执行时期待有错误. 参数:·message–我们所期望的错误信息.如果出现不正确的错误信息,该命令将失败.同断言相 ...
- robotframework笔记17
执行测试用例 基本用法 机器人框架从命令行执行测试用例,和 最终的结果是,在默认情况下,一个 输出文件 以XML格式和一个HTML 报告 和 日志 . 执行后,可以组合和输出文件 否则 进行后期处理 ...
- Nodejs开发框架Express3.0开发手记–从零开始
转载请注明出处: http://blog.fens.me/nodejs-express3/ 程序代码已经上传到github有需要的同学,自行下载. https://github.com/bsspiri ...
- 数据库中User和Schema的关系
如果我们想了解数据库中的User和Schema到底什么关系,那么让我们首先来了解一下数据库中User和Schema到底是什么概念. 在SQL Server2000中,由于架构的原因,Us ...
- C#容易忽略點--包含多線程 委託事件等等--此頁面bug,編輯能查看全部內容
委託事件 http://www.cnblogs.com/sdya/p/5217635.html 反射 1 创建用于反射使用的DLL 新建一个C#类库项目,拷贝源代码如下,编译生成DLL(假如DLL的文 ...
- MASM6.15汇编程序例子
/***************通过调用(INT 21H)表中的01h号功能号从键盘输入一个字符并回显到视频显示器上*****************/ DATAS SEGMENT ;此处输入数据段代 ...
- c# form的设定
1. 窗体的显示位置startPosition CenterScreen 窗体在当前居中 CenterParent 窗体在其父窗体居中 WindowDefaultBounds 窗体定期在windows ...