理解C语言(一) 数组、函数与指针

2.6 数组和指针的异同点

指针的特点:

  • 保存数据的地址,间接访问数据,首先取得指针的内容,把它当做地址,加上偏移量作为新地址提取数据
  • 通常用于动态数据结构,如malloc、free,用于指向匿名数据(指针操作匿名内存)
  • 可用下标形式访问指针,一般都是指针作为函数参数,但你要明确实际传递给函数的是一个数组

数组的特点:

  • 直接保存数据,以数组名+偏移量访问数据
  • 通常用于固定数目的元素
  • 数组作为函数参数会当做指针看待

另外从变量在内存的位置来说:

  • 数组要么在静态存储区被创建,如全局数组,要么在用户栈中被创建。
  • 数组名对应着一块内存(而非指向),其地址与容量在生命期内保持不变,只有其内容可以改变。
  • 指针可以随时指向任意类型的内存块,所以我们常用指针来操作动态分配的内存,但使用起来也容易出错

下面以字符串为例比较指针与数组的特性,程序为test.c:

#include <stdio.h>
#include <stdlib.h>

void ex1(){
    char a[]="hello";// 字符数组,a的内容可变,如a[0]='X'
    a[0]='X';
    printf("%c\n",*a);
    char *p="world"; //指针p指向常量字符串"world\0"(存储于静态存储区),内容不可以被修改
    p[0]='X'; //编译时尚不能发现错误,在运行时发现该语句企图修改常量字符串内容而导致运行错误
    printf("%s\n",p);
}

void ex2(){
    /* 数组与数组内容复制与比较 */
    char a[]="hello";
    char b[10];
    strcpy(b,a); //数组与数组的复制不能用b=a,否则产生编译错误
    if(strcmp(b,a)==0) //数组与数组内容的比较同样不能用b=a
        printf("内容相同\n");


    /* 数组与指针内容复制与比较 */
    int len=strlen(a);
    char *p=(char *)malloc(sizeof(char)*(len+1));
    strcpy(p,a); //复制不能用p=a,否则产生编译错误
    if(strcmp(p,a)==0) //数组与数组内容的比较同样不能用p=a,用p=a比较的是地址
        printf("内容相同\n");
}

int main()
{
    // ex1();
    ex2();
    return 0;
}

可以了解到常量字符串的内容是不可以被修改的,而字符数组的内容是可以被修改的;并且如果想要复制或比较数组内容,不能简单用b=a或b==a等来判断,需要使用如程序里所描述的strcpy和strcmp函数

注意也有例外, 就是把数组当做一个整体来考虑,而对数组的引用不能作为指向该数组第一个元素的指针来代替,看参见介绍中程序arr.c的执行结果:

  • 数组作为sizeof的操作数,显示要求的是整个数组的大小,但注意当数组作为函数形参时,自动退化为指针,在函数内部计算sizeof,结果只是计算指针类型的大小,这一般与机器字长有关,两者并不矛盾。通常可以在头文件定义一个宏语句:#define TABLESIZE(arr) (sizeof(arr)/sizeof(arr[0]))
  • 使用&获取字符数组的地址

3 实现动态数组

当我们想周期性地聚合一堆数据时,我们需要一个数组,并且这个长度是不确定的,可以动态增长。C++的vector便满足这个需求,那么对于C来说呢?一般来说C语言中的数组是静态数组,它的长度在编译期就确定了。如果你预先不知道数组的长度,想在程序运行的时候根据需要动态扩充数组的大小,这里可以设计一个动态数组的ADT。

它的基本思路是使用如malloc/free等内存分配函数得到一个指向一大块内存的指针,以数组的方式引用这块内存或者直接调用动态数组的接口,根据其内部的实现机制自行扩充空间,动态增长并能快速地清空数组,对数据进行排序和遍历。

3.1 动态数组的结构和接口定义

动态数组的数据结构定义如下:

