地址和指针的概念
内存区的每一个字节都有一个编号,这就是"地址"
在C语言中,对变量的访问有两种方式,直接访问和间接访问
直接访问: a=5;(系统在编译时,已经对变量分配了地址,例如,若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元)
间接访问:scanf("%d",&a);(调用函数时,调用函数时,把变量a的地址传递给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到变量a中)
指针:在C语言中,指针是一种特殊的变量,它是存放地址的.
*:取值操作符
&:取址操作符
专门用来存放变量地址的变量称为指针变量
指针是所需变量的地址
指针变量前面的"*",表示该变量的类型为指针型变量,其一般形式为:类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的变量名,类型说明符表示本指针变量所指向的变量的数据类型
例如:float *pointer_1;
指针变量与原变量类型一定要一致
例子1
/*通过指针变量访问整型变量*/
#include <stdio.h>
void main()
{
int a, b;
int *pointer_1, *pointer_2;
a = 100, b = 10;
pointer_1 = &a;
pointer_2 = &b;
printf("%d,%d\n",a,b);
printf("%d,%d\n",*pointer_1,*pointer_2);
}
*&a == a;
(*pointer_1)++ == a++;(最好加括号,因++和*为同一优先级别,结合方向自右向左)
例子2
/*输入a和b两个整数,并按先大后小的顺序输出a和b*/
#include <stdio.h>
void main()
{
void swap(int *p1, int *p2);
int a, b;
int *pointer_1, *pointer_2;
printf("input a & b:");
scanf("%d%d",&a,&b);
pointer_1 = &a;
pointer_2 = &b;
if(a<b)
{
swap(pointer_1, pointer_2);
}
printf("%d > %d\n",a,b);
}
void swap(int *p1, int *p2) //使用指针,无需返回值即可完成a与b的互换
{
int p; /*注意此处不能设为*p,因*p声明后后续语句并没有取地址,再使用*p时因p指针无地址造成运行错误*/
printf("please wait......\n");
p = *p1;
*p1 = *p2;
*p2 = p;
}
例子3
/*输入a,b,c三个整数,并按先大后小的顺序输出*/
#include <stdio.h>
void main()
{
void exchange(int *q1, int *q2, int *q3);
int a, b, c;
int *pointer_1, *pointer_2, *pointer_3;
printf("input a & b & c:");
scanf("%d %d %d",&a,&b,&c);
pointer_1 = &a;
pointer_2 = &b;
pointer_3 = &c;
exchange(pointer_1, pointer_2, pointer_3);
printf("%d > %d > %d\n",a,b,c);
}
void exchange(int *q1, int *q2, int *q3) //定义调换函数
{
void swap(int *p1, int *p2);
if(*q1 < *q2)
{
swap(q1, q2); //注意,此处不可用*q1, *q2,因swap函数用的形参还是指针
}
if(*q1 < *q3)
{
swap(q1, q3);
}
if(*q2 < *q3)
{
swap(q2, q3);
}
}
void swap(int *p1, int *p2) //使用指针,无需返回值即可完成a与b的互换
{
int p;
p = *p1;
*p1 = *p2;
*p2 = p;
}
数组与指针
一个数组包含若干个元素,每个数组都在内存中占用存储单元,他们都有相应的地址
指针变量既然可以指向变量,当然也可以指向数组元素
数组名即翻译成数组的第一个元素的地址
即指针p == &a[0];
例子4
/*有一个10个元素的整型数组,要求用三种方法输出各元素的值*/
#include <stdio.h>
void main()
{
int a[10] = {9,8,7,6,5,4,3,2,1,0};
int i;
int *p;
p = &a;
for(i=0 ; i<10 ; i++)
{
printf("a[%d] = %d ",i,a[i]);
}
printf("\n");
for(i=0 ; i<10 ; i++)
{
printf("a[%d] = %d ",i,*(a+i));
}
printf("\n");
for(i=0 ; i<10 ; i++)
{
printf("a[%d] = %d ",i,*(p+i));
}
printf("\n");
}
fact(int arr[], int n) 和 fact(int *arr, int n)完全等价
例子5
/*将数组a中的n个整数按相反顺序存放,使用两个函数*/
#include <stdio.h>
void main()
{
int a[10];
int i;
int *p;
void contrast(int *q);
void print(int *p, int n);
for(i=0 ; i<10 ; i++)
{
scanf("%d", &a[i]);
}
p = &a;
printf("原数组:\n");
print(p, 10);
contrast(p);
}
void contrast(int *q)
{
int b[10];
int i, t;
int *r;
for(i=9 ; i>=0 ; i--)
{
b[i] = *q;
q++;
}
r = &b;
printf("改变顺序后的数组:\n");
print(r, 10);
}
void print(int *p, int n)
{
int i;
for(i=0 ; i<n ; i++)
{
printf("%d ",*(p+i));
}
printf("\n");
}
例子5改进
/*将数组a中的n个整数按相反顺序存放,使用两个函数*/
#include <stdio.h>
void main()
{
int a[10];
int i;
int *p, *r;
void contrast(int *q, int n);
void print(int *p, int n);
for(i=0 ; i<10 ; i++)
{
scanf("%d", &a[i]);
}
p = &a;
printf("原数组:\n");
print(p, 10);
contrast(p, 10);
printf("改变顺序后的数组:\n");
print(p, 10);
}
void contrast(int *q, int n) /*形参q为指针变量*/
{
int *p, temp, *i, *j, m;
m = (n-1)/2;
i = q; //指向数组的第一个元素
j = q+n-1; //指向数组的最后一个元素
p = q+m; //指向数组的中间,忽略此步将会使得下面的for循环无法进行
for( ; i<=p ; i++, j--)
{
temp = *i;
*i = *j;
*j = temp;
}
}
void print(int *p, int n)
{
int i;
for(i=0 ; i<n ; i++)
{
printf("%d ",*(p+i));
}
printf("\n");
}
例子6
/*又有一个包含10个元素的一维数组a[10],求数组内最大的元素值和最小的元素值,并标明元素序号*/
#include <stdio.h>
void main()
{
int a[10];
int i;
int *p;
void max_min(int *arr, int n);
printf("请输入10个数:");
for(i=0 ; i<10 ; i++)
{
scanf("%d", &a[i]);
}
//max_min(a, 10); //此处不能将直接引用a,程序无法识别
p = &a;
max_min(p, 10);
}
void max_min(int *arr, int n)
{
int *max, *min, *j, *m;
max = min = arr;
j = arr + 1;
m = arr + n;
for( ; j<m ; j++)
{
if(*j > *max)
{
//*max = *j;
max = j;
}
if(*j < *min)
{
//*min = *j;
min = j;
}
}
printf("最大值为%d,最小值为%d\n",*max, *min);
//printf("第%d个元素为最大值,值为%d\n",max,*max); /*max和min为最大值和最小值所在元素的地址,地址不能代表序号*/
//printf("第%d个元素为最小值,值为%d\n",min,*min);
}
如果有一个数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下四种情况:
1.形参和实参都用数组名;
2.实参采用数组名,形参采用指针变量
3.实参形参都采用指针变量
4.实参为指针变量,形参为数组名
多维数组:前一维度的数组存储的是后一维度数组的地址,最后一个维度的数组存储数据;
例子7
/*通过输入指定行数和列数打印出二维数组对应任一行任一列元素的值*/
#include <stdio.h>
void main()
{
int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23};
//int row, line, i, j;
int i, j;
int (*p)[4];
p = a;
printf("input row(0~2):");
scanf("%d", &i);
for( ; i<0 || i>2 ; )
{
printf("input row(0~2):");
scanf("%d", &i);
}
printf("input line(0~3):");
scanf("%d", &j);
for( ; j<0 || j>3 ; )
{
printf("input line(0~3):");
scanf("%d", &j);
}
/*
for(i=0 ; i<3 ; i++)
{
for(j=0 ; j<4 ; j++)
{
if(row == i && line == j)
{
printf("a[%d][%d] = %d\n", i, j, a[i][j]);
break;
}
}
}
*/
printf("a[%d][%d] = %d\n", i, j, *(*(p+i)+j));
}
字符串和指针
例子8
/*定义一个字符数组,对它初始化,然后输出该字符串*/
/*
#include <stdio.h>
void main()
{
char string[] = " 树有新叶,人有新生";
printf("%s\n",string);
}
*/
#include <stdio.h>
void main()
{
char *string = " 树有新叶,人有新生";
printf("%s\n",string);
}
对字符串中字符的存取,可以用下标方法,也可以用指针方法
例子9
/*使用下标法存取字符串中的字符*/
#include <stdio.h>
void pointer(char x[], char y[]);
void main()
{
//char a[] = "树有新叶,人有新生";
//当字符串中为中文字符时,输出全为问号,原因:编码格式不是utf-8
char a[] = "The tree has new leaves, and people have new life.";
char b[60];
#if(0)
int i;
for(i=0 ; *(a+i) != '\0'; i++)
{
*(b+i) = *(a+i);
}
*(b+i) = '\0';
printf("string a is %s\n", a);
printf("string b is %s\n", b);
#endif
pointer(a, b);
}
/*使用指针方法存取字符串中的字符*/
void pointer(char x[], char y[])
{
char *p1, *p2;
int i;
p1 = x;
p2 = y;
for( ; *p1 != '\0' ; p1++, p2++)
{
*p2 = *p1;
}
*p2 = '\0';
printf("string a is :%s\n", x);
printf("string b is :");
for(i=0 ; y[i] != '\0' ; i++)
{
printf("%c", y[i]);
}
printf("\n");
}
例子10
/*用函数调用实现字符串的复制*/
#include <stdio.h>
void copy_arr(char a[], char b[]); //用字符数组作参数
void copy_pointer(char *a, char *b); //形参用字符指针变量
void main()
{
char a[] = "The tree has new leaves, and people have new life.";
char b[60];
copy_arr(a, b);
printf("string a is %s\n", a);
printf("string b is %s\n", b);
copy_pointer(a, b);
printf("string a is %s\n", a);
printf("string b is %s\n", b);
}
void copy_arr(char a[], char b[])
{
int i =0;
while(a[i] != '\0')
{
b[i] = a[i];
i++;
}
b[i] = '\0';
}
void copy_pointer(char *a, char *b)
{
char *p1, *p2;
int i;
p1 = a;
p2 = b;
for(i=0 ; *p1 != '\0' ; p1++, p2++)
{
*p2 = *p1;
}
*p2 = '\0';
}
将字符串a复制到字符串b(使用指针变量作函数形参):
char *a = "I am a teacher.";
char b[] = "I am a student.";
这里数组b不能用*b,因b需要被重新赋值,如果采用*b,b存储在常量存储区(五大存储区),不可以被替换
当使用指针变量指向一个字符串时,可以使用数组下标的形式表示字符串中相应位置的字符
指向函数的指针
一个函数在编译时被分配给一个入口地址,这个函数的入口地址就称为函数的指针
*****************************
int max(int, int);
int (*p)(); //指向函数的指针
p = max;
*****************************
void sub(int (*x1)(int), int(*x2)(int)); //用指向函数的指针作函数参数,这里(*x1)(int)和(*x2)(int)都是一个函数
例子11
/*设一个函数process,在调用它的时候,每次是实现不同的功能(惊呆了。。。)
如:输入两个数,第一次调用输出较大的,第二次调用输出较小的,第三次调用输出两者之和*/
#include <stdio.h>
void main()
{
int max(int x, int y);
int min(int x, int y);
int add(int x, int y);
void process(int x, int y, int(*fun)());
int a, b;
printf("input number a&b:");
scanf("%d %d", &a, &b);
printf("max = ");
process(a, b, max);
printf("min = ");
process(a, b, min);
printf("add = ");
process(a, b, add);
}
void process(int x, int y, int(*fun)())
{
int (*p1)(), (*p2)(), (*p3)();
int s;
p1 = max;
p2 = min;
p3 = add;
if(fun == max)
s = max(x, y);
else if(fun == min)
s = min(x, y);
else
s = add(x, y);
printf("%d\n", s);
}
int max(int x,int y)
{
if(x > y)
return x;
else
return y;
}
int min(int x,int y)
{
if(x < y)
return x;
else
return y;
}
int add(int x,int y)
{
int c;
c = x + y;
return c;
}
返回指针值的函数
类型名 *函数名(参数表列);
如:int *a(int x, int y);
例子12
/*有若干个学生的成绩(每个学生有四门课程)
1.要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现
2.查找不及格的学生并输出该学生序号。用指针函数来实现*/
#include <stdio.h>
void main()
{
int a[][4] = {{59,78,89,90},{98,87,76,65},{80,85,90,95}};
int *score(int arr[][4], int x);
int *serach(int arr[][4], int y);
int i, j, s;
int *p;
i = 3;
p = serach(a, i);
s = (p - a)/4 + 1; //不及格的学生序号
printf("第%d位学生不及格!\n", s);
printf("第%d位学生的成绩为:", s);
for(j=0 ; j<4 ; j++)
{
printf("%3d", *(p+j));
}
printf("\n");
#if(0)
printf("请输入学生序号:");
scanf("%d", &i);
i = i-1;
p = score(a, i);
printf("第%d位学生的成绩为:", i+1);
for(j=0 ; j<4 ; j++)
{
printf("%3d", *(p+j));
}
printf("\n");
#endif
}
int *score(int arr[][4], int x) //查找学生序号
{
int *pt;
pt = *(arr + x);
return pt;
}
int *serach(int arr[][4], int y) //找出不及格的学生
{
int *pt;
int i, j;
for(i=0 ; i<y ; i++)
{
for(j=0 ; j<4 ; j++)
{
if( *(*(arr+i)+j) < 60)
pt = arr + i;
}
}
return pt;
}
指针函数和函数指针的区别
指针函数是带指针的函数,本质为函数
函数指针是指向函数的指针变量,本质为变量
指针数组和指向指针的指针
指针数组:一个数组,若其元素均为指针类型数据,称为指针数组,即每一个元素均为一个指针变量
一维指针数组定义形式:类型名 数组名[数组长度]; 如int *name[4];
例子13
/*将字符串按字母顺序(由小到大)输出*/
#include <stdio.h>
#include <string.h>
void main()
{
void sort(char *a[], int n);
void print(char *a[], int n);
char *name[] = {"Fishc.com", "www.fichc.com", "home.fishc.com", "Thank you!"};
int x = 4;
print(name, x);
sort(name, x);
print(name, x);
}
void sort(char *a[], int n)
{
char *temp;
int i, j, k;
for(i=0 ; i<n-1 ; i++)
{
for(j=i+1 ; j<n ; j++)
{
if( strcmp(a[i], a[j]) > 0)
/*strcmp为字符串大小比较库函数,在string.h头文件中*/
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
void print(char *a[], int n)
{
int k;
for(k=0 ; k<n ; k++)
{
printf("%s\n", a[k]);
}
printf("\n\n");
}
指向指针的指针:如 char **p;
指针数组作main函数的形参:如:void main(int argc, char *argv[])
void类型:1.对函数返回的限定;2.对函数参数的限定。
void指针:不指向一个确定的类型数据,仅仅用来存放一个地址(可以用任何类型的指针直接给void指针赋值,反过来则需要进行强制转换)
const指针:锁死后面的变量,其数据不可被改变
memcpy:内存拷贝函数