C指针与内存

  指针是C / C++ 中重要的构造类型,指针赋予了C / C++程序直接访问和修改内存的能力。C / C++的许多重要应用,如编译、OS、嵌入式开发都依赖于这种能力。

  冯诺依曼体系的计算机内存存储指令和数据,我们可以将其抽象为指令区和数据区(当然实际情况要复杂得多)。数据区中包含栈(stack)和堆(heap)区,栈区的数据由编译器管理而堆区数据则由程序员管理。

  由于指令同样存在于内存中,那么函数在内存中也会拥有地址(指针)。 函数指针相对于函数名来说 可以在运行期动态地选择调用的函数,这一特性是C++实现动态多态性的重要基础。

一、 指针的基本用法

  指针是一种构造类型,所有的基本类型和struct等都有自己的指针。指针包含首地址(即指针的值)和指向对象的类型两部分信息,所以指针并不严格等价于地址。

  不要使用未初始化的指针否则将导致严重的运行时错误,所幸操作系统的内存管理可以保证操作系统自身和其它进程的稳定,但你的程序肯定会异常退出了。指针的类型是重要的,指向struct的指针初始化后可以使用ptr -> member表达式来访问某一个成员,但是未经初始化或void 指针将导致错误或警告。

  在《C编程基础》中提到,方括号([]),指针(*)这些符号在声明语句和执行语句中的含义有所不同,但依旧具有运算符的一些特性。

即指针(*),方括号([])以及函数调用的(参数表)在声明语句中 可以认为是将一个标识符标记为特殊类型的标志,当同一个声明语句中存在多个标志时,它们按照表达式求解的顺序决定声明的类型:

  (1)基本声明:

     int *p; 声明指针p(注意不是*p),*p表示其指向的数据。

     int p[M]; 声明长为M的一维数组p。

     int p[M][M]; 声明M×M二维数组p。

  (2)复合声明:

     int **p 声明p为二重指针(不是**p),则 *p为一重指针,**p为数据对象。

     int *p[M] 声明指针数组,初等运算符从右向左结合首先声明一个数组,然后声明数组元素类型为指针。

     int *fun(...) 声明返回指针的函数,从右向左首先声明fun为函数,然后声明返回值类型为指针。

     int (*ptr) (...) 声明ptr为函数指针,从右向左首先声明这是一个函数,(*ptr)声明这是一个函数指针。使用函数指针时只需以(*ptr)代替函数名即可,例:

   qsort库函数使用函数指针作谓词函数

#include<stdio.h>

#include<stdlib.h>

int tell(const void *a,const void *b) {

   int x, y;

   x = *(int *)a;

   y = *(int *)b;

   return x - y;

}

int main(void) {

   ] = {  };

   int (*tp) (const void *a, void *b);

   tp = tell;

   scanf("%d", &m);

   ; i < m; i++) {

     scanf("%d", &a[i]);

   }

   qsort (a, m,sizeof(int ),tp);

   ;i<m;i++) {

printf("%d ",a[i]);

   }

   printf("\n");

   ;

}

   指针作为函数参数则可以实现传址调用,下面给出一个最简单的交换两个数的程序:

#include<stdio.h>

   int swap(int *pa, int *pb) {
             int t;

  t = *pa; // *pa is "a" itself

  *pa = *pb;

  *pb = t;

  ;

         }

   int main(void) {

  int a,b;

  scanf("%d %d",&a,&b);

  swap(&a,&b); // using &a as a ptr to "a"

  printf("%d %d\n",a,b);

  ;

   } 

·常指针

    const int * ptr;  int const * ptr; 指向常对象的指针

    int * const ptr = &a; 指向不能改变的指针,必须初始化

    const int * const ptr = &a;  int const * const ptr;

   指向与对象均不能改变的指针;

   以*为标志,靠近基本类型的为指向常对象,靠近指针名为指向不变。

二、 数组及其存储

  1.一维数组

  上文已经说明数组的声明方式:

    (1)声明一个长度为8的int数组arr ];

    (2)数组的长度必须为常正整数,不能是const对象【const int n = 8;int arr[n];】;可以是枚举元素但不能是枚举对象,通常使用字面值或宏来作为长度 #define N 10 ;

    (3)数组下标从0开始(不是1);

    (4)方括号([])可以访问数组的某个成员, arr[i];arr[]; 。

  在函数内定义的数组不会自动初始化,在函数外定义的数组将自动初始化为0(或等价值)。在函数内定义数组同时初始化时,未初始化变量将自动初始为0值 ] = {,,}; 常用这个特性将数组全部初始化为0值, ] = {}; 。将所有元素初始化时不需要指定数组长度,即 ,}; 与 ] = {,} 等价。

  2.高维数组

  我们可以将高维数组理解为数组的数组,即高维数组的元素是低一维的数组。因为数组就名就是其首地址,那么,高维数组的元素是低维数组的首地址。以二维数组为例,利用a[i]<=>*(a+i)逐级展开

        a[i][j] <=> *(a[i]+j) <=> *(*(a+i)+j)

  i的单位为一维数组的长度,j的单位为基本类型的长度。推导时,将[]展开即可,加一个&就是少一个*。

        *(a+i)+j <=> a[i]+j <=> &a[i][j]

  高维数组的初始化有两种形式。首先将其元素理解为数组,每一个数组的初值用一个花括号括起,把它们当做一个元素用另一个大括号括起。高维数组在内存中是线性存储的,可以根据它在内存中的存储顺序当做一维数组进行初始化。这两种方式下,没有被赋值的元素均被自动赋0。在对所有元素赋值时,第一维的长度可以省略由编译系统求出,其它维数不可省略。

  3.

  数组是一段连续的内存空间,数组名可以认为是常指针(不允许更改指向)。从数组实现来看,下标从0开始是非常自然的。

  C对下标的处理方法的处理方法就是将它转化为地址,a[i]与*(a+i)无条件等价。

