一.
变量存储空间的分类顺序
先声明的变量后分配存储空间;撤销的顺序与之相反:先建立的后撤销。这种机制就称为栈机制c;好像往一个只能允许进出一个盘子的桶里放盘子c;先放进的后拿出。在Cclass="tags" href="/tags/YuYan.html" title=语言>语言程序中c;局部变量就被分配在栈区c;而且是以高端为栈底建立的。所以后建变量的地址较小。
每个变量只有一个地址c;但占用的空间不同
二.
一个指针所指连变量的类型c;称为该指针的基类型。
三.
指向void类型的指针void *p;这表示指针变量不指向某一确定类型的数据。它的作用仅仅是用来存放一个地址c;而不能指向非void类型的变量。例如c;下面的写法是错误的。
int *p1;
void *p2;
int i;
p2 = &i;
printf("%d", *p2);
如果确实需要将&i的值放在p2中c;应先进行强制类型转换c;使之成为(void *)类型c;再将p2赋值给p1时c;同样应进行强制类型转换。
p2 = (void *)&i;
p1 = (int *)p2;
printf("%d", *p1);
注意:执行p1=(int *)p2之后c;p2本身的类型并未改变。只是在向p1赋值前先进行强制类型转换c;生成一个(int *)类型的临时数据赋值给p1
四.
#include<stdio.h>
int main()
{
int a[5] = {1, 3, 5, 7, 9}, i, *p;
printf("下标法:");
for(i = 0; i < 5; ++i)
printf("%d,", a[i]);
printf("\n数组名法:");
for(i = 0; i < 5; ++i)
printf("%d,", *(a + i));
printf("\n指针变量法:");
for(p = a; p < a + 5; ++p)
printf("%d,", *p);
printf("\n");
return 0;
}
虽然上面三种方法都能输出同样的结果c;但它们的执行效率是不同的。用下标法访问数组元素时c;是把a[i]转换成*(a+i)进行计算的c;即先计算出数组元素的地址(a+i)c;然后再找到它所指的存储单元c;读出或写入它的值。而用指针变量p指向数组元素时则不必每次都计算数组元素地址。特别是使用p++这样的操作是速度比较快的。
五.
char str[M][N];
char *str[N];
char **str;
1.char str[M][N],在这个声明中c;有两个相同的数组类型说明符[]c;按照从左到右的结核性c;额可以首先确定str是一个大小为M的向量(一维数组)。对于数组c;自然要说明其类型c;由char和[N]补充说明c;这个数组是长度为N的字符数组类型c;即它的每个元素都是长度为N的字符数组。用这种方式存储的几个字符串占有连续的存储空间。
2.char *str[N],在这个声明中c;有两个不同的数组类型生命符[]和*。其中数组类型生命符的优先级别高c;可首先确定str是一个大小为N的一维数组。余下的char和*补充说明:这个数组的每个元素都是字符类型指针。采用这种方式存储的几个字符串的长度可以不同c;不一定占有连续的存储空间。
3.char **strc;在这个声明中c;有两个相同的类型生命符*。按照自右向左的结合性c;可以首先将后面的一个*与名字相结合c;得出结论:str是一个指针。对于指针c;就要声明它指向什么。由余下的char和*补充声明c;这个指针是指向字符指针的。
用二维字符数组存储c;需要按照最长字符串开辟存储空间c;而用字符串数组(即字符指针数组)存储时c;可以为不同长度的字符串开辟不同长度的存储空间。采用字符串数组来存储字符串不进可以节省空间c;还可以提高执行效率。例如c;对存储的字符串进行交换c;如果是二维数组存储c;必须实际交换所存储的字符串c;而采用字符指针数组则只需要交换两指针变量的值c;不需要实际移动字符串。
六.P212页
全局变量是在编译时在内存静态存储区分配的c;非静态的局部变量是程序运行是在栈区自动分配的c;而为指针所进行的内存空间动态分配是在程序运行过程中在自由内存区——堆(heap)区分配的。堆可以形成比较大的存储空间c;供动态分配使用。
动态分配的特点是c;可以由程序员控制c;在需要时分配c;在不需要时释放c;还可以根据具体需要改变所分配存储空间的大小。
以下四个函数包含在stdlib.h中。
void * malloc(unsigned int size)
其作用是在内存的动态存储区中分配一个长度为size的连续空间c;函数的返回值是所分配区域的第一个字节的地址。
void * calloc(unsigned n,unsigned size)
其作用是在内存的动态存储区中分配n个长度为size的连续空间c;一般用来保存一个数组(n为数组元素的个数c;每个数组元素长度为sizec;这就是动态数组)c;函数返回指向所分配域的起始位置的指针。如果分配不成功c;返回NULL。
3、free函数c;其原型为:
void free(void * pointer)
其作用是释放指针变量p所指向的动态空间c;使这部分空间能重新被其他变量使用。pointer应是最近一次调用calloc或malloc函数所得到的函数返回值。free函数没有返回值。
void * realloc(void * pointer,unsigned int size)
对已使用malloc函数或calloc函数获得的动态空间进行重新分配。如果重新分配不成功c;返回NULL
说明:
1.因为void *p说明p是void*类型的指针c;声明其类型是未确定的类型c;可以通过强制转换的方法将其转换为任何其他类型。例如
double *pd=NULL;
pd = (double *)calloc(10,sizeof(double));
并且通过(double*)对calloc()的返回类型进行强制类型转换c;以便把double类型数据的地址赋值给指针pd
2.使用sizeof的母的是用来计算一种类型所占有的字节数c;以便适合不同的编译器。
3.由于动态分配不一定成功c;为此要附加一段异常处理程序c;不致程序运行停止
if(p == NULL)
{
printf("No enough memory!\n");
exit(1);
}
七.
其实main函数也可以有参数。有参数的main函数的原型为
int main(int argc, char *argv[]);
也就是说c;带参数main函数的第一个形参argc是一个整型变量c;第二个形参argv是一个指针数组c;其每个元素都指向字符型数据(即一个字符串)。
这两个参数的值从哪里传递而来呢?main函数是主函数c;它不能被程序中的其他函数调用c;因此显然不可能从其他函数向它传递所需要的参数值c;只能从程序以外传递而来。也就是在启动一个程序时c;从程序的命令行中给出。
八.
main()
{
int a[5];
int i;
for(i = 0; i < 5; ++i, ++a)
printf("%d", *a);
}
这段程序看起来没错c;但是仔细一看c;或者一运行就会发现有错误。因为a++的使用而导致了这种情况的发生。因为a代表数组的起始地址c;是一个常量。而常量是不能进行自加运算的。所以a++是错误的。如果a只是一个函数里面的形参c;那么就可以作为一个指针变量就可以进行自加运算了。
九.
利用指针实现各种函数c;如:strcpy, strlen, stringcat, stringchr, delchar等。。
十.
一个函数包括一系列指令c;在内存中占据一片存储单元c;它有一个起始地址c;即函数的入口地址c;通过这个地址可以找到该函数。也可以定义一个指针变量c;使它的值等于函数的入口地址c;通过这个指针变量也能调用此函数c;这个指针变量称为指向函数的指针变量。例如:
int (*p)();表示p指向一个“返回正兴致的函数”。注意*p两侧的括号不能省略c;如果写成“int *p()”就变成“返回指针值的函数”了。
可以用指向函数的指针变量作为被调用函数的实参。由于该指针变量是指向某一函数的c;因此先后使指针变量指向不同的函数c;就可以在被调函数中调用不同的函数。
十一.
有人可能会认为c;实现不同的功能时直接调用不同的函数即可c;例如在main函数中直接调用几个函数即可c;何必通过一个函数借助传递函数地址的方式实现呢?的确c;如果程序功能很简单c;可以不通过这种方法。但是在一些较为复杂的问题中c;以函数地址作为参数的优越性就比较明显了。。。比如求一个函数的定积分。显然c;如果不采用这种方法c;而分别编写求解某函数定积分的函数c;将是十分麻烦的。