C语言---指针
C语言中对变量的访问有两种方式:
1)直接访问,通过变量名;
2)间接访问,通过地址来访问某一个变量,该变量的地址是另一个变量的内容。
事实上,程序经过编译后,对变量的访问都会变为从地址的访问。
printf("%d \n", i);
scanf("%d \n", &i); //scanf语句使用&i来访问
一个变量的地址称为该变量的指针,地址被形象的称为指针,如果一个变量专门用来存放另一变量的地址,它被称为
"指针变量",指针变量的值---是另一个变量的地址,指针变量的地址---是本身变量的地址。
定义指针变量:
类型名 *指针变量名;
int *point_1;
int *point_2 = &b; //定义时,初始化
指针变量名前的"*"表示变量为指针变量,point_1的值表示另一变量的地址,*point_1表示另一变量的值
&point_1表示该指针变量的地址
定义指针变量时,必须制定基类型,因为访问一个变量不但需要知道首地址,还需要知道访问的字节数。
&取地址运算符; *指针运算符
指针在调用指针运算符,对另一变量赋值的时候,必须先对指针的值进行初始化,否则,指针指向的地址未知,容易发生错误。
int * temp;
*temp = *p1; //容易产生错误
当指针变量作为函数参数时,它表示将一个变量的地址传送给另一变量。
void swap(int *ptr1, int *ptr2) {
int *temp;
temp = *ptr1; //输入指针交互各自内容,temp可以定义为普通int变量
*ptr1 = *ptr2;
*ptr2 = temp;
}
void swap(int *ptr1, int *ptr2) {
int *temp;
temp = ptr1; //输入指针各自指向的地址交换
ptr1 = ptr2;
ptr2 = temp;
}
数组名作为函数的实参传递时,也是表示指针的传递。
void main () {
fun(array,10);
}
void fun(int arr[], int n) {
}
实参用指针、数组表示,形参用数组、指针表示都是等价的。
通过指针引用数组。数组本身也是一种特殊的指针:
int a[10];
int *ptr;
ptr = &a[0]; //C语言中数组a[10]的地址,可以用&a[0]或者a来访问
ptr = a; //与&a[0]等价
数组元素的引用:
下标: a[i];
指针:*(a+i)
*(p++),表示先取*p的值,然后p自加1;
*(++p),表示p先自加1,然后再取*p的值;
C语言中,字符串是存放在字符数组中的,有两种表示方法:
1) 用字符数组存放一个字符串,通过数组下标引用某个字符,%s+数组名输出整个字符串,%c+数组下标输出某个字符
char string[] = "I am a big man"
printf("%s\n",string);
printf("%c\n",string[4]); //最后一个字符存放"\0"
2) 用指针变量指向一个字符串常量
char *string = "I am a big man"
等价于 char *string; string = "I am a big man" //对字符指针的访问不需要加"*"
指向函数的指针,系统编译的时候,编译系统会为子函数代码分配一段存储空间,这段存储空间的起始地址称为这个函数的指针。
int (*p) (int,int)表示指向一个返回值为int,且有两个int 参数的函数。
所以函数的调用可以使用函数名直接调用,也可以通过函数指针来调用。
int main() {
int max(int, int); //函数声明
int (*p)(int, int); //函数指针声明
int a,b;
p = max; //使得P指向max函数
c = (*p) (a,b); //通过函数指针调用函数
}
返回指针的函数定义: 类型名 *函数名(参数列表)
float *search (float *point, int b) //返回值为float *类型的指针
指针数组:一个数组,其中的每个值都是指针变量,类型名 *数组名[数组长度]
int *p[4]; //[]优先级比*高,所以先形成p[4]形式,后加*p
int (*p)[4]; //表示指向一维数组的变量
动态分配内存空间,
void *malloc(unsigned int size); //分配大小为size字节的连续空间,赋值时,最好强制转换指针类型。
void *calloc(unsigned n, unsigned int size); //分配n个连续的size字节大小的连续空间,赋值时,最好进行强制类型转换。
void *free(void *p); //释放指针p指向的已分配的动态空间。
void *realloc(void *p, unsigned int size); //已分配过的地址,重新改变其大小。将p指向的地址,改为size大小。
将void类型的指针赋值给不同的基类型的指针变量时,不需要用户进行强制类型转换,编译系统会自动进行转换。
链表是一种常见的动态分配内存的结构。
链表由很多节点组成,每个节点,包括两部分,1)用户实际的存储数据,2)下一个节点的地址。
链表都有一个头指针,"head",只存放一个地址,指向下一个数据元素。最后一个节点的地址指向null
使用结构体来建立链表是比较合适的。
struct Student {int num;
float score;
struct Student *next;}
静态链表的建立: //静态链表,存储空间在编译时,已经是定死的
struct Student a,b,c,*head,*p
head = &a;
a.next = &b;
b.next = &c;
c.next = null;
动态链表的建立:
struct Student * creat(void) { //返回一个指向struct Student类型的指针
struct Student * head;
struct Student *p1, *p2;
n = 0;
p1 = p2 =(struct Student *) malloc(LEN);
scanf("%ld, %f", &p1->num, &p2->score); //输入第一个同学的分数
while(p1->num != 0) { //直到输入的num为0,停止
n = n+1;
if(n==1) head = p1;
else p2->next = p1; //p2中指向的是,新的内存空间的入口
p2 = p1; //p2被赋值为p1 //将p2表示为上一次的节点地址。
p1 = (struct Student *) malloc(len); //p1是每次重新分配的内存入口
scanf("%ld, %f", &p1->num, &p2->score); //输入下一个同学的分数
}
}
数组指针与指针数组,[]的优先级高于*(p);
数组指针:int (*p)[n],也称为指向一维数组的指针,
int (*p)[4];定义一个数组指针,指向含4个元素的一维数组,每次加一,是增加4个byte。
int a[3][4];
p = a;
p++,该语句执行结束之后,p=p+1,p直接指向a[1][0]
指针数组:int *p[n],本身也是一个数组,但是数组中的数据,都是指针,
赋值的时候,需要注意,*(p[n]) = num,作为指针进行赋值。
数组指针,只是一个特殊的指针,专门用来指向一个二维数组,
指针数组,是一个指针的list,多个指针,连续的存放在内存中。