/**
 * 动态数组的结构定义
 * data:  指向一块连续内存的指针;type_size: 元素类型的大小(动态执行时才能确定类型)
 * capacity: 动态数组的容量大小,最大可用空间
 * index: 动态数组的实际大小
 * int (*comp)(const void *,const void *): 元素的大小比较函数
 */
typedef struct {
	void *data; 
	int capacity;
	int index;
	int type_size;
	int (*comp)(const void *,const void *);
} array_t;

动态数组常见的接口函数设计:

/*为动态数组分配内存*/
array_t *array_alloc(int capacity,int type_size,int (*comp)(const void *,const void *));

/*为动态数组分配默认容量大小的内存*/
array_t *array_alloc_default(int type_size,int (*comp)(const void *,const void *));

/*释放动态数组的内存*/
void array_free(array_t *arr);

/*判断数组是否为空*/
bool array_empty(array_t *arr);

/*返回数组存储的元素个数*/
int array_size(array_t *arr);

/*借助函数指针遍历数组中每个元素*/
void array_foreach(array_t *arr, void (*visit)(void *elt));

/**
 * 插入一个元素,根据实际空间决定是否扩充或缩减容量
 * 默认范围内,保持不变;超过默认容量,则扩充
 */
void array_push(array_t *arr,void *elt);

/*把尾部元素拿掉*/
void *array_pop(array_t *arr);

/*成功找到pos位置上的元素,否则返回NULL*/
void *array_get(array_t *arr, int pos);

/*把位置pos上的内容设置成item对应的内容*/
void array_set(array_t *arr,void *item,int pos);

/*动态数组排序*/
void array_sort(array_t *arr);

3.2 动态数组的实现

具体代码如下:
dynarr.h : 头文件实现

#ifndef _DYNARR_H_
#define _DYNARR_H_

#include <stdbool.h>

#define DEFAULT_CAPACITY 16

/**
 * 动态数组的结构定义
 * data:  指向一块连续内存的指针;type_size: 元素类型的大小(动态执行时才能确定类型)
 * capacity: 动态数组的容量大小,最大可用空间
 * index: 动态数组的实际大小
 * int (*comp)(const void *,const void *): 元素的大小比较函数
 */
typedef struct {
	void *data; 
	int capacity;
	int index;
	int type_size;
	int (*comp)(const void *,const void *);
} array_t;

/*为动态数组分配内存*/
array_t *array_alloc(int capacity,int type_size,int (*comp)(const void *,const void *));

/*为动态数组分配默认容量大小的内存*/
array_t *array_alloc_default(int type_size,int (*comp)(const void *,const void *));

/*释放动态数组的内存*/
void array_free(array_t *arr);


bool array_empty(array_t *arr);

bool array_full(array_t *arr);

int array_size(array_t *arr);

void array_foreach(array_t *arr, void (*visit)(void *elt));

/**
 * 插入一个元素,根据实际空间决定是否扩充或缩减容量
 * 默认范围内,保持不变;超过默认容量,则扩充
 */
void array_push(array_t *arr,void *elt);

/*把尾部元素拿掉*/
void *array_pop(array_t *arr);

/*成功找到pos位置上的元素,否则返回NULL*/
void *array_get(array_t *arr, int pos);

/*把位置pos上的内容设置成item对应的内容*/
void array_set(array_t *arr,void *item,int pos);

/*动态数组排序*/
void array_sort(array_t *arr);

#endif

dynarr.c : 动态数组接口实现

#include "dynarr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*为动态数组分配内存*/
array_t *array_alloc(int capacity,int type_size,int (*comp)(const void *,const void *)){
	array_t *arr=malloc(sizeof(array_t));
	arr->data=malloc(capacity*type_size);
	arr->capacity=capacity;
	arr->index=0;
	arr->type_size=type_size;
	arr->comp=comp;
	return arr;
}

/*为动态数组分配默认容量大小的内存*/
array_t *array_alloc_default(int type_size,int (*comp)(const void *,const void *)){
	return array_alloc(DEFAULT_CAPACITY,type_size,comp);
}

/*释放动态数组的内存*/
void array_free(array_t *arr){
	free(arr->data);
	free(arr);
}

