1 指针数组和数组指针
(1) 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32位系统下永远是占4个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称
int *p1[10];
//“[]”的优先级比“*”要高。p1先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。
//那现在我们清楚,这是一个数组,其包含10个指向int类型数据的指针,即指针数组。
int (*p2)[10];
//至于p2,在这里“()”的优先级比“[]”高,“*”号和p2构成一个指针的定义,指针变量名为p2,int修饰的是数组的内容,即数组的每个元素。
//数组在这里并没有名字,是个匿名数组。
//那现在我们清楚p2是一个指针,它指向一个包含10个int类型数据的数组,即数组指针。
(2) 地址的强制转换
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
//在x86系统下,其值为多少?
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;
}
ptr1:将&a+1的值强制转换成int*类型,赋值给int* 类型的变量ptr,ptr1肯定指到数组a的下一个int类型数据了。ptr1[-1]被解析成*(ptr1-1),即ptr1往后退4个byte。所以其值为0x4。
ptr2:按照上面的讲解,(int)a+1的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int*类型的值赋给ptr2,也就是说*ptr2的值应该为元素a[0]的第二个字节开始的
连续4个byte的内容。
//这个函数来测试当前系统的模式
int checkSystem( )
{
union check
{
int i;
char ch;
}c;
c.i = 1;
return (c.ch ==1);
}
如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1。
也就是说如果此函数的返回值为1的话,*ptr2的值为0x2000000。如果此函数的返回值为0的话,*ptr2的值为0x100
2 二维数组
内存不是表状的,而是线性的。内存的最小单位为1个byte。平时我们说内存地址为0x0000FF00也是指从内存零地址开始偏移0x0000FF00个byte。既然内存是线性的,那二维数组在内存里面肯定也是线性存储的。char a[3][4]; 实际上其内存布局如下图:
以数组下标的方式来访问其中的某个元素:a[i][j]。编译器总是将二维数组看成是一个一维数组,而一维数组的每一个元素又都是一个数组。
a[3]这个一维数组的三个元素分别为:a[0],a[1],a[2]。每个元素的大小为sizeof(a[0]),即sizof(char)*4。
由此可以计算出a[0],a[1],a[2]三个元素的首地址分别为&a[0],& a[0]+ 1*sizof(char)*4,& a[0]+ 2*sizof(char)*4。亦即a[i]的首地址为& a[0]+ i*sizof(char)*4。
这时候再考虑a[i]里面的内容。就本例而言,a[i]内有4个char类型的元素,其每个元素的首地址分别为&a[i],&a[i]+1*sizof(char),&a[i]+2*sizof(char),&a[i]+3*sizof(char),即a[i][j]的首地址为&a[i]+j*sizof(char)。
再把&a[i]的值用a表示,得到a[i][j]元素的首地址为:a+ i*sizof(char)*4+ j*sizof(char)。同样,可以换算成以指针的形式表示:*(*(a+i)+j)
#include <stdio.h>
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]); //输出1
}
//花括号里面嵌套的是小括号,而不是花括号!这里是花括号里面嵌套了逗号表达式!
//其实这个赋值就相当于int a [3][2]={ 1, 3, 5};
//在初始化二维数组的时候一定要注意,别不小心把应该用的花括号写成小括号了
3 数组参数与指针参数
无法向函数传递一个数组。
C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。在C语言中,所有非数组形式的数据实参均以传值形式(对实参做一份拷贝并传递给被调用的函数,函数不能修改作为实参的实际变量的值,而只能修改传递给它的那份拷贝)调用。然而,如果要拷贝整个数组,无论在空间上还是在时间上,其开销都是非常大的。更重要的是,在绝大部分情况下,你其实并不需要整个数组的拷贝,你只想告诉函数在那一刻对哪个特定的数组感兴趣。这样的话,为了节省时间和空间,提高程序运行的效率,于是就有了上述的规则。同样的,函数的返回值也不能是一个数组,而只能是指针。这里要明确的一个概念就是:函数本身是没有类型的,只有函数的返回值才有类型。实际传递的数组大小与函数形参指定的数组大小没有关系
main函数内的变量不是全局变量,而是局部变量,只不过它的生命周期和全局变量一样长而已。全局变量一定是定义在函数外部的
无法把指针变量本身传递给一个函数
void GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
}
int main()
{
char *str = NULL;
GetMemory(str,10);
strcpy(str,”hello”);
free(str);//free并没有起作用,内存泄漏
return 0;
}
在运行strcpy(str,”hello”)语句的时候发生错误。这时候观察str的值,发现仍然为NULL。也就是说str本身并没有改变,我们malloc的内存的地址并没有赋给str,而是赋给了_str。而这个_str是编译器自动分配和回收的,我们根本就无法使用。所以想这样获取一块内存是不行的。那怎么办? 两个办法:
//第一:用return。
char * GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
str = GetMemory(str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
//第二:用二级指针。
void GetMemory(char ** p, int num)
{
*p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
GetMemory(&str,10);
strcpy(str,”hello”);
free(str);
return 0;
}
// 注意,这里的参数是&str而非str。这样的话传递过去的是str的地址,是一个值。在函数内部,用钥匙(“*”)来开锁:*(&str),其值就是str。所以malloc分配的内存地// 址是真正赋值给了str本身。