【嵌入式开发】C语言 指针数组 多维数组(一)

1. 地址算数运算示例





指针算数运算 : int *p, array[5]; p = array; p 指向一个 int 数组元素, p + i 的地址时数组中第 i 个元素的地址, 即 p + i 指向 第 i 个元素;




存储分配示例函数 :


-- char *alloc(int n) 函数 : 传入分配的字符个数, 返回连续字符存储单元指针, 这个指针可以存储 n 个字符元素;


-- int afree(char *p) 函数 : 释放分配的内存空间;


-- 缺陷 : 分配内存的时候, 有一个偏移量, 偏移量的大小代表已经分配了多少内存, 释放内存必须按照分配内存的顺序释放, 否则偏移量就会乱;


-- 内存分配原理 : 设置一个大数组, 内存分配就分配这个数组的中的空间, alloc 和 afree 函数操作的是指针, 不是数组, 因此这个数组可以隐藏, 将数组定义为static 类型, 那么在其它文件中, 不能访问该数组, 设置一个偏移量, 当分配 n 个元素, 偏移量就加上 n, 当偏移量 等于 数组大小, 说明内存全部分配完毕;


-- 偏移量设计 : 设置一个偏移量, 偏移量始终指向大数组的下一个空闲的元素地址, 当分配内存的时候, 通过计算 数组首地址 + 数组长度 - 偏移量 >= 分配大小 , 成立的话就可以分配内存, 分配内存就是将偏移量 加上 分配大小; 释放内存的时候, 就将偏移量 指向 释放内存的指针的首地址, 因此 要保持后进先出的次序;




代码 :



/*************************************************************************
    > File Name: alloc_afree.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月17日 星期一 19时34分08秒
 ************************************************************************/
#include<stdio.h>
//用于内存分配载体的大数组大小
#define ALLOCSIZE 1000
/*
 * 该数组就是用于内存分配的主体, 
 * 设置为static , 意味着只能在本文件中访问, 在其它文件中不能访问
 */
static char alloc_buf[ALLOCSIZE];
/*
 * 将alloc_buf 数组的首地址赋值给allocp字符指针
 * 对allocp 进行算数运算, 每次加减都是 char * 运算数
 * allocp的值就可以代表所分配内存的首地址
 */
static char *allocp = alloc_buf;
/*
 * 分配n个char类型数组的内存, 
 * 如果分配成功, 返回分配的内存的指针,
 * 如果分配失败, 返回0
 */
char *alloc(int n)
{
    //如果大数组剩余的空间可以分配, 那么就进行分配
    if(alloc_buf + ALLOCSIZE - allocp >= n)
    {
  //分配空间, allocp 指向下一个空间的内存首地址
  allocp += n;
  //返回分配的空间首地址
  return allocp - n;
    }else //如果数组剩余空间不足, 返回0
    {
  return 0;
    }
}
/*
 * 释放分配的内存
 * 释放内存就是将allocp 指针地址指向 要释放的内存指针首地址
 */
void afree(char *p)
{
    //释放内存的前提是 内存必须是大于数组首地址, 小于数组尾地址
    if(p >= alloc_buf && p < alloc_buf + ALLOCSIZE)
    {
  allocp = p;
  printf("allocp = %p \n", allocp);
    }
}
int main(int argc, char **argv)
{
    char *p1;
    char *p2;
    char *p3;
    char *p4;
    //打印数组首地址
    printf("alloc_buf = %p \n", alloc_buf);
    //分配300个字符内存
    p1 = alloc(300);
    printf("char *p1 = alloc(300), p1 = %p \n", p1);
    p2 = alloc(300);
    printf("char *p2 = alloc(300), p2 = %p \n", p2);
    p3 = alloc(300);
    printf("char *p3 = alloc(300), p3 = %p \n", p3);
    //上面已经分配了900了, 在分配就溢出了, 这里alloc()函数返回0
    p4 = alloc(300);
    printf("char *p4 = alloc(300), p4 = %p \n", p4);
    afree(p4);
    afree(p3);
    afree(p2);
    afree(p1);
}



执行结果 :


/*************************************************************************
    > File Name: alloc_afree.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月17日 星期一 19时34分08秒
 ************************************************************************/
#include<stdio.h>
//用于内存分配载体的大数组大小
#define ALLOCSIZE 1000
/*
 * 该数组就是用于内存分配的主体, 
 * 设置为static , 意味着只能在本文件中访问, 在其它文件中不能访问
 */
