C语言数值和指针(重点数组指针和指针数组)

         对于数组指针和指针数组的区别,我们听一遍往往以为自己理解了,然而课后却细思极恐。那么数组指针和指针数组到底有什么重点和难点呢?今天我们就从基础慢慢将起,里面有一些内容参考了百度。


注意:

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个字节   --->  0xbffff2ec




short 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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值