- 指针的定义声明,例: int *p;,就声明了一个基类型为int的指针p,指针名为p。但是只声明了,还未给其指向。
- 声明时同时给定指向: int i; int *p = &i; ,声明的同时,让其指向int型变量i的地址。
- 又或者声明后,再给出指向: int i; int *p; p=&i; ,同样让其指向变量i的地址。
//声明变量 i ,赋值为 1 int i = 1; //声明指针变量 p ,此处的 * 不表示取值 , 而是为了区别 表示声明的是一个指针变量,不是int型变量 int *p; //使指针p指向变量i的地址 , &符号,表示取其地址 p = &i; //通过指针变量p 打印变量i的值,此处 * 表示取其指向地址的值 printf("%d", *p); //打印结果 1 //如没有 * 符号 , 表示p指向的地址 printf("%d", p); //打印结果为指针p所指向的地址: 为不确定的值
注意区别int型变量与int 型指针变量的声明与赋值:
- 对于int 型的声明: int i; 声明时同时赋值: int i = 1; 声明后再赋值: int i; i = 1;
- int i = 1; 表示 将常量1赋值给int型变量 i
- int *p = &i; 应理解为 使指针p 指向 变量i 的地址,而不是将变量i的地址赋值给指针p
指针更常用的场景之一是与数组配合使用
//声明一个数组a ,a表示的是数组的首地址,也就是数组中第一个元素的地址 int a[3] = {1,3,5}; //声明一个指针变量p , 并使其指向数组 a int *p = a; //打印数组中第一个元素 printf("%d", *p); //1 //打印数组中第二个元素,注意括号不可省 printf("%d", *(p+1)); //3 //省略则 表示打印数组中第一个元素 加上1 printf("%d", *p+1); //2 //使指针p指向下一个地址,即数组中第二个元素的地址 p=p+1; //此刻p指向的是数组中的第二个元素的地址 printf("%d", *p); //2
- 若有定义
int a[3]; int *p = a;
数组中元素的引用方式有:a[i]
,*(p+i)
,p[i]
,*(a+i)
四种 ,常用的常常为前面两个 。 但应明白四者等价。
二维数组:
//二维数组的声明 int a[2][3] = {{1,2,3},{4,5,6}};
- a 同样表示的是数组首地址,但是数组中第一个元素是一个包含3个元素的一维数组。实际是一个指向一维数组的地址的指针。
- 这时,*a并不能取到第一个元素的值了,*a 表示数组中第一个元素的地址,也就是一维数组的地址。再进行一次取值,**a 则表示取 数组中第一个元素(指 {1,2,3})中的第一个元素(指1)的值,可能已经有点绕了。
- 若有定义int *p; ,试图将指针p指向a: p=a; , 实际是不对的 , 应将p定义为指向 由3个元素组成的一维数组的 指针: int (*p)[3]; .注意此处括号不可省,不能定义为 int *p[3]; (这样表示定义一个有三个指针变量元素的指针 数组) , 也不可定义为int **p;
- 注意区别 *int (p)[3]; 与 *int p[3]; 的含义
- 虽然定义int *p; 的指针p不可直接指向a,但是可以指向a中 第一个元素 的地址p = a[0];
区别其不同的指向方式:
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; //指针 数组 , 它还是数组,只不过其中的元素是int为基类型的指针 int *p[2]; for (int i = 0; i < 2; i++) p[i] = a[i]; //表示 取第二个元素的首元素的值 printf("%d\n",*p[1]); //与a[1][2]含义相同 printf("%d\n",p[1][2]); for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) printf("%d\t", *(*(p + i) + j));
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; //指向 由3个元素组成的一维数组的 指针,但是它还是 指针 int(*p)[3]; p = a; for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++) printf("%d\t", *(*(p + i) + j)); //当i和j都为0时 printf("%d\t", **p);
指针与字符串及字符串数组
注意不同的定义方式 , 及其定义方式的区别
//正确的 char s[] = "hello"; char s1[10] = "hello"; //s2表示:字符串的首字符的地址 char *s2 = "hello"; char *s3; s3 = "hello"; //输出方式 printf("%c",*s2); //以字符型格式输出: h printf("%s", s2); //以字符串格式输出: hello puts(s2); //使用字符串函数输出函数输出: hello
//错误的 , 只能采用循环一个一个字符的赋值 char s1[10]; s1 = "hello"; s1[10] = "hello"; char *s2; *s2 = "hello";
//正确的 char s1[10]; gets(s1); //错误的 char *s2; gets(s2);
- 数组形式不能进行自加操作,即不能改变,而指针型定义的字符串可以改变
char s1[] = "hello"; s1 = s1 + 1; //不合法,数组首地址不可变 char *s2 = "hello"; s2 = s2 + 1; //合法 printf("%c",*s2); //输出: e puts(s2); //输出: ello
//试图将首字母 h 改为大写 H //不合法,字符串常量不能改变 char *s1 = "hello"; *s1 = 'H'; // 合法 char s2[10] = "hello"; s2[0] = 'H';
字符串数组 (二维数组) 的定义与遍历引用:
char *s1[] = {"Java", "Python", "C", "JavaScript"}; char s2[][10] = {"Java", "Python", "C", "JavaScript"}; for (int i = 0; i < 4; i++){ puts(s1[i]); puts(s2[i]); }
指针与函数
- int (*p)(); 声明 基类型为无参且返回类型为int型函数的指针,其基类型为:
int (*)()
- int *p(); 声明 返回类型为
int *
的函数
#include <stdio.h> void fun1(){ printf("fun1"); } int fun2(int x){ printf("fun%d", x); } int fun3(int x, int y){ printf("fun%d", x + y); } int main(){ //无参无返回值 void (*p1)() = fun1; (*p1)(); //带参与返回值 int (*p2)(int) = fun2; (*p2)(2); //多个参数 int (*p3)(int, int) = fun3; (*p3)(2, 1); return 0; }
函数指针作函数参数
#include <stdio.h> int max(int x,int y){ return (x>y?x:y); } int min(int x,int y){ return (x<y?x:y); } //以函数指针作函数形参, x、y表示形参函数指针m 的参数。x、y不必须,可省。 int fun(int x,int y,int (*m)(int,int)){ return (*m)(x,y); } int main() { //求两者 最大值 printf("%d",fun(3,9,max)); //求两者 最小值 printf("%d",fun(3,9,min)); return 0; }