C++查缺补漏之数组与指针

一.数组的定义和初始化

1.数组的定义

数组的维数必须使用大于等于一的常量表达式来定义,此常量表达式只能包含整型字面值常量,枚举类型,或者用常量表达式初始化的整型,非const变量和到运行阶段才知道其值的const变量都不能用来定义数组的维数

/** ok 字面值常量 */

int array[10];

/** ok 枚举类型*/

enum ARRAYSIZE{

SIZE1 = 10,

SIZE2 = 12

};

int array2[SIZE1];

/** ok 字面值常量初始化的const整型*/

const unsigned int arraySize =10;

int array3[arraySize];

/** error 非const整型 */

int arraySize = 10;

int array[arraySize];//error: array bound is not an integer constant before ‘]‘ token int array[arraySize];

/** error 在运行时才知道数组的大小 */

const int arraySize = getSize();

int array[arraySize];

注:这样在运行时才知道维数的写法在g++上面编译没问题,但是在VC6上面编译不通过,所以还是建议不要这样写(可能是g++版本比VC6高出好多)

2.数组的初始化

  • 在函数体外定义的内置数组,其元素都被初始化为0
  • 在函数体内定义的内置数组,其元素无初始化
  • 不管在函数内还是函数外定义的类类型数组,都将调用默认构造函数进行初始化(这里类类型一定要有缺省构造函数)
  • 用{}括起来的初始化列表来显式初始化数组时,初始化列表内的元素个数不能大于数组的大小,否则编译报错(error: too many initializers)
  • 用{}括起来的初始化列表显式初始化数组时,初始化列表的元素小于数组元素的个数,那么剩余的元素全都初始化为0

注意:数组的大小一旦确定就不能再更改,如果需要再想向输入里面插入元素,建议使用vector,或者操作内存,分配一块更大的内存(realloc)

3.数组的操作

数组的下标一般来说是size_t类型的,在我们编程过程中如果忘记检查数组的下标是否越界时,可能会造成“缓冲区溢出”

二.指针

什么是指针?指针用于指向对象!

1.指针的初始化和赋值的约束

对于指针的初始化可以使用下面四中类型的值:

  • 0值的常量表达式(在定义指针的时候最好初始化为NULL,野指针很可怕)
  • 类型匹配的对象的地址
  • 另一个对象的下一个地址
  • 同类型的另一个有效指针

把int类型的值赋值给指针是非法的,但是把字面常量值0或者是初始化为0(除0以外的都不行)的const整型赋值给指针就行!

/** ok */

const int myNull = 0;

int * ptr = myNull;

/** error 只可以为0*/

const int myNull = 1;

int * ptr = myNull;

/** error 类型不匹配 */

double dNumber;

int * ptr = &dNumber;

/** ok 强制的类型转换为相同类型的指针 */

double dNumber = 10.0;

//这里会内存截断(8字节->4字节),根据大端小端来指向dNumber在内存里面的高四字节或者低四字节,一旦对ptr解引用,得到的数据是不可预知的!

int * ptr = (int *)(&dNumber);

printf("%d\n",*ptr);

2.void *指针

C++提供了一种特殊的指针,他可以保存任何类型的指针

/** ok */

int number = 10;

double dNumber = 10.0;

void * ptr = &number;

ptr = &dNumber;

注意:void * 指针表明他与一内存地址相关,但是不知道这块内存的类型,所以我们不能对void *指针进行解引用操作,在解引用之前需要进行类型转换!

void * 指针只只支持下面几个有限的指针操作:

  • 进行两个void * 指针的比较
  • 向函数传递void * 指针或者从函数返回void *指针

3.指针和引用的区别

虽然指针和引用都可以间接访问一个值,但是他们之间还是有一些区别的:

  1. 引用总是指向某个对象的(一旦初始化,就永远绑定同一个特定对象),在定义引用时没有初始化是错误的,而指针可以不指向任何对象(赋值为NULL)
  2. 在给引用赋值的时候,修改的是引用所绑定的对象,而给指针赋值时,修改的是指针本身的值,指针指向的对象没有发生改变