static char alloc_buf[ALLOCSIZE];
/*
 * 将alloc_buf 数组的首地址赋值给allocp字符指针
 * 对allocp 进行算数运算, 每次加减都是 char * 运算数
 * allocp的值就可以代表所分配内存的首地址
 */
static char *allocp = alloc_buf;
/*
 * 分配n个char类型数组的内存, 
 * 如果分配成功, 返回分配的内存的指针,
 * 如果分配失败, 返回0
 */
char *alloc(int n)
{
    //如果大数组剩余的空间可以分配, 那么就进行分配
    if(alloc_buf + ALLOCSIZE - allocp >= n)
    {
  //分配空间, allocp 指向下一个空间的内存首地址
  allocp += n;
  //返回分配的空间首地址
  return allocp - n;
    }else //如果数组剩余空间不足, 返回0
    {
  return 0;
    }
}
/*
 * 释放分配的内存
 * 释放内存就是将allocp 指针地址指向 要释放的内存指针首地址
 */
void afree(char *p)
{
    //释放内存的前提是 内存必须是大于数组首地址, 小于数组尾地址
    if(p >= alloc_buf && p < alloc_buf + ALLOCSIZE)
    {
  allocp = p;
  printf("allocp = %p \n", allocp);
    }
}
int main(int argc, char **argv)
{
    char *p1;
    char *p2;
    char *p3;
    char *p4;
    //打印数组首地址
    printf("alloc_buf = %p \n", alloc_buf);
    //分配300个字符内存
    p1 = alloc(300);
    printf("char *p1 = alloc(300), p1 = %p \n", p1);
    p2 = alloc(300);
    printf("char *p2 = alloc(300), p2 = %p \n", p2);
    p3 = alloc(300);
    printf("char *p3 = alloc(300), p3 = %p \n", p3);
    //上面已经分配了900了, 在分配就溢出了, 这里alloc()函数返回0
    p4 = alloc(300);
    printf("char *p4 = alloc(300), p4 = %p \n", p4);
    afree(p4);
    afree(p3);
    afree(p2);
    afree(p1);
}


函数执行示例图 :


-- alloc()函数示例图 :

【嵌入式开发】C语言 指针数组 多维数组(一)



-- afree()函数示例图 :


【嵌入式开发】C语言 指针数组 多维数组(一)






指针初始化 : static char *allocp = alloc_buf, 将char数组的首地址赋值给char类型指针;


-- 初始化内容 : 0 或者 地址;


-- 地址限定 : 对指针初始化的地址, 该地址存储的数据的类型必须是该指针类型;




内存可用判断 : alloc_buf + ALLOCSIZE - allocp >= n;


-- 意义 : alloc_buf 是数组首地址, ALLOCSIZE 是数组大小, allocp是可用内存偏移量, alloc_buf + ALLOCSIZE -allocp 结果是可用的内存量, 如果可用内存大于n, 则可以赋值;


-- 如果内存不足 : 内存不足, 将0作为地址返回, C语言中设定 0 不是有效的数据地址, 0地址的数据为NULL, 返回0表示发生了异常事件;




指针整数转换特例 : 指针 和 整数 不能相互转换;


-- 通常情况 : 指针 和 整型 之间不能相互转换, 0 除外;


-- 特例 : 常量 0 可以赋值给指针, 指针 可以和 常量 0 进行比较, 这里注意是常量;


-- 0的特殊性 : NULL 可以代替 常量0, 常量0 是一个特殊值;




指针运算 :


-- 比较运算 : 两个指针都指向同一个数组中的元素, 那么两个指针之间的比较是有意义的, 指向两个不同数组元素的指针之间比较无意义;


-- 加减运算 : 指向数组元素的指针, 进行加减运算, 地址的计算按照 运算数 * 指针指向元素的大小 进行计算;




计算字符串长度示例 :


-- 代码 :



/*************************************************************************
    > File Name: strlen_pointer.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月17日 星期一 21时38分52秒
 ************************************************************************/
#include<stdio.h>
//计算字符串长度
int strlen(char *s)
{
    //指针 p 记录首地址
    char *p = s;
    //循环获取字符串最后的字符首地址
    while(*p != '\0')
  p++;
    //字符串占用的内存地址个数
    return p - s;
}
int main(int argc, char **argv)
{
    char *c = "fuck you!!";
    printf("length = %d \n", strlen(c));
    return 0;
}


-- 执行效果 :


octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc strlen_pointer.c 
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./a.out 
length = 10


指针差值类型 :


-- ptrdiff_t : 该类型定义在 stddef.h 头文件中, 表示两个指针之间的带符号的差值;


-- size_t : 该类型定义在 stdio.h 头文件中, size_t 可以作为 sizeof 返回的无符号整型;




