理解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);
}