C语言学习笔记(四)——数组和指针

2016-12-14 09:53:40来源:http://blog.csdn.net/kittyjie/article/details/4366877作者:kittyjie人点击

int *p = NULL;
这时候我们可以通过编译器查看p 的值为0x00000000。这句代码的意思是:定义一个指针
变量p,其指向的内存里面保存的是int 类型的数据;在定义变量p 的同时把p 的值设置为
0x00000000,而不是把*p 的值设置为0x00000000。这个过程叫做初始化,是在编译的时候
进行的。


int *p;
*p = NULL;
同样,我们可以在编译器上调试这两行代码。第一行代码,定义了一个指针变量p,其指向
的内存里面保存的是int 类型的数据;但是这时候变量p 本身的值是多少不得而知,也就是
说现在变量p 保存的有可能是一个非法的地址。第二行代码,给*p 赋值为NULL,即给p
指向的内存赋值为NULL;但是由于p 指向的内存可能是非法的,所以调试的时候编译器可
能会报告一个内存访问错误。这样的话,我们可以把上面的代码改写改写,使p 指向一块合
法的内存:
int i = 10;
int *p = &i;
*p = NULL;
在编译器上调试一下,我们发现p 指向的内存由原来的10 变为0 了;而p 本身的值, 即内
存地址并没有改变。


==============================================================


int *p = (int *)0x12ff7c;
*p = 0x100;


*(int *)0x12ff7c = 0x100;


===============================================================


int a[100]


a 不能作为左值!这个错误几乎每一个学生都犯过。编译器会认为数组名作为左值代表
的意思是a 的首元素的首地址,但是这个地址开始的一块内存是一个总体,我们只能访问数
组的某个元素而无法把数组当一个总体进行访问。所以我们可以把a[i]当左值,而无法把a
当左值。其实我们完全可以把a 当一个普通的变量来看,只不过这个变量内部分为很多小块,
我们只能通过分别访问这些小块来达到访问整个变量a 的目的。


================================================================


下面到底哪个是数组指针,哪个是指针数组呢:
A),int *p1[10];
B),int (*p2)[10];
每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。
“[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *
修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个
指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比
“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,
即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指
针,它指向一个包含10 个int 类型数据的数组,即数组指针。


//以下两个VC6下会报类型转换错误


int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[3] = &a;
char (*p4)[3] = a;
return 0;
}
甚至还可以把代码再修改:
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[10] = &a;
char (*p4)[10] = a;
return 0;
}


=============================================================


struct Test
{
int Num;
char *pcName;


short sDate;
char cha[2];
short sBa[4];
}*p;
假设p 的值为0x100000。如下表表达式的值分别为多少?
p + 0x1 = 0x___ ?
(unsigned long)p + 0x1 = 0x___?
(unsigned int*)p + 0x1 = 0x___?


p + 0x1 的值为0x100000+sizof(Test)*0x1。至于此结构体的大小为20byte,前面的章
节已经详细讲解过。所以p +0x1 的值为:0x100014。
(unsigned long)p + 0x1 的值呢?这里涉及到强制转换,将指针变量p 保存的值强制转换
成无符号的长整型数。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就
是一个无符号的长整型数加上另一个整数。所以其值为:0x100001。
(unsigned int*)p + 0x1 的值呢?这里的p 被强制转换成一个指向无符号整型的指针。所
以其值为:0x100000+sizof(unsigned int)*0x1,等于0x100004。


==============================================================


int main()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}



也就是说如果是小端的话,*ptr2 的值为0x2000000。
如果是大端的话,*ptr2 的值为0x100。


==============================================================


char a[3][4];


得到a[i][j]元素的首地址为:a+ i*sizof(char)*4+ j*sizof(char)。同样,可以换
算成以指针的形式表示:*(*(a+i)+j)。


#include
int main(int argc,char * argv[])
{
int a [3][2]={(0,1),(2,3),(4,5)};
int *p;
p=a [0];
printf("%d",p[0]);
}
问打印出来的结果是多少?
很多人都觉得这太简单了,很快就能把答案告诉我:0。不过很可惜,错了。答案应该
是1。如果你也认为是0,那你实在应该好好看看这个题。花括号里面嵌套的是小括号,而
不是花括号!这里是花括号里面嵌套了逗号表达式!其实这个赋值就相当于int a [3][2]={ 1, 3,
5};


================================================================


int main()
{
int a[5][5];
int (*p)[4];
p = a;
printf("a_ptr=%#p,p_ptr=%#p/n",&a[4][2],&p[4][2]);
printf("%p,%d/n",&p[4][2] - &a[4][2],&p[4][2] - &a[4][2]);
return 0;
}


所以&a[4][2]表示的是&a[0][0]+4*5*sizeof(int) + 2*sizeof(int)。


&p[4][2]表示的是&a[0][0]+4*4*sizeof(int)+2* sizeof(int)。




==================================================================


void GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
}
intmain()
{
char *str = NULL;
GetMemory(str,10);
strcpy(str,”hello”);
free(str);//free 并没有起作用,内存泄漏
return 0;
}


在运行strcpy(str,”hello”)语句的时候发生错误。这时候观察str 的值,发现仍然为NULL。


