对于数组指针和指针数组的区别,我们听一遍往往以为自己理解了,然而课后却细思极恐。那么数组指针和指针数组到底有什么重点和难点呢?今天我们就从基础慢慢将起,里面有一些内容参考了百度。
注意:
1、字符常量 ‘A’ 占一个字节空间,字符串常量 "A"占两个字节空间 'A''\0',字符串以'\0'结束(编译器自动处理)
2、字符串赋值可用 strcpy(str,"hello");
3、同类型指针相减,得到两个对应变量的地址间隔(存储空间的距离);
4、数组指针是一个指针,该指针是一个数组的首地址。
如int(*a)[5],那么a就是一个地址,是个存放5个int型数据的数组的首地址。a+1与a差5个int型数据的长度。
5、指针数组是一个数组,该数组里面的元素都是指针,也就是地址。
如int*a[5],那么a就是一个数组,里面有5个地址,这些地址对应的内存单元里存放的是int数据.
6、例main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf(“%d,%d”,*(a+1),*(ptr-1));
}
&a是取数组首地址的地址,即让指针指向整个数组,类型为数组类型,(int*)(&a+1)将起强制转换成整型指针赋值给*ptr。
首先,什么是数组呢?
1、定义一个数组会分配连续的空间。
一维数组(同一类型数据的集合,地址空间连续(奢侈)---> 堆内存(malloc)):定义 :
数据类型 数组名[成员数] ----> 变长数组、零长度数组
char str[10];
数据类型 数组名[缺省成员数] ----> 根据数据的初始化情况确定数组长度
char str[];
访问: str[0] - str[9]; //下标访问,0开始
GNU C 扩展:
变长数组:
可以根据程序运行环境,动态定义数组
使用:
在函数体内定义,并根据指定变量的值,来定义数组的长度(在定义数组时,该长度变量的值为常量)
int len;
char Data[10];
scanf("%d",&len);
unsigned char haha[len];
printf("Data size=%d \n",sizeof(Data));
printf("haha size=%d \n",sizeof(haha));
零长度数组:
配合结构使用,实现数据空间扩展
使用方法:
struct Data{
int len;
char Str[0];
}
字符常量 ‘A’ ---> 占一个字节空间
字符串常量 "A" ---> 占两个字节空间 ---> 'A''\0' ----> 字符串以'\0'结束(编译器自动处理)
定义并初始化:
char str[10] = {0,1,2,3,4,5,6,7,8,9}; //给字符数组,使用整型进行赋值
char str[10] = {0,1}; //给字符数组,使用整型进行赋值,未完全赋值,默认0填充
char str[10] = {'A','B'}; //给字符数组,使用ASCII码进行赋值
char str[10] = "hello"; //给字符数组,使用字符串进行赋值,默认字符串最后一个字节填充'\0'
char str[] = {0,1,2,3,4,5,6,7,8,9} //给字符数组,使用整型进行赋值,长度初始化为10
char str[] = "hello"; //给字符数组,使用字符串进行赋值,默认字符串最后一个字节填充'\0',长度为6
char str[10] = {0,0,0,0,0,5,0,0,0,0}; //给字符数组,对指定元素使用整型进行赋值
char str[10] = {[5]=5,[0 ... 3]=2}; //给字符数组,对指定元素使用整型进行赋值
int data[10] = {0,1,2,3,4,5,6,7,8,9}; //给整型数组,使用整型进行赋值
先定义后初始化:
char str[10];
逐个赋值:
str[0] = 0; //给字符数组成员赋值,使用整型进行赋值
str[0] = 'A'; //给字符数组成员赋值,使用ASCII码进行赋值
循环赋值:
for(i=0;i<10;i++)
str[i] = ?;
字符串拷贝函数:
#include <string.h>
char *strcpy(char *dest, const char *src); //从源数据地址将数据拷贝到目标地址区域
目标地址 源数据地址
char str[10];
strcpy(str,"hello"); //将"hello"拷贝到str[10]数组 ,从第一个元素拷贝到'\0' 截止
char *strncpy(char *dest, const char *src, size_t n); //从源数据地址将指定长度的数据拷贝到目标地址区域
目标地址 源数据地址 数据长度
char str[10];
strncpy(str,"hello",sizeof("hello")); //将"hello"拷贝到str[10]数组 ,从第一个元素拷贝到'\0' 截止,包含'\0'
备注:
注意 strlen 与 sizeof 的长度
安全长度确定:
#define min(A,B) (sizeof(A)>sizeof(B)?sizeof(B):sizeof(A)) //获取目标地址与源地址中最短长度
char str1[10];
int size = min("hello world",str1);
strncpy(str1,"hello world",size);
printf("size: %d : %s \n",size,str1);
二维数组(同一类型数据的集合,地址空间连续(奢侈)---> 堆内存(malloc)):
定义 :
数据类型 数组名[行成员数][列成员数]
char str[10][10];
数据类型 数组名[缺省成员数][] ----> 根据数据的初始化情况确定数组长度
char str[][100];
访问: str[0][0] - str[9][9]; //下标访问,0开始
str[0][9] 一下一个单元是 str[1][0]
初始化:
定义并初始化:
str[0](第0行) str[1](第1行)
char str[10][10] = {{0,1,2,3,4,5,6,7,8,9},{0,1,2,3,4,5,6,7,8,9}}; //给字符数组,使用整型进行赋值
char str[10][10] = {{0,1},{0,1,2,3,4,5,6,7,8,9}...}; //给字符数组,使用整型进行赋值,未完全赋值,默认0填充
char str[10][10] = {{'A','B'}...}; //给字符数组,使用ASCII码进行赋值,未完全赋值,默认0填充
char str[10][10] = {"hello","world"...}; //给字符数组,使用字符串进行赋值,默认字符串最后一个字节填充'\0',未完全赋值,默认0填充
char str[][10] = {"hello","world"}; //给字符数组,使用整型进行赋值,长度初始化为2行 ---> str[2][10] ,未完全赋值,默认0填充
int data[10] = {{0,1,2,3,4,5,6,7,8,9},{0,1,2,3,4,5,6,7,8,9}...}; //给整型数组,使用整型进行赋值
先定义后初始化:
char str[10][10];
逐个赋值:
str[0][0] = 0; //确定对应的行列坐标,再给字符数组成员赋值,使用整型进行赋值
str[0][0] = 'A'; //确定对应的行列坐标,给字符数组成员赋值,使用ASCII码进行赋值
str[0] ---> 代表访问二维数组str的第0行 (行的地址:char *类型)
str[0][0] ---> 代表访问二维数组str的第0行第0列 (元素:char 类型)
循环赋值:
for(i=0;i<10;i++) //遍历每一行
for(j=0;j<10;j++) //遍历每一列
str[i][j] = ?;
提示:
内外循环效率:-----> 大循环在内,小循环在外
for(i=0;i<10;i++) for(i=0;i<100;i++)
for(j=0;j<100;j++) for(j=0;j<10;j++)
data[10][100] data[100][10]
字符串拷贝函数:
#include <string.h>
char *strcpy(char *dest, const char *src); //从源数据地址将数据拷贝到目标地址区域
目标地址 源数据地址
char str[10][10];
strcpy(str[0],"hello"); //将"hello"拷贝到数组str[0](第0行) ,从第一个元素拷贝到'\0' 截止
char *strncpy(char *dest, const char *src, size_t n); //从源数据地址将指定长度的数据拷贝到目标地址区域
目标地址 源数据地址 数据长度
char str[10][10];
strncpy(str[0],"hello",sizeof("hello")); //将"hello"拷贝到数组str[0](第0行) ,从第一个元素拷贝到'\0' 截止,包含'\0'
备注:
注意 strlen 与 sizeof 的长度
指针 ----> 装载数据地址的量 ----> 无符号整型数 ----> 地址的长度由CPU决定(32bit计算机 指针长度 32bit 4字节)
定义指针变量:
( 数据类型 * ) 变量名 ---> 用于存储相应类型的变量的地址
int * pdata;
使用:
确保左值与右值等价()
int data //data用于存放有符号的int类型的值
int *data; //data用户存放有符号的int类型的变量的地址(无符号整型数)
int data = 100; 正确
int *pdata = 100; 错误
pdata = &data; 正确,将data的地址存放到pdata变量中
& 取地址运算符
&变量名 ---> int A; (A的地址是0x12345678)
&A 表示对A进行取地址 等价于 0x12345678
* 取值转换
*指针 ---> int *p = &A;
*p 取出p存储的地址中变量A的值 等价于 A
*&A 等价A
初始化:
定义并初始化:
int data = 100;
int *pdata = &data; 正确,将data的地址存放到pdata变量中
定义后初始化:
int data = 100;
int *pdata;
pdata = &data; 正确,将data的地址存放到pdata变量中
指针访问:
基本可以实现越界读取(除非系统保护区),但不可以越界写
int data = 100; //计算机本质: 将100存放到data对应的存储空间
int *pdata;
pdata = &data;
通过指针访问data: *pdata
*pdata = 120; //将120写到pdata所指向的存储空间
char Buf[10] = "hello";
char *pBuf = &Buf[0]; //获取第一个元素的地址
char *pBuf = Buf; //数组名当地址常量使用 ---> 等价于首元素的地址(数组名位数组首地址)
*pBuf ---> Buf[0];
常见错误:
int *pdata; ----> pdata未初始化,指向的地址不确定,野指针(偶尔不出现异常,恰巧命中可用地址,但会修改该地址的数据,危险!!)
*pdata = 100; ----> Segmentation fault (core dumped) 段错误 (内存越界访问),因为地址不确定
int *plen = 0x1000; ----> 计算机将0x1000作为地址写入指针变量
*plen = 120; ----> Segmentation fault (core dumped) 段错误 (内存越界访问),因为0x1000无权访问
指针运算:
指针加减操作 -----> 实现相应单元的地址偏移(一个单位:对应的变量类型的存储单元大小 )
int A[10]; -----> &A[0] ---> 0xbffff2f0
int *pA = A;
应为是整型类型的数组指针,所以以四个字节为一个单位
pA+1 ----> 增加4个字节 ---> 0xbffff2f4
pA-1 ----> 减少4个字节 ---> 0xbffff2ecshort A[10]; -----> &A[0] ---> 0xbffff2f0
short *pA = A;
pA+1 ----> 增加2个字节 ---> 0xbffff2f2
pA-1 ----> 减少2个字节 ---> 0xbffff2fe
同类型指针相减,得到两个对应变量的地址间隔(存储空间的距离)
指针不用于相加操作(无意义)
char Buf[10] = "hello";
char *pBuf = &Buf[0]; //获取第一个元素的地址
char *pBuf = Buf; //数组名当地址常量使用 ---> 等价于首元素的地址
pBuf = Buf; //数组名当地址常量使用 ---> 等价于首元素的地址
*pBuf ---> Buf[0]; ---> *(Buf+0)
*(pBuf+1) ---> Buf[1]; ---> *(Buf+1)
pBuf[0] ---> Buf[0];
strcpy(char *dest, const char *src);
char buf[10];
strccpy(Buf,"ghjkgk");
char buf[10][10]; ----> (char buf[10])[10];
strcpy(Buf[0],"ghjkgk"); //Buf[0] 代表二维数组的第0行的数组名 (一维数组的数组名: char *) ---> &Buf[0][0]
数组指针 与 指针数组
数组指针
类型名 (* 变量名)[成员数] ----> 圆括弧不可缺少,圆括弧强调的是指针
int (*p)[10] //指向一个int型拥有10个成员的数组的地址
p+1时直接跳过10个字节。
使用:
int Data[10]; -----> Data[0] 等价于 *(Data+0)
p = &Data; -----> *&Data 等价于 Data -----> *p 等价 Data -----> Data[0] 等价于 *(*p+0)
Data[0] 等价于 *(Data+0)
指针数组
(类型名 * ) 变量名[成员数]
(int *)p[10]; 等价于 int * p[10]; //拥有10个整型指针成员的数组
使用:
int Data[10];
int *p[10];
p[0] = &Data[0];
p[0] = Data;
*(p[0]+1) ---> Data[1]
1.数组指针是一个指针,该指针是一个数组的首地址。
如int(*a)[5],那么a就是一个地址,是个存放5个int型数据的数组的首地址。a+1与a差5个int型数据的长度。
2.指针数组是一个数组,该数组里面的元素都是指针,也就是地址。
如int*a[5],那么a就是一个数组,里面有5个地址,这些地址对应的内存单元里存放的是int数据.
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf(“%d,%d”,*(a+1),*(ptr-1));
}
输出结果是什么?
2,5
--------------------------试验代码(数组指针和指针数组区别)-----------------------------
int main(int argc,char *argv[])
{
int i;
char msg[10][20] = {"good","yes"};
char *pm[10]; //指针数组,每个成员指向数组msg各行的首地址。
for(i=0;i<10;i++)
pm[i] = msg[i];
char (*pM)[20];
pM = msg;
printf("%c\n",*(pm[1]+1)); // output 'e'
printf("%c\n",*(*(pM+1)+1)); // output 'e'
return 0;
}
#include<stdio.h>
int main()
{
int Data[10];
int Data2[10];
int *p[10];
int (*a)[10];
p[0]=&Data[0];
p=&Data2[0];
int i;
char msg[10][20]={"fafa","asd"};
char *p[10];
for(i=0;i<10;i++)
pm[i]=msg[i]; //p pm[1] =asd p *(pm[1]+1)=s
char (*pm)[20];
pm=msg; // &msg[0][20] p *(pm+1)==asd p *(*(pm+1)+1)==s
}