指针初步:
#includeint main() { int i = 10; int *p1 = &i; printf("*p1 = %d \n",*p1); } root@ubuntu:~/pointer# gcc main.c ;./a.out *p1 = 10
#includeint main() { int i = 10; int *p1 = NULL; p1 = &i; printf("*p1 = %d \n",*p1); } root@ubuntu:~/pointer# gcc main.c ;./a.out *p1 = 10
#includeint main() { int i = 10; int *p1 = NULL; p1 = &i; *p1 = 20; printf("*p1 = %d \n",*p1); } root@ubuntu:~/pointer# gcc main.c ;./a.out *p1 = 20
野指针
#includeint main() { int *p3 ; //未初始化的指针,野指针, *p3 = 10; //gcc 编译不通过:Segmentation fault (core dumped) } root@ubuntu:~/pointer# gcc main.c Segmentation fault (core dumped)
空指针类型,强转指针
#includeint main() { int i = 10; void *p; p = &i; printf("*p = %d\n",*(int *)p); // (int *)p 把p强转为 int 类型的指针 } root@ubuntu:~/pointer# gcc main.c ;./a.out *p = 10
指针类型兼容,不兼容
#includeint main() { int *p = NULL; unsigned i ; p = &i; char c = 'a'; p = &c; } main.c:8:11: warning: assignment from incompatible pointer type [enabled by default] p = &c; ^
数组的地址
#includeint main() { int chunli[10] = {0}; printf("%p\n",chunli); printf("%p\n",&chunli); printf("%p\n",&chunli[0]); printf("%p\n",&chunli[1]); } 数组的名字就是数组的初始地址; root@ubuntu:~/pointer# gcc main.c ;./a.out 0x7fffc53060c0 0x7fffc53060c0 0x7fffc53060c0 0x7fffc53060c4
#includeint main() { int chunli[10] = {2}; int *p1 = &chunli[0]; int *p2 = chunli; printf("%d \n",*p1); printf("%d \n",*p2); } p1 ,p2 指向的是同一个地址 root@ubuntu:~/pointer# gcc main.c ;./a.out 2 2
指针与数组1
#includeint main() { int chunli[10] = {2}; int *p1 = chunli; printf("sizeof(chunli) =%ld \n",sizeof(chunli)); printf("sizeof(p1) =%ld \n",sizeof(p1)); } // 32bit系统 一个指针占用4个字节 // 64bit系统 一个指针占用8个字节 root@ubuntu:~/pointer# gcc main.c ;./a.out sizeof(chunli) =40 sizeof(p1) =8
指针与数组 面试题
#includeint main() { int chunli[10] = {1,2,3}; printf("%p,%d,%p \n",chunli,*chunli,&chunli); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out 0x7ffe2f696310,1,0x7ffe2f696310
指针与数组,指针在数组中移动
#includeint main() { int chunli[10] = {1,2,3}; int *p1 = chunli; //指向数组的首地址 //int *p2 = &chunli; gcc 会报错 printf("%p,%d\n",p1, *p1); printf("%p,%d\n",p1+1,*p1+1); printf("%p,%d\n",p1+2,*p1+2); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out 0x7fff5103f740,1 0x7fff5103f744,2 0x7fff5103f748,3
系统的小端对齐方式,由CPU决定!
int类型数据 在内存的存放
chunli@pc0003:~/C_language$ vim main.c #includeint main() { int i = 15; char *p = (char *)&i; printf("int 的第1个字节 %x\n",*(p+0)); printf("int 的第2个字节 %x\n",*(p+1)); printf("int 的第3个字节 %x\n",*(p+2)); printf("int 的第4个字节 %x\n",*(p+3)); } chunli@pc0003:~/C_language$ gcc main.c ;./a.out int 的第1个字节 f int 的第2个字节 0 int 的第3个字节 0 int 的第4个字节 0
再看看16进制的
#includeint main() { int i = 0x12345678; char *p = (char *)&i; printf("int 15 的第1个字节 %x\n",*(p+0)); printf("int 15 的第2个字节 %x\n",*(p+1)); printf("int 15 的第3个字节 %x\n",*(p+2)); printf("int 15 的第4个字节 %x\n",*(p+3)); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out int 的第1个字节 78 int 的第2个字节 56 int 的第3个字节 34 int 的第4个字节 12
指针的使用--给数组赋值 scanf
#includeint main() { int myarr[10] ={0} ; int i=0; int *p=myarr; for(i=0;i<10;i++) { scanf("%d",p++); } for(i=0;i<10;i++) { printf("arr[%d] = %d \n",i,myarr[i]); } } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out 1 2 3 4 4 5 6 7 8 8 arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 4 arr[5] = 5 arr[6] = 6 arr[7] = 7 arr[8] = 8 arr[9] = 8
使用指针输出数组
#includeint main() { int myarr[10] ={1,4,5,2,3,4,8,9} ; int i=0; int *p=myarr; for(i=0;i<10;i++) { printf("arr[%d] = %d \n",i,*p++);//*p++自右向左结合运算 } } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out arr[0] = 1 arr[1] = 4 arr[2] = 5 arr[3] = 2 arr[4] = 3 arr[5] = 4 arr[6] = 8 arr[7] = 9 arr[8] = 0 arr[9] = 0
把指针名字当数组使用
#includeint main() { int myarr[10] ={1,4,5,2,3,4,8,9} ; int i=0; int *p=myarr; for(i=0;i<10;i++) { printf("arr[%d] = %d \n",i,p[i]); //p[i]等同于*(p+i) } } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out arr[0] = 1 arr[1] = 4 arr[2] = 5 arr[3] = 2 arr[4] = 3 arr[5] = 4 arr[6] = 8 arr[7] = 9 arr[8] = 0 arr[9] = 0
*(p++) 取下一个地址值
(*p)++ 把值取出来加1
数组 指针 面试题
#includeint main() { //指针数组,10个全是指针 char *p1[10]; p1[0] = "Hello"; p1[1] = "World"; p1+1; printf("%ld \n",sizeof(p1)); //数组指针,一个指针指向了数组的首地址 char (*p2)[10][10]; printf("%ld \n",sizeof(p2)); } 64位操作系统,一个指针占8位 32位操作系统,一个指针占4位 chunli@ubuntu:~/pointer$ gcc main.c ;./a.out 80 8
二级指针、多级指针:
#includeint main() { int i = 10; int *p1 = &i; int **p2 = &p1; int ***p3 = &p2; int ****p4 = &p3; printf(" *p1 =%d \n",*p1); printf(" **p2 =%d \n",**p2); printf(" ***p3 =%d \n",***p3); printf(" ****p4 =%d \n",****p4); //**p2 = 100; // *p2 就等于&p1 ,p1的地址; // **p2 =* 代表的是pa1的地址; //printf("%d \n",**p2); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out *p1 =10 **p2 =10 ***p3 =10 ****p4 =10
指针数据交换
#includeint main() { void swap(int *a,int *b) { int i ; i = *a; *a = *b; *b = i; } int x,y; printf("Enter two int number !\n"); scanf("%d %d",&x,&y); printf("x= %d ,y =%d\n",x,y); swap(&x,&y); printf("x= %d ,y =%d\n",x,y); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out Enter two int number ! 12 67 x= 12 ,y =67 x= 67 ,y =12
面试:
不需第三变量 据交换:
#includeint main() { void swap(int *a,int *b) { *a = *a ^ *b; *b = *a ^ *b; *a = *a ^ *b; } int x,y; printf("Enter two int number !\n"); scanf("%d %d",&x,&y); printf("x= %d ,y =%d\n",x,y); swap(&x,&y); printf("x= %d ,y =%d\n",x,y); } chunli@ubuntu:~/pointer$ gcc main.c ;./a.out Enter two int number ! 12 34 x= 12 ,y =34 x= 34 ,y =12
用指针修改地址的方法,函数对数组排序,
#includevoid swap(int *a,int *b) { *a = *a ^ *b; *b = *a ^ *b; *a = *a ^ *b; } void func(int myarray[]) { for(int i = 0;i<10;i++) { for(int j=0;j<10-i-1;j++) { if(myarray[j] > myarray[j+1]) { swap(&myarray[j],&myarray[j+1]); } } } } int main() { int array[10] ={1,3,7,4,5,6,0,12,56,43} ; func(array); //把数组名作为地址传过去 for(int i=0;i<10;i++) { printf("%d \n",array[i]); } } chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out 0 1 3 4 5 6 7 12 43 56 chunli@ubuntu:~/pointer$
用函数修改数组指定位置的数据
#includeint mod(int *p) { *(p+4) = 100; } int main() { int array[10] ={1,3,7,4,5,6,0,12,56,43} ; mod(array); for(int i=0;i<10;i++) { printf("%d \n",array[i]); } printf("***********************\n"); mod(array + 2); //把第2个 for(int i=0;i<10;i++) { printf("%d \n",array[i]); } } chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out 1 3 7 4 100 6 0 12 56 43 *********************** 1 3 7 4 100 6 100 12 56 43
指针面试题
#includeint main() { printf("sizeof(char) = %ld \n",sizeof(char)); printf("sizeof(char *) = %ld \n",sizeof(char *)); printf("sizeof(int) = %ld \n",sizeof(int)); printf("sizeof(int *) = %ld \n",sizeof(int *)); printf("sizeof(long) = %ld \n",sizeof(long)); printf("sizeof(long *) = %ld \n",sizeof(long *)); char buf[10]; printf("char buf[10] = %ld \n",sizeof(buf )); char *s[10];//指针的数组 printf("char *s[10] = %ld \n",sizeof(s)); char (*c)[10];//数组的指针 printf("char (*c)[10] = %ld \n",sizeof(c)); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out sizeof(char) = 1 sizeof(char *) = 8 sizeof(int) = 4 sizeof(int *) = 8 sizeof(long) = 8 sizeof(long *) = 8 char buf[10] = 10 char *s[10] = 80 char (*c)[10] = 8
指针面试题
#includeint main() { int arr[10]={4,5,6,7,8}; //arr + =3; 这是一个错误的语法,因为数组的名称是常量 int *p = arr; printf("%d \n",p[3]); // 指针下标,返回数组第4个具体的值 printf("%d \n",*(p+3)); // 指针位置,返回数组第4个具体的值 int *s1[100]; //定义了一个数组,数组的100个元素全是int类型指针 //s1++; // 非法语句 int (*s2)[100][100];//定义了一个指针,指向了int [100][100]这样的数据空间 char *s3; s3++; //移动了1个字节 int *s4; s4++; //移动了4个字节 } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 7 7
*星号是从指针变量的值去
#includeint main() { int a = 10; int *p = &a; printf("%d\n",*p); // *p直接操作&变量的值 *p =11; printf("%d\n",*p); } chunli@Linux:~/high$ gcc 123.c && ./a.out 10 11 chunli@Linux:~/high$
#includeint main() { int *p0 = NULL; int **p1 = &p0; int ***p2 = &p1; p0 = (int *)&p2; printf("***p2 = %x \n",***p2); printf("p0 address=%p \n",&p0); printf("p1 address=%p \n",&p1); printf("p2 address=%p \n",&p2); } root@ubuntu:~/pointer# gcc main.c && ./a.out ***p2 = 73442910 p0 address=0x7fff73442908 p1 address=0x7fff73442910 p2 address=0x7fff73442918
#includeint main() { int *p1 = NULL; int **p2 = &p1; int ***p3 = &p2; int ****p4 = &p3; int *****p5 = &p4; int ******p6 = &p5; p1 = (int *)&p6; printf("******p6 = %x \n",******p6); printf("p1 address=%p \n",&p1); printf("p2 address=%p \n",&p2); printf("p3 address=%p \n",&p3); printf("p4 address=%p \n",&p4); printf("p5 address=%p \n",&p5); printf("p6 address=%p \n",&p6); } chunli@Linux:~/high$ gcc 123.c && ./a.out ******p6 = 7ee14bf0 p1 address=0x7ffe7ee14bd0 p2 address=0x7ffe7ee14bd8 p3 address=0x7ffe7ee14be0 p4 address=0x7ffe7ee14be8 p5 address=0x7ffe7ee14bf0 p6 address=0x7ffe7ee14bf8 chunli@Linux:~/high$
把数组传到函数
#include//这三种描述的是一样的 void fun(int *arr,int len) //数组通过函数形参传递时,无法传递数组的大小 //void fun(int arr[1],int len) //无法传递数组的大小,并不认为这是一个大小为 1的数组 //void fun(int arr[],int len) { for(int i=0;i
数组地址
#includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; printf(" %p\n %p\n %p\n %p\n %p\n",arr,*arr,&arr,&arr[0],&arr[0][0]); //在一维数组中,*数组名就是第一个数组的值 } root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 0x7ffd2163f9f0 0x7ffd2163f9f0 0x7ffd2163f9f0 0x7ffd2163f9f0 0x7ffd2163f9f0
二维数组与指针1 #includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; printf(" %p\n %p\n %p\n %p\n %p\n",arr,*arr,&arr,&arr[0],&arr[0][0]); //在一维数组中,*数组名就是第一个数组的值 printf(" %p \n",arr + 1); printf(" %p \n",arr[0] + 1); } arr + 1 :一次移动一排 arr[0] + 1 :一次移动一个 root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 0x7ffcbd4da550 0x7ffcbd4da550 0x7ffcbd4da550 0x7ffcbd4da550 0x7ffcbd4da550 0x7ffcbd4da560 0x7ffcbd4da554
二维数组与指针2,很难理解! #includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; int (*p)[4]; //变相二维数组的指针,可以理解为*p就是指针名,**P才是取值 p = arr; printf("%d \n",*((*p)+1)); printf("%d \n",*(*p)+1); printf("%d \n",**p+1); } root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 1 1 1
指针输出二维数组
#includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; int (*p)[4]; //变相二维数组的指针,**P才是取数组的值 p = arr; //*(*(p+0)+1) 代表int * 向后移了一个位置 //printf("%d \n",*(*(p+1)+1)); //输出第一行的第2个数值 for(int i=0;i<3;i++) { for(int j=0;j<4;j++ ) { printf("%d\t",*(*(p+i)+j)); } printf("\n"); } } root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 0 1 2 3 4 5 6 7 8 9 10 11
指针转置输出二维数组
#includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; int (*p)[4]; //变相二维数组的指针,**P才是取数组的值 p = arr; //*(*(p+0)+1) 代表int * 向后移了一个位置 //printf("%d \n",*(*(p+1)+1)); //输出第一行的第2个数值 for(int i=0;i<4;i++) { for(int j=0;j<3;j++ ) { printf("%d\t",*(*(p+j)+i)); } printf("\n"); } } root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 0 4 8 1 5 9 2 6 10 3 7 11
二维数组与指针3
#includeint main() { int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}}; int (*p)[4]; //变相二维数组的指针,**P才是取数组的值 p = arr; //*(*(p+0)+1) 代表int * 向后移了一个位置 printf("%d \n",*(*(p+1)+1)); //输出第一行的第2个数值 //*(*(p+1)+1)) : (p+1) 一次连续移动int (*p)[4] 16个字节 } root@ubuntu:~/pointer# gcc -std=c99 main.c && ./a.out 5
函数打印二维数组
#includevoid fun(int p[][4],int a,int b) { for(int i=0;i
#includeint main() { //指向常量的指针 int i = 10; const int *p1 = &i; //定义一个指针,指向一个常量,通过指针无法修改所指向变量的值 //*p =100; //这个语句无法通过编译 //通过指向常量的指针,可以限制指针修改变量的值 //虽然无法修改变量的值,但是可以乱指 //指针常量 int *const p2 = &i;//定义一个指针的常量,这个指针就只能指向这个变量,不能再指向其他的变量 int j = 20; //*p2 = &j; //这条语句不会通过,因为这个指针无法指向其他的变量 }
#includevoid fun(const int *p,int n) //不希望通过指针修改变量的值 { for(int i=0;i
函数指针的定于使用
#includevoid fun(int n) { printf("fun %d\n",n); } int main() { void (*p)(int);//定义一个指针,指向有一个int参数,返回值为void的函数指针 p = fun; //让p指向fun函数的入口地址 p(10); //执行函数 } chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out fun 10
函数指针---回调函数
#includevoid fun(int n) { printf("fun %d\n",n); } int myadd(int a,int b) { return a + b; } int mysub(int a,int b) { return a - b; } int pointer_fun(int (*p)(int ,int),int a,int b) { return p(a,b); } int main() { int i = pointer_fun(myadd,4,5); fun(i); i = pointer_fun(mysub,4,5); fun(i); } chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out fun 9 fun -1
#includeint main() { char s[] = "Hello World!"; char *p = s; p[2] = 'a'; printf("%s \n",s); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out Healo World! #include int main() { char *p = "Hello World!"; p[2] = 'a'; printf("%s \n",s); } 编译报错!
因为 char *p = "Hello World!"; 指向的是字符串常量,只读!
字符串指针
#includefun(char *p) { p[3] = 'a'; } int main() { char s[] = "Hello World!"; fun(s); printf("%s \n",s); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out Helao World! 自己实现strcopy功能; #include void fun(char *p1,const char *p2) { int i=0; while(p2[i] != '\0'){ p1[i] = p2[i]; i++; } } int main() { char buf[100] = {0}; fun(buf,"Hello World!"); printf("%s \n",buf); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out Hello World!
代码优化:
#includevoid fun(char *p1,const char *p2) { while(*p1++ = *p2++); //当*p2 == 0的时候就停止了 } int main() { char buf[100] = "AAAAAAAAAAAAAAAAAAAAAAA"; fun(buf,"Hello World!"); printf("%s \n",buf); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out Hello World!
strncpy的自定义版本:
#includevoid fun(char *p1,const char *p2,int n) { while(n--) { *p1++ = *p2++; } *(p1) = '\0'; //*(p1-1) = '\0'; } int main() { char buf[100] = "AAAAAAAAAAAAAAAAAAAAAAA"; fun(buf,"Hello World!",3); printf("%s \n",buf); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out Hel
字符编码长度
#include#include int main() { char *p = "UTF8编码"; printf("%ld \n",strlen(p)); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 10
计算UTF编码编码的字符串长度
#includeint mystrlen(const char *p) { int num = 0; while(*p) { num++; if(*p++<0) { p++; p++; } } return num; } int main() { char *p = "UTF8编码"; printf("%d \n",mystrlen(p)); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 6
字符串指针,实现strcmp
#includeint mystrcmp(const char *p1, const char *p2) { int result = 1; while( *p1 && *p2 ) { while(*p1++ != *p2++) { result = 0; } } return result; } int main() { char c1[] = "UTF8编码"; char c2[] = "GBK编码"; printf("%d \n",mystrcmp(c1, c2)); } 有一个bug,当两个字符串前面完全一样时,后面的细微变化导致bug chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 0
指针实现字符串分割
#includechar *mystrtok(char *s, int n) { while(1) { if (n == 1) return s; if (*s++ == ' ') n--; } } int main() { char c[] = "UTF8 编 123码"; printf("%s \n",mystrtok(c,2)); printf("%s \n",c); } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 编 123码 UTF8 编 123码
数字转字符,支持负数
#includeint caluintlen(unsigned int n) { int result = 1; while (n /= 10) { result++; } return result; } int calnumlen(int i) { int result = i < 0 ? 1 : 0; result += caluintlen(i < 0 ? -i : i); return result; } void myitoa(int i, char *s) { int count = calnumlen(i); if (i<0) { *s = '-'; i = -i; } s += count;//指向了要转化后的字符串的最后 *s = 0;//字符串结尾赋值0, while (i > 0) { *--s = (i % 10) + 0x30; i /= 10; } } int main() { char buf[100] = { 0 }; myitoa(23412, buf); printf("%s \n",buf); return 0; } chunli@ubuntu:~/pointer$ gcc main.c && ./a.out 23412
main函数的参数
#includeint main(int argc,char **args) //二级指针 //int main(int argc,char *args[]) //等效上 { for(int i = 0; i< argc; i++) { //printf("%s \n",args[i]); //等效下 printf("%s \n",*args++); } return 0; } chunli@ubuntu:~/pointer$ chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out 123 "Hello World!" 您好 ./a.out 123 Hello World! 您好
返回字符串
#includeconst char *chunli_getchar() { const char *s = "abcd"; return s; } /*函数放在代码区 */ int main() { printf("%s \n",chunli_getchar()); } chunli@ubuntu:~/pointer$ gcc -std=c99 main.c && ./a.out abcd
1.1 指针
1.1.1 指针的概念
指针也是一个变量,做为指针变量的值是另一个变量的地址。
指针存放的内容是一个地址,该地址指向一块内存空间
1.1.2 指针变量的定义
可以定义一个指向一个变量的指针变量。
int *p;//表示定义一个指针变量。
*p;//代表指针所指内存的实际数据
切记,指针变量只能存放地址,不能将一个int型变量直接赋值给一个指针。
int *p = 100;
1.1.3 NULL
一个指向NULL的指针,我们称之为空指针,意味着这个指针不指向任何一个变量。
野指针
1.1.4 &取地址运算符
1.1.5 无类型指针
定义一个指针变量,但不指定它指向具体哪种数据类型。可以通过强制转化将void *转化为其他类型指针,也可以用(void*)将其他类型指针强制转化为void类型指针。
void *p
在C语言当中,可以将任何一种地址赋值给void *指针
1.1.6 指针的兼容性
指针之间赋值比普通数据类型赋值检查更为严格,例如:不可以把一个double *赋值给int *
1.1.7 指针与数组的关系
一个变量有地址,一个数组包含若干个元素,每个元素在内存中都有地址。
int a[10];
int *p = a;
比较p和&a[0]的地址是否相同
在C语言当中数组的名称代表数组的首地址,如果取数组名称的地址,C语言认为就是取数组的首地址。
1.1.8 通过指针使用数组元素
通过指针计算,不是把指针当做一个整数,计算结果,而是指针在内存当中移动
p + 1代表&a[1],也可以直接使用p[1]表示a[5]
p + 5 代表&a[5]
p++
在C语言里面数组名称是个常量,值是不可改变的
1.1.9 指针数组
int *p[5];
1.1.10 数组指针
int (*P)[5];
1.1.11 指向指针的指针(二级指针)
指针就是一个变量,既然是变量就也存在内存地址,所以可以定义一个指向指针的指针。
inti=10;
int*p1=&i;
int**p2=&p1;
printf("%d\n",**p2);
以此类推可以定义3级甚至多级指针。
1.1.12 指针变量做为函数的参数
函数的参数可以是指针类型。,它的作用是将一个变量的地址传送给另一个函数。
1.1.13 一维数组名作为函数参数
当数组名作为函数参数时,C语言将数组名解释为指针
当数组名作为函数参数传递给被调用函数时,被调用函数是不知道数组有多少元素的
int func(int array[10]);
相当于传递是一个地址,那么就可以通过地址来修改实参的值。
只要传递是数组名,那么形参一定可以通过地址修改实参的值。
1.1.14 二维数组名作为函数参数
二维数组做函数参数时可以不指定第一个下标。
int func(int array[][10]);
1.1.15 指向二维数组的指针
int a[3][5]
a
二维数组名称,数组首地址
a[0], *(a + 0), *a
0行,0列元素地址
a + 1
第1行首地址
a[1], *(a + 1)
第1行,0列元素地址
a[1] + 2, *(a + 1) + 2, &a[1][2]
第1行,2列元素地址
*(a[1] + 2), *(*(a + 1) + 2), a[1][2]
第1行,2列元素的值
1.1.16 指向常量的指针与指针常量
const char *p;//定义一个指向常量的指针
char *const p;//定义一个指针常量,一旦初始化之后其内容不可改变
1.1.17 const关键字保护数组内容
如果将一个数组做为函数的形参传递,那么数组内容可以在被调用函数内部修改,有时候不希望这样的事情发生,所以要对形参采用const参数
func(const int array[])
1.1.18 指针做为函数的返回值
char *func();//返回值为char *类型的函数
1.1.19 指向函数的指针
指针可以指向变量,数组,也可以指向一个函数。
一个函数在编译的时候会分配一个入口地址,这个入口地址就是函数的指针,函数名称就代表函数的入口地址。
函数指针的定义方式:int(*p)(int);//定义了一个指向int func(int n)类型函数地址的指针。
1定义函数指针变量的形式为:函数返回类型(*指针变量名称)(参数列表)
2函数可以通过函数指针调用
3int( * P)()代表指向一个函数,但不是固定哪一个函数。
void man()
{
printf("抽烟\n");
printf("喝酒\n");
printf("打牌\n");
}
void woman()
{
printf("化妆\n");
printf("逛街\n");
printf("网购\n");
}
int main()
{
void(*p)();
int i = 0;
scanf("%d", &i);
if (i == 0)
p = man;
else
p = woman;
p();
return0;
}
1.1.20 把指向函数的指针做为函数的参数
将函数指针做为另一个函数的参数称为回调函数
int max(inta, intb)
{
if (a > b)
returna;
else
returnb;
}
int add(inta, intb)
{
returna + b;
}
void func(int(*p)(int, int), inta, intb)
{
int res = p(a, b);
printf("%d\n", res);
}
int main()
{
int i = 0;
scanf("%d", &i);
if (i == 0)
func(max, 10, 20);
else
func(add, 10, 20);
return0;
}
1.1.21 指针运算
赋值:int*p = &a;
求值:int I= *p;
取指针地址 int**pp = &p;
将一个整数加(减)给指针:p + 3; p – 3;
增加(减少)指针值p++,p--
求差值 ,p1 –p2,通常用于同一个数组内求两个元素之间的距离
比较 p1== p2,通常用来比较两个指针是否指向同一个位置。
1.1.22 指针小结
定义
说明
Int i
定义×××变量
int *p
定义一个指向int的指针变量
Int a[10]
定义一个int数组
Int *p[10]
定义一个指针数组,其中每个数组元素指向一个int型变量的地址
Int func()
定义一个函数,返回值为int型
Int *func()
定义一个函数,返回值为int *型
Int (*p)()
定义一个指向函数的指针,函数的原型为无参数,返回值为int
Int **p
定义一个指向int的指针的指针,二级指针
2 字符指针与字符串
2.1 指针和字符串
在C语言当中,大多数字符串操作其实就是指针操作。
char s[] = "hello word";
char *p = s;
p[0] = 'a';
2.2 通过指针访问字符串数组
复习:
指针复习:
1,函数引用外部指针
#include#include void fun1(int *p) { p = malloc(4); printf("fun1 %p \n",p); } void fun2(int **p) { *p = malloc(4); printf("fun2 %p \n",*p); } int main() { int *p = NULL; fun1(p); printf("main %p \n\n",p); fun2(&p); printf("main %p \n",p); return 0; } 编译运行: chunli@ubuntu:~/review$ gcc main.c && ./a.out fun1 0x176d010 main (nil) fun2 0x176d030 main 0x176d030
数组、指针复习:
chunli@ubuntu:~/review$ cat main.c #include#include const char *test() { return malloc(1); } int *test2(int *a) //把传过来的数值当做地址传出去 { return a+1 ; } int main() { int a[5][10]; //a[0]= 3; 没有这个变量,无法通过编译 printf("%ld \n",sizeof(a)); printf("%ld \n",sizeof(a[0])); printf("%ld \n",sizeof(a[0][0])); printf("二维数组首地址 %p ,%p\n",&a,*(a)); printf("二维数组第1行地址 %p ,%p\n",&a[0],*(a+0)); printf("二维数组第1行第1个元素 %p \n",&a[0][0]); printf("移动到下一个这么大小的地址 %p \n",&a+1); printf("移动到二维数组第2行 %p \n",&a[0]+1); printf("移动到二维数组第1行第2个元素 %p \n",&a[0][0]+1); a[1][3] = 9; printf("%d \n",*(*(a+1)+3)); const char *p = test(); int c = 100; int *b = test2(&c); printf("%d \n",*b); int *(*p2)(int *a); //定义一个函数指针p2,参数是int,返回值是int p2 = test2; return 0; } chunli@ubuntu:~/review$ 编译运行: chunli@ubuntu:~/review$ gcc main.c && ./a.out 200 40 4 二维数组首地址 0x7ffe2c0282b0 ,0x7ffe2c0282b0 二维数组第1行地址 0x7ffe2c0282b0 ,0x7ffe2c0282b0 二维数组第1行第1个元素 0x7ffe2c0282b0 移动到下一个这么大小的地址 0x7ffe2c028378 移动到二维数组第2行 0x7ffe2c0282d8 移动到二维数组第1行第2个元素 0x7ffe2c0282b4 9 8749072 chunli@ubuntu:~/review$
函数的参数由右向左入栈
函数变量从栈底(高地址)向栈顶(低地址)递减