两个办法:
第一:用return。
char * GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
return p;
}
intmain()
{


char *str = NULL;
str = GetMemory(str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
这个方法简单,容易理解。


intmain()
{
char *str = NULL;
GetMemory(&str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
注意,这里的参数是&str 而非str。这样的话传递过去的是str 的地址,是一个值。在函
数内部,用钥匙(“*”)来开锁:*(&str),其值就是str。所以malloc 分配的内存地址是真正
赋值给了str 本身。


=============================================================


A),char * (*fun1)(char * p1,char * p2);
B),char * *fun2(char * p1,char * p2);
C),char * fun3(char * p1,char * p2);


C):这很容易,fun3 是函数名,p1,p2 是参数,其类型为char *型,函数的返回值为char *
类型。
B):也很简单,与C)表达式相比,唯一不同的就是函数的返回值类型为char**,是个
二级指针。
A):fun1 是函数名吗?回忆一下前面讲解数组指针时的情形。我们说数组指针这么定
义或许更清晰:
int (*)[10] p;


#include
#include
char * fun(char * p1,char * p2)
{
int i = 0;
i = strcmp(p1,p2);
if (0 == i)
{
return p1;
}
else
{
return p2;
}
}
intmain()
{
char * (*pf)(char * p1,char * p2);
pf = &fun;
(*pf) ("aa","bb");


return 0;
}


===========================================================


void Function()
{
printf("Call Function!/n");
}
intmain()
{
void (*p)();
*(int*)&p=(int)Function;
(*p) ();
return 0;
}
这是在干什么?*(int*)&p=(int)Function;表示什么意思?
别急,先看这行代码:
void (*p)();
这行代码定义了一个指针变量p,p 指向一个函数,这个函数的参数和返回值都是void。
&p 是求指针变量p 本身的地址,这是一个32 位的二进制常数(32 位系统)。
(int*)&p 表示将地址强制转换成指向int 类型数据的指针。
(int)Function 表示将函数的入口地址强制转换成int 类型的数据。
分析到这里,相信你已经明白*(int*)&p=(int)Function;表示将函数的入口地址赋值给指
针变量p。


那么(*p) ();就是表示对函数的调用。


(*(void(*) ())0)();


这是《C Traps and Pitfalls》这本经典的书中的一个例子。没有发狂吧?下面我们就来分
析分析:
第一步:void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(void(*) ())0,这是将0 强制转换为函数指针类型,0 是一个地址,也就是说一
个函数存在首地址为0 的一段区域内。
第三步:(*(void(*) ())0),这是取0 地址开始的一段内存里面的内容,其内容就是保存
在首地址为0 的一段区域内的函数。
第四步:(*(void(*) ())0)(),这是函数调用。


=========================================================


char * (*pf[3])(char * p);
这是定义一个函数指针数组。它是一个数组,数组名为pf,数组内存储了3 个指向函数的
指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函
数。这念起来似乎有点拗口。不过不要紧,关键是你明白这是一个指针数组,是数组。


#include
#include
char * fun1(char * p)
{
printf("%s/n",p);
return p;
}
char * fun2(char * p)
{
printf("%s/n",p);
return p;


}
char * fun3(char * p)
{
printf("%s/n",p);
return p;
}
intmain()
{
char * (*pf[3])(char * p);
pf[0] = fun1; // 可以直接用函数名
pf[1] = &fun2; // 可以用函数名加上取地址符
pf[2] = &fun3;
pf[0]("fun1");
pf[1]("fun2");
pf[2]("fun3");
return 0;


}


=========================================================


char * (*(*pf)[3])(char * p);
注意,这里的pf 和上一节的pf 就完全是两码事了。上一节的pf 并非指针,而是一个数组名;
这里的pf 确实是实实在在的指针。这个指针指向一个包含了3 个元素的数组;这个数字里
面存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针、参数为一个
指向字符的指针的函数。


#include
#include
char * fun1(char * p)
{
printf("%s/n",p);
return p;
}
char * fun2(char * p)
{
printf("%s/n",p);
return p;
}
char * fun3(char * p)
{
printf("%s/n",p);


return p;
}
intmain()
{
char * (*a[3])(char * p);
char * (*(*pf)[3])(char * p);
pf = &a;
a[0] = fun1;
a[1] = &fun2;
a[2] = &fun3;
pf[0][0]("fun1");
pf[0][1]("fun2");


pf[0][2]("fun3");
return 0;
}


==========================================================


在看一个问题:


int b=3;
int c=4;
float ff=5.9;


int *p1,*p2;
p1=&b;
p2=(int*)&p1;
*p2=(int)&c; //注意这里和 p2=&c 的区别
printf("%x,%x,%x,%d,%d/n",p1,p2,&p1,*p1,*p2);


知道打印出来什么吗?


int b=3;
int c=4;
float ff=5.9;


int *p1,*p2;
p1=&b;
p2=p1;
*p2=5;
printf("%x,%x,%x,%d,%d/n",p1,p2,&p1,*p1,*p2);


知道打印出来什么吗?

解释一下:


我们声明一个指针,int *p;那么就应该了解p和&p到底什么区别?p代表的是指针p所指向的地址,而&p代表指针变量p的本身地址。


int类型可以转换成指针类型,同样指针类型也能转换成int类型,过程分别如下:


int c=3;


int *p=&c;


int b=(int)p;

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台