4.使用指针访问数组

C++里面,指针和数组密切相关,特别是在表达式中使用数组名时,数组名会自动转化成为指向数组第一个元素的指针

指针和数组的下标操作:

我们知道数组的下标是size_t类型的,也就是unsigned类型的,但是对于指针来说也一定是这样的吗?下面我们来看一个例子:

int intArray[]={1,2,3,4,5};

int * ptr = &intArray[2];

for (int i = -2; i < 3; ++i)

{

printf("%d\n", ptr[i]);

}

这里的ptr下标为-1 -2 都正常使用,这里数组的使用采用一种相对寻址的方法,ptr地址+i的偏移,所以这里可以采用相对寻址

5.指向const的指针和const指针

5.1指向const对象的指针

指向const指针定义如下:

const int * ptr ;

表明ptr指向的对象的值不能修改

const对象一定要用指向const对象的指针来指向,而非const对象既可以用一般指针来指向也可以使用指向const对象的指针来指向,说的有点拗口,下面我们来看一些例子:

/** ok */

const int number = 10;

const int * ptr = &number;

/** error number为const对象,需要用const指针来指向 */

const int number = 10;

int * ptr = &number;//如果这里可以的话,那么就可以通过ptr来为const对象赋值,这是极其危险的!

/** ok */

int number = 10;

const int * ptr = &number;

int * ptr = &number;

下面注意一点:如果是void 类型的指针,那么不能用void *指向const对象,必须要使用const void * 才可以!

5.2const指针

const指针定义如下:

int * const ptr;

表明ptr本身的值不能修改

/** error ptr的值不能修改 */

int number = 0;

int * const ptr = &number;

ptr++;

/** ok 可以修改指向的对象的值*/

int number = 0;

int * const ptr = &number;

*ptr = 10;// number = 10;

5.3指向const对象的const指针

顾名思义,就是指向一个const的指针,指针自身的特性也是const类型的,指针指向的对象值不能改变,自身的指针值也不能改变

6.创建动态数组

创建动态数组一般规则大家知道,我们来看一下一些特殊情况:

int * ptr = new int[-1];

int * ptr = new int [0];

要是申请的数组的大小小于等于0会发生什么?

    int * ptr=NULL;
    try
    {
        ptr  = new int[-1];
    }
    catch(std::bad_alloc e)
    {
        printf("%s\n", "bad alloc");
    }
    printf("%p\n" , ptr);

运行结果为

bad alloc

00000000

抛出了bad_alloc 异常

那要是0呢?

    int * ptr=NULL;
    try
    {
        ptr  = new int[0];
    }
    catch(std::bad_alloc e)
    {
        printf("%s\n", "bad alloc");
    }
    printf("%p\n" , ptr);

运行结果为

00481818

表明在0的时候分配数组成功!那我们来对这个分配好的内粗操作

*ptr = 10;

printf("%d\n",*ptr );

输出10

操作正常!!!在《C++ Primer》上面写的是不能进行解引用操作!求解释???

7.多维数组

在《C陷阱与缺陷》里面提到过,C语言中没有所谓的多维数组,只不过是数组的类型是数组而已也就是说多维数组起始就是这样定义的:

typedef int array_t[3];

array_t array[2];

array就和定义为 array[2][3]一样的;

时间: 2024-10-11 08:36:40

C++查缺补漏之数组与指针的相关文章

Android查缺补漏(View篇)--自定义View利器Canvas和Paint详解