三、动态内存管理

  1.分配内存(memory alloc):

     void *malloc(unsigned int size); (常用)

    (1) 在内存中分配一个大小为size的连续空间,参数size为无符号整型(不允许为负数)。函数的返回值是所分配区域第一个字节的地址。如果函数不能成功地执行(如内存空间不足)则返回空指针(NULL)。

    (2) void指针类型不能理解为指向任何类型,而应理解为指向不确定类型的数据的指针。应用指派运算符(强制转换)将void指针变为具体的类型指针,size参数应用sizeof()运算符求得以避免错误,如结构体的大小不严格等于所有成员大小之和并提高程序可移植性。例如:

     int * ptr = (int *)malloc( sizeof(int)* N );

    声明ptr指向一段长度为N连续int型空间(实际上是一个长度为N的int数组)。

  2.分配连续空间:

     void *calloc(unsigned n,unsigned size);

    在动态存储区分配n个长度为size的连续空间,可以用来保存一个数组。

  3.释放内存(free):

     void free(void *p);

    释放指针变量p所指向的动态空间,无返回值。例如:  free(ptr);

  4.重新分配内存(realloc):

     void *realloc(void *p,unsigned int size);

    将p指向的动态空间大小改变为size。

C指针与内存的更多相关文章

  1. 基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

    本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...

  2. C 语言中的指针和内存泄漏

    引言对于任何使用 C 语言的人,如果问他们 C 语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄漏.这些的确是消耗了开发人员大多数调试时间的事项.指针和内存泄漏对某些开发人员来说似乎令人畏惧 ...

  3. C语言中的指针和内存泄漏

    引言 对于任何使用C语言的人,如果问他们C语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄漏.这些的确是消耗了开发人员大多数调试时间的事项.指针和内存泄漏对某些开发人员来说似乎令人畏惧,但是 ...

  4. 【ZZ】C 语言中的指针和内存泄漏 & 编写高效的C程序与C代码优化

    C 语言中的指针和内存泄漏 http://www.ibm.com/developerworks/cn/aix/library/au-toughgame/ 本文讨论了几种在使用动态内存分配时可以避免的陷 ...

  5. Delphi 的内存操作函数(2): 给数组指针分配内存

    静态数组, 在声明时就分配好内存了, 譬如: var   arr1: ..] of Char;   arr2: ..] of Integer; begin   ShowMessageFmt('数组大小 ...

  6. [Windows] [VS] [C] [取得指针所指内存的二进制形式字符]

    // 取得指针所指内存的十六进制形式字符串,size指定字节长度#define Mem_toString(address, size) _Mem_toString((PBYTE)address, si ...

  7. [Windows] [VS] [C] [取得指针所指内存的十六进制形式字符串]

    接口定义如下: #include <Windows.h> // 取得指针所指内存的十六进制形式字符串,size指定字节长度 #define Mem_toString(address, si ...

  8. [C]C语言中的指针和内存泄漏几种情况

    引言 原文地址:http://www.cnblogs.com/archimedes/p/c-point-memory-leak.html,转载请注明源地址. 对于任何使用C语言的人,如果问他们C语言的 ...

  9. C语言中的指针和内存泄漏几种情况

    引言 原文地址:http://www.cnblogs.com/archimedes/p/c-point-memory-leak.html,转载请注明源地址. 对于任何使用C语言的人,如果问他们C语言的 ...

随机推荐

  1. CSS 基础 例子 浮动float

    一.基本概念 设置了float属性的元素会根据属性值向左或向右浮动,设置了float属性的元素为浮动元素,浮动元素会从普通文档流中脱离,直到它的外边缘碰到包含框或另一个浮动框的边框为止. 浮动元素之后 ...

  2. .net core 与ELK(5)安装logstash

    1.下载https://www.elastic.co/downloads/logstash到/usr/local/src wget https://download.elastic.co/logsta ...

  3. ovs flow 原理及实验

    OpenFlow概述 在支持OpenFlow的交换机中包含了若干个Flow table,Flow table可以用来控制数据包的处理,交换机会执行与flow相匹配的表项中所罗列的动作. OpenFlo ...

  4. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  5. JVM锁优化

    1. 概述 JDK1.6版本花费了大量精力去实现各种锁优化,如适应性自旋,锁消除,锁粗化,轻量级锁,偏向锁等,这些技术都是为了在线程期间更高效的共享数据,以及解决竞争问题. 2. 自旋锁与自适应自旋 ...

  6. 移动一根火柴使等式成立js版本(递归)

    修改成递归版本 思路: 1.设定规则数组,比如:1加一根火柴只可以变成7. 2.设定方法数组,比如:一个数增加了一根火柴,其他的数必然减少一根火柴. 3.增加Array方法,由元素名和方法,得到规则对 ...

  7. Python staticmethod classmethod 普通方法 类变量 实例变量 cls self 概念与区别

    类变量 1.需要在一个类的各个对象间交互,即需要一个数据对象为整个类而非某个对象服务. 2.同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见. 3.有独立的存储区,属于整个类.   ...

  8. One difference between AngularJS' $location and window.location

    Recenently, I encountered a problem. Client side code is: $http({ url: "/api/runtimelicense&quo ...

  9. MySQL笔记(3)---文件

    1.前言 第二章简单记录了一下InnoDB存储引擎的一个基本内容,介绍了保证高效插入的Insert Buffer,change Buffer和确保数据安全的write ahead log以及doubl ...

  10. list、vector、deque互相拷贝

    #include <iostream> #include <stdlib.h> #include <string.h> #include <algorithm ...