bool array_empty(array_t *arr){
	return (arr->index==0)?true:false;
}

bool array_full(array_t *arr){
	return (arr->index==arr->capacity)?true:false;
}

int array_size(array_t *arr){
	return arr->index;
}

void array_foreach(array_t *arr, void (*visit)(void *elt)){
	for(int i=0;i<arr->index;i++){
		void *elt=(char *)arr->data+i*arr->type_size;
		visit(elt);
	}
	printf("\n");
}



/****************辅助函数**********************/
/*使用字节流从src复制size个字节到dst位置上*/
void copy(void *dst,void *src,int size){
	char *buf=malloc(size);
	memcpy(buf,src,size);
	memcpy(dst,buf,size);
	free(buf);
}

/*使用字节流交换v1和v2的size个字节大小*/
void swap(void *v1,void *v2,int size){
	char *buf=malloc(size);
	memcpy(buf,v1,size);
	memcpy(v1,v2,size);
	memcpy(v2,buf,size);
	free(buf);
}

void exchange(array_t *arr,int i,int j){
	void *v1=(char *)arr->data+i*arr->type_size;
	void *v2=(char *)arr->data+j*arr->type_size;
	swap(v1,v2,arr->type_size);

}

int compare(array_t *arr,int i,int j){
	void *v1=(char *)arr->data+i*arr->type_size;
	void *v2=(char *)arr->data+j*arr->type_size;
	return (arr->comp)(v1,v2);
}


void array_qsort(array_t *arr,int left,int right){
	if(left<right) {
		int last=left,i;
		exchange(arr,left,(left+right)/2); 
		for(i=left+1;i<=right;i++){
			if(compare(arr,i,left)<0){
				exchange(arr,++last,i);
			}
		}
		exchange(arr,left,last);
		array_qsort(arr,left,last-1);
		array_qsort(arr,last+1,right);
	}
}
/*******************************************/


/**
 * 插入一个元素,根据实际空间决定是否扩充或缩减容量
 * 默认范围内,保持不变;超过容量,则扩充
 */
void array_push(array_t *arr,void *elt){
	if(array_full(arr)){
		/*实现复制函数*/
		void *new_data=malloc(arr->type_size*arr->capacity*2);
		void *src;
		void *dst;
		for(int i=0;i< arr->index;i++){
			src=(char *)arr->data + i*arr->type_size;
			dst=(char *)new_data + i*arr->type_size;
			copy(dst,src,arr->type_size);
		}
		free(arr->data);
		arr->data=new_data;
		arr->capacity *=2;

		/*使用realloc函数*/
		// arr->capacity *=2;
		// arr->data=realloc(arr->data,arr->type_size*arr->capacity);

	}
	void *new_elt=(char *)arr->data + arr->index * arr->type_size;
	copy(new_elt,elt,arr->type_size);
	arr->index++;
}


/*把尾部元素拿掉*/
void *array_pop(array_t *arr){
	--arr->index;
	void *elt=(char *)arr->data+arr->index*arr->type_size;
	return elt;
}

/**
 * 成功找到pos位置上的元素,否则返回NULL
 * 注意: 如果查找函数,需要定义元素大小的比较函数int (*comp)(const void *,const void *)
 */
void *array_get(array_t *arr, int pos){
	if(pos<0||pos>=arr->index){
		printf("Invalid position\n");
		return NULL;
	}
	void *elt=(char *)arr->data+pos*arr->type_size;
	return elt;
}

/*把位置pos上的内容设置成item对应的内容*/
void array_set(array_t *arr,void *elt,int pos){
	if(pos<0||pos>=arr->index){
		printf("Invalid position\n");
		return ;
	}
	void *new_elt=(char *)arr->data+pos*arr->type_size;
	copy(new_elt,elt,arr->type_size);
}


/*动态数组排序*/
void array_sort(array_t *arr){
	array_qsort(arr,0,arr->index-1);
}



posted @ 2015-03-06 23:25  charlesxiong  阅读(5811)  评论(1编辑  收藏  举报