上篇文章介绍了自定义View的创建流程,从宏观上给出了一个自定义View的创建步骤,本篇是上一篇文章的延续,介绍了自定义View中两个必不可少的工具Canvas和Paint,从细节上更进一步的讲解自定义View的详细绘制方法.如果把自定义View比作盖一座房子,那么上篇文章就相当于教会了我们怎么一步步的搭建房子的骨架,而本篇文章将要教会我们的是为房子的骨架添砖加瓦直至成型,甚至是怎么装修. Canvas 为了后文更为方便的讲解Canvas的常用方法的使用,我们先来做一些准备工作,创建一个自定义V

Android查缺补漏--Service和IntentService

Service的运行不依赖界面,即使程序被切换到后台,Service仍然能够保持正常运行.当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行. Service 分为启动状态和绑定状态.当处于仅启动状态时,通过 stopService或 stopSelf 即可停止 Service.当处于绑定状态时需要通过 unBindService 和 stopService 结合使用才能完全停止 Service. 一.Service的生命周期(onCreate()-onStartComma

Android查缺补漏--ContentProvider的使用

ContentProvider (内容提供者)是一种共享型组件,可以为系统内应用于与应用之间提供访问接口. ContentProvide要想正常工作需要三个关键点: ContentProvider:对外提供数据的访问接口. Uri:ContentProvider的唯一标识,外界可根据其访问对应的ContentProvider. ContentResolver 比如,当应用A想把自己数据暴露出来让别的应用也可以操作的话,就可以在应用A内部创建一个ContentProvider实现相关方法并添加UR

Android查缺补漏(View篇)--在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0?

在 Activity 的 onCreate() 方法中为什么获取 View 的宽和高为0 ? @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_view); myview = ViewUtils.find(this, R.id.myview); getViewSize("onCr

Android查缺补漏(View篇)--自定义 View 的基本流程

View是Android很重要的一部分,常用的View有Button.TextView.EditView.ListView.GridView.各种layout等等,开发者通过对这些View的各种组合以形成丰富多彩的交互界面,一个应用中界面交互的体验往往在应用的受欢迎程度上起了很关键得作用,所以开发者们大多会想方设法的做出一个更加精美的界面,例如:通过自定义View.深入学习View的原理以便更好的对其优化使其在操作起来更加流畅等等,也正因为如此,在面试中View也常常作为面试官重点考察的对象之一

Android查缺补漏(View篇)--事件分发机制源码分析

在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)--事件分发机制> ,先来看一下本篇的分析思路,一会儿会按照事件传递的顺序,针对以下几点进行源码分析: Activity对点击事件的分发过程 PhoneWindow是如何处理点击事件的 顶级View对点击事件的分发过程 View对点击事件的处理过程 Activity对点击事件的分发过程 通过上一篇博文中我们

20165306 第二周查缺补漏

第二周查缺补漏 1.位运算符 位运算符主要针对两个二进制数的位进行逻辑运算,所以要先把其他进制数转化为二进制数. 例如: 128&129 128转换成二进制就是10000000,129转换成二进制就是10000001,根据与运算符的运算规律,"只有两个位都是1,结果才是1",可以知道结果就是10000000,即128. 128|129 根据或运算符的运算规律,"只要两个位有一个是1,结果就是1",可以知道结果就是10000001,即129. 15^2 15转

Android查缺补漏(线程篇)-- IntentService的源码浅析

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8975114.html 在Android中有两个比较容易弄混的概念,Service和Thread,我们都知道这两个东西都可以执行后台任务,但要注意的是Service是跑在主线程中的,如果不做特殊处理是会阻塞主线程的,而IntentService正好弥补了这一点,在<Android查缺补漏--Service和IntentService>这篇博文中已经简单介绍过了IntentSe

React查缺补漏之二

译文链接 通过给一个通用函数传入参数定制特定函数的用法 _onFieldChange函数是一个通用实例方法,通过给这个函数传入不同的参数来实现返回结果的不同. 在构造函数中,进行绑定(没有想过这种用法). 1. `this._onNameChanged = this._onFieldChange.bind(this, 'name');` 2. `this._onPasswordChanged =this._onFieldChange.bind(this, 'password');` **注意点击