指针运算一致性 : 指针运算会自动考虑其指向的元素的长度, p 指向一个类型元素 a, 不管 a 占多少字节, p++ 的下一个元素都指向下一个 同类型的元素;




指针之间的有效运算 : 除下面几种合法运算之外, 其它运算都是非法的, 但不会报错, 会警告;


-- 赋值 : 同类型指针之间的赋值运算;


-- 算数 : 指针与整数的加减运算;


-- 0相关 : 指针与0 的赋值运算, 比较运算;






2. 字符指针与函数示例





字符串常量 : 字符串常量是一个字符数组;


-- 字符串常量界定 : 字符数组以 '\0' 结束, 程序通过检查 NULL 字符找到字符串的结尾;


-- 长度大于1 : 字符串常量占据的存储单元 比 字符的个数 多1位, 这一位是 '\0';




常量字符串访问方式 : 通过指针进行访问, 指针指向常量字符串的第一个字符, 程序可以通过这个指针操作字符串常量;




字符串定义方式 :


-- 数组 : char array[] = "fuck"; array 存放 fuck 字符串 和 '\0', array 地址是字符串首地址;


-- 指针 : char *c = "fuck"; 将字符串的首地址赋值给指针c, 没有经过字符串复制;


-- 区别 : 数组 - array 指向的地址不能改变, 单个字符可以修改; 指针 - c 指向字符串常量, 可以被修改指向其它地址, 修改字符串内容没有意义, 这样会在创建一个字符串常量, 并将首地址赋值给指针;


【嵌入式开发】C语言 指针数组 多维数组(一)




示例代码 : 分别用数组 和 指针 用法 拷贝字符串, 字符串比较;



/*************************************************************************
    > File Name: string.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: Tue 18 Mar 2014 12:34:20 AM CST
 ************************************************************************/
#include<stdio.h>
/*
 * 字符串拷贝
 * 将 source[i] 赋值给 density[i], 每次循环 i++ 
 * 当density[i] == '\0'的时候停止循环
 */ 
void strcpy_array(char *density, char *source)
{
        int i = 0;
        while((density[i] = source[i]) != '\0')
                i++;
} 
/*
 * 字符串拷贝
 * *density++ 是对*density地址指向的值赋值, 然后地址进行自增操作
 * *source++ 是先取出source 指向的地址的值, 然后地址进行自增操作
 */
void strcpy_pointer(char *density, char *source)
{
        while(*density++ = *source++);
}
/*
 * s[i] == t[i] 前提下 s[i] = '\0'
 * 此时 返回 s[i] - t[i] 
 * 如果返回0 
 */
int strcmp_array(char *s, char *t)
{
        int i;
        for(i = 0; s[i] == t[i]; i++)
                if(s[i] == '\0')
                        return 0;
        return s[i] - t[i];
}
int strcmp_pointer(char *s, char *t)
{
        for(; *s == *t; s++, t++)
                if(*s == '\0')
                        return 0;
        return *s - *t;
}
int main(int argc, char **argv)
{
        char *source = "octopus";
        char density[10];
        printf("strcmp_array = %d \n", strcmp_array(density, source));
        printf("strcmp_pointer = %d \n", strcmp_pointer(density, source));
        strcpy_pointer(density, source);
        //打印字符串, 使用 字符串首地址 替代 %s;
        printf("source = %s \n", source);
        printf("density = %s \n", density);
}

运行结果 :


[root@ip28 pointer]# gcc string.c 
[root@ip28 pointer]# ./a.out 
strcmp_array = -239 
strcmp_pointer = -239 
source = octopus 
density = octopus



* 和 自增(减) 运算 :


-- *source++ : 上面的该表达式的意义是 执行 自增运算之前, source 指针指向的字符, 读取到该字符之后, 该指针指向的地址 +1;


-- *density++ = *source++ : source指针自增前, 现将其指向的字符 赋值给 density 之前指向的地址的字符, 然后将 source 指针 +1;


-- 入栈 : *p++ = val, 这是标准的入栈操作, 将val压入栈, 然后指针自增1, 注意, 这里最后一个栈多加了一个1, 然后出栈的时候要先减1 在取值;


-- 出栈 : val = *--p, 这是标准的出栈操作, 现将指针减一, 然后取出指针指向的数据, 因为指针总是指向首地址, 如果我们想要取出某一段的值, 先要将指针指向首地址才可以;



上一篇:【Android 应用开发】Android应用的自动更新模块(三)


下一篇:【嵌入式开发】C语言 内存分配 地址 指针 数组 参数 实